apes 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +82 -0
- data/.travis-gemfile +15 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +22 -0
- data/README.md +177 -0
- data/Rakefile +44 -0
- data/apes.gemspec +34 -0
- data/doc/Apes.html +130 -0
- data/doc/Apes/Concerns.html +127 -0
- data/doc/Apes/Concerns/Errors.html +1089 -0
- data/doc/Apes/Concerns/Pagination.html +636 -0
- data/doc/Apes/Concerns/Request.html +766 -0
- data/doc/Apes/Concerns/Response.html +940 -0
- data/doc/Apes/Controller.html +1100 -0
- data/doc/Apes/Errors.html +125 -0
- data/doc/Apes/Errors/AuthenticationError.html +133 -0
- data/doc/Apes/Errors/BadRequestError.html +157 -0
- data/doc/Apes/Errors/BaseError.html +320 -0
- data/doc/Apes/Errors/InvalidDataError.html +157 -0
- data/doc/Apes/Errors/MissingDataError.html +157 -0
- data/doc/Apes/Model.html +378 -0
- data/doc/Apes/PaginationCursor.html +2138 -0
- data/doc/Apes/RuntimeConfiguration.html +909 -0
- data/doc/Apes/Serializers.html +125 -0
- data/doc/Apes/Serializers/JSON.html +389 -0
- data/doc/Apes/Serializers/JWT.html +452 -0
- data/doc/Apes/Serializers/List.html +347 -0
- data/doc/Apes/UrlsParser.html +1432 -0
- data/doc/Apes/Validators.html +125 -0
- data/doc/Apes/Validators/BaseValidator.html +278 -0
- data/doc/Apes/Validators/BooleanValidator.html +494 -0
- data/doc/Apes/Validators/EmailValidator.html +350 -0
- data/doc/Apes/Validators/PhoneValidator.html +375 -0
- data/doc/Apes/Validators/ReferenceValidator.html +372 -0
- data/doc/Apes/Validators/TimestampValidator.html +640 -0
- data/doc/Apes/Validators/UuidValidator.html +372 -0
- data/doc/Apes/Validators/ZipCodeValidator.html +372 -0
- data/doc/Apes/Version.html +189 -0
- data/doc/ApplicationController.html +547 -0
- data/doc/Concerns.html +128 -0
- data/doc/Concerns/ErrorHandling.html +826 -0
- data/doc/Concerns/PaginationHandling.html +463 -0
- data/doc/Concerns/RequestHandling.html +512 -0
- data/doc/Concerns/ResponseHandling.html +579 -0
- data/doc/Errors.html +126 -0
- data/doc/Errors/AuthenticationError.html +123 -0
- data/doc/Errors/BadRequestError.html +147 -0
- data/doc/Errors/BaseError.html +289 -0
- data/doc/Errors/InvalidDataError.html +147 -0
- data/doc/Errors/MissingDataError.html +147 -0
- data/doc/Model.html +315 -0
- data/doc/PaginationCursor.html +764 -0
- data/doc/Serializers.html +126 -0
- data/doc/Serializers/JSON.html +253 -0
- data/doc/Serializers/JWT.html +253 -0
- data/doc/Serializers/List.html +245 -0
- data/doc/Validators.html +126 -0
- data/doc/Validators/BaseValidator.html +209 -0
- data/doc/Validators/BooleanValidator.html +391 -0
- data/doc/Validators/EmailValidator.html +298 -0
- data/doc/Validators/PhoneValidator.html +313 -0
- data/doc/Validators/ReferenceValidator.html +284 -0
- data/doc/Validators/TimestampValidator.html +476 -0
- data/doc/Validators/UuidValidator.html +310 -0
- data/doc/Validators/ZipCodeValidator.html +310 -0
- data/doc/_index.html +435 -0
- data/doc/class_list.html +58 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file.README.html +252 -0
- data/doc/file_list.html +60 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +252 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +181 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +615 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/apes.rb +40 -0
- data/lib/apes/concerns/errors.rb +111 -0
- data/lib/apes/concerns/pagination.rb +81 -0
- data/lib/apes/concerns/request.rb +237 -0
- data/lib/apes/concerns/response.rb +74 -0
- data/lib/apes/controller.rb +77 -0
- data/lib/apes/errors.rb +38 -0
- data/lib/apes/model.rb +94 -0
- data/lib/apes/pagination_cursor.rb +152 -0
- data/lib/apes/runtime_configuration.rb +80 -0
- data/lib/apes/serializers.rb +88 -0
- data/lib/apes/urls_parser.rb +233 -0
- data/lib/apes/validators.rb +234 -0
- data/lib/apes/version.rb +24 -0
- data/spec/apes/concerns/errors_spec.rb +141 -0
- data/spec/apes/concerns/pagination_spec.rb +114 -0
- data/spec/apes/concerns/request_spec.rb +244 -0
- data/spec/apes/concerns/response_spec.rb +79 -0
- data/spec/apes/controller_spec.rb +54 -0
- data/spec/apes/errors_spec.rb +14 -0
- data/spec/apes/models_spec.rb +148 -0
- data/spec/apes/pagination_cursor_spec.rb +113 -0
- data/spec/apes/runtime_configuration_spec.rb +100 -0
- data/spec/apes/serializers_spec.rb +70 -0
- data/spec/apes/urls_parser_spec.rb +150 -0
- data/spec/apes/validators_spec.rb +237 -0
- data/spec/spec_helper.rb +30 -0
- data/views/_included.json.jbuilder +9 -0
- data/views/_pagination.json.jbuilder +9 -0
- data/views/collection.json.jbuilder +4 -0
- data/views/errors/400.json.jbuilder +9 -0
- data/views/errors/403.json.jbuilder +7 -0
- data/views/errors/404.json.jbuilder +6 -0
- data/views/errors/422.json.jbuilder +19 -0
- data/views/errors/500.json.jbuilder +12 -0
- data/views/errors/501.json.jbuilder +7 -0
- data/views/layouts/general.json.jbuilder +36 -0
- data/views/object.json.jbuilder +4 -0
- metadata +262 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Apes::Concerns::Response do
|
|
4
|
+
class ResponseHandlingMockContainer
|
|
5
|
+
include Apes::Concerns::Response
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module Mockspace
|
|
9
|
+
class NameMockController
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
subject { ResponseHandlingMockContainer.new }
|
|
14
|
+
|
|
15
|
+
describe "#response_template_for" do
|
|
16
|
+
it "should return the right template" do
|
|
17
|
+
expect(subject.response_template_for(Mockspace::NameMockController.new)).to eq("mockspace_name_mock_controller")
|
|
18
|
+
expect(subject.response_template_for([Mockspace::NameMockController.new])).to eq("mockspace_name_mock_controller")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "#response_meta" do
|
|
23
|
+
it "should return the right meta" do
|
|
24
|
+
expect(subject.response_meta).to be_a(HashWithIndifferentAccess)
|
|
25
|
+
expect(subject.response_meta("FOO")).to eq("FOO")
|
|
26
|
+
|
|
27
|
+
subject.instance_variable_set(:@meta, "BAR")
|
|
28
|
+
expect(subject.response_meta).to eq("BAR")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "#response_data" do
|
|
33
|
+
it "should return the right data" do
|
|
34
|
+
expect(subject.response_data).to be_a(HashWithIndifferentAccess)
|
|
35
|
+
expect(subject.response_data("FOO")).to eq("FOO")
|
|
36
|
+
|
|
37
|
+
subject.instance_variable_set(:@data, "BAR")
|
|
38
|
+
expect(subject.response_data).to eq("BAR")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "#response_links" do
|
|
43
|
+
it "should return the right links" do
|
|
44
|
+
expect(subject.response_links).to be_a(HashWithIndifferentAccess)
|
|
45
|
+
expect(subject.response_links("FOO")).to eq("FOO")
|
|
46
|
+
|
|
47
|
+
subject.instance_variable_set(:@links, "BAR")
|
|
48
|
+
expect(subject.response_links).to eq("BAR")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "#response_included" do
|
|
53
|
+
it "should return the right included data" do
|
|
54
|
+
allow(subject).to receive(:controller).and_return(OpenStruct.new(included: nil))
|
|
55
|
+
expect(subject.response_included).to be_a(HashWithIndifferentAccess)
|
|
56
|
+
expect(subject.response_included("FOO")).to eq("FOO")
|
|
57
|
+
|
|
58
|
+
allow(subject).to receive(:controller).and_return(OpenStruct.new(included: "BAR"))
|
|
59
|
+
subject.instance_variable_set(:@links, "BAR")
|
|
60
|
+
expect(subject.response_links).to eq("BAR")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "#response_include" do
|
|
65
|
+
it "should add included data" do
|
|
66
|
+
data = {}
|
|
67
|
+
allow(subject).to receive(:controller).and_return(OpenStruct.new(included: data))
|
|
68
|
+
|
|
69
|
+
subject.response_include("DATA")
|
|
70
|
+
expect(data).to eq({"string:DATA"=>["DATA", nil]})
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "#response_timestamp" do
|
|
75
|
+
it "should return the right timestamp" do
|
|
76
|
+
expect(subject.response_timestamp(DateTime.civil(2024, 12, 6, 3, 2, 1, "+7"))).to eq("2024-12-06T03:02:01.000+0700")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
|
|
3
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "spec_helper"
|
|
7
|
+
|
|
8
|
+
describe Apes::Controller do
|
|
9
|
+
describe "#handle_cors", type: :controller do
|
|
10
|
+
it "should render the right content" do
|
|
11
|
+
expect(subject).to receive(:render).with(nothing: true, status: :no_content)
|
|
12
|
+
subject.handle_cors
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "#default_url_options" do
|
|
17
|
+
it "should always mark the host as needed in URLs" do
|
|
18
|
+
allow(Apes::RuntimeConfiguration).to receive(:development?).and_return(true)
|
|
19
|
+
expect(subject).to receive(:request).and_return(OpenStruct.new(url: "http://localhost"))
|
|
20
|
+
expect(subject.default_url_options[:only_path]).to be_falsey
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#render_error" do
|
|
25
|
+
it "should render the right template" do
|
|
26
|
+
expect(subject).to receive(:render).with("errors/403", status: :forbidden)
|
|
27
|
+
subject.render_error(:forbidden, ["1", "2"])
|
|
28
|
+
expect(subject.instance_variable_get(:@errors)).to eq(["1", "2"])
|
|
29
|
+
|
|
30
|
+
expect(subject).to receive(:render).with("errors/404", status: 404)
|
|
31
|
+
subject.render_error(404, ["3", "4"])
|
|
32
|
+
expect(subject.instance_variable_get(:@errors)).to eq(["3", "4"])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe "#render_default_views (private)" do
|
|
37
|
+
it "should render the object template if a object is set" do
|
|
38
|
+
subject.instance_variable_set(:@object, "FOO")
|
|
39
|
+
expect(subject).to receive(:render).with("/object")
|
|
40
|
+
subject.send(:render_default_views, RuntimeError.new)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should render the collection template if a collection is set" do
|
|
44
|
+
subject.instance_variable_set(:@objects, "FOO")
|
|
45
|
+
expect(subject).to receive(:render).with("/collection")
|
|
46
|
+
subject.send(:render_default_views, RuntimeError.new)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should fallback to the default handler otherwise" do
|
|
50
|
+
expect(subject).to receive(:error_handle_exception).with(an_instance_of(RuntimeError))
|
|
51
|
+
subject.send(:render_default_views, RuntimeError.new)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
|
|
3
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "spec_helper"
|
|
7
|
+
|
|
8
|
+
describe Apes::Errors::BaseError do
|
|
9
|
+
it "should save details" do
|
|
10
|
+
subject = Apes::Errors::BaseError.new({a: 1})
|
|
11
|
+
expect(subject.message).to eq("")
|
|
12
|
+
expect(subject.details).to eq({a: 1})
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
|
|
3
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "spec_helper"
|
|
7
|
+
|
|
8
|
+
describe Apes::Model do
|
|
9
|
+
class MockBaseModel < ActiveRecord::Base
|
|
10
|
+
include Apes::Model
|
|
11
|
+
|
|
12
|
+
self.abstract_class = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class MockQueryingModel < MockBaseModel
|
|
16
|
+
SECONDARY_QUERY = "name = :id"
|
|
17
|
+
|
|
18
|
+
def self.table_name
|
|
19
|
+
"querying_temp_table"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :field
|
|
23
|
+
validates :field, "presence" => true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class MockQueryingOtherModel < MockBaseModel
|
|
27
|
+
include Apes::Model
|
|
28
|
+
|
|
29
|
+
SECONDARY_KEY = :handle
|
|
30
|
+
|
|
31
|
+
def self.table_name
|
|
32
|
+
"querying_temp_table"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class MockQueryingAnotherModel < MockBaseModel
|
|
37
|
+
include Apes::Model
|
|
38
|
+
|
|
39
|
+
def self.table_name
|
|
40
|
+
"querying_temp_table"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subject {
|
|
45
|
+
MockQueryingModel.new(id: SecureRandom.uuid, handle: "HANDLE", name: "NAME")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
around(:each) do |example|
|
|
49
|
+
db = Tempfile.new("apes-test")
|
|
50
|
+
MockBaseModel.establish_connection(adapter: "sqlite3", database: db.path)
|
|
51
|
+
MockBaseModel.connection.execute("CREATE TABLE IF NOT EXISTS querying_temp_table(id uuid, handle varchar, name varchar);")
|
|
52
|
+
example.call
|
|
53
|
+
db.unlink
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe ".find_with_any!" do
|
|
57
|
+
it "should find a record using the primary key when the ID is a UUID" do
|
|
58
|
+
expect(MockQueryingOtherModel).to receive(:find).with(subject.id).and_return(subject)
|
|
59
|
+
expect(MockQueryingOtherModel.find_with_any!(subject.id)).to eq(subject)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should find a record using the secondary key" do
|
|
63
|
+
expect(MockQueryingOtherModel).to receive(:find_by!).with(handle: subject.handle).and_return(subject)
|
|
64
|
+
expect(MockQueryingOtherModel.find_with_any!(subject.handle)).to eq(subject)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should find a record using the secondary query" do
|
|
68
|
+
expect(MockQueryingModel).to receive(:find_by!).with("name = :id", {id: subject.name}).and_return(subject)
|
|
69
|
+
expect(MockQueryingModel.find_with_any!(subject.name).id).to eq(subject.id)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should fallback to a reasonable query" do
|
|
73
|
+
expect(MockQueryingAnotherModel).to receive(:find_by!).with(handle: subject.handle).and_return(subject)
|
|
74
|
+
expect(MockQueryingAnotherModel.find_with_any!(subject.handle).id).to eq(subject.id)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should raise an exception when nothing is found" do
|
|
78
|
+
expect { MockQueryingOtherModel.find_with_any!("NOTHING") }.to raise_error(ActiveRecord::RecordNotFound)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe ".find_with_any" do
|
|
83
|
+
it "should find a records" do
|
|
84
|
+
expect(MockQueryingOtherModel).to receive(:find_by!).with(handle: subject.handle).and_return(subject)
|
|
85
|
+
expect(MockQueryingOtherModel.find_with_any(subject.handle)).to eq(subject)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "should raise an exception when nothing is found" do
|
|
89
|
+
expect { MockQueryingOtherModel.find_with_any("NOTHING") }.not_to raise_error
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe ".search" do
|
|
94
|
+
let(:params) { {filter: {query: "ABC"}} }
|
|
95
|
+
let(:table_name) { "querying_temp_table" }
|
|
96
|
+
|
|
97
|
+
it "should do nothing if no value is present" do
|
|
98
|
+
expect(MockQueryingOtherModel.search().to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\"")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should perform a query on the fields" do
|
|
102
|
+
expect(MockQueryingOtherModel.search(params: params, fields: [:name, :token, :secret]).to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\" WHERE (name ILIKE '%ABC%' OR token ILIKE '%ABC%' OR secret ILIKE '%ABC%')")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should allow prefix based queries" do
|
|
106
|
+
expect(MockQueryingOtherModel.search(params: params, start_only: true).to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\" WHERE (name ILIKE 'ABC%')")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should allow case sensitive searches" do
|
|
110
|
+
expect(MockQueryingOtherModel.search(params: params, case_sensitive: true).to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\" WHERE (name LIKE '%ABC%')")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should allow to use AND based searches" do
|
|
114
|
+
expect(MockQueryingOtherModel.search(params: params, method: :other).to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\" WHERE (name ILIKE '%ABC%')")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should extend existing queries" do
|
|
118
|
+
expect(MockQueryingOtherModel.search(params: params, query: MockQueryingOtherModel.where("secret IS NOT NULL")).to_sql).to eq("SELECT \"#{table_name}\".* FROM \"#{table_name}\" WHERE (secret IS NOT NULL) AND (name ILIKE '%ABC%')")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe "#additional_errors" do
|
|
123
|
+
it "should return a ActiveModel::Errors object" do
|
|
124
|
+
expect(MockQueryingModel.new.additional_errors).to be_a(ActiveModel::Errors)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe "#run_validations!" do
|
|
129
|
+
it "should merge errors when validating" do
|
|
130
|
+
subject = MockQueryingModel.new
|
|
131
|
+
subject.additional_errors.add(:field, "ANOTHER")
|
|
132
|
+
subject.validate
|
|
133
|
+
expect(subject.errors.to_hash).to eq({field: ["ANOTHER", "can't be blank"]})
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe "#all_validation_errors" do
|
|
138
|
+
it "should allow to add additional errors after validation" do
|
|
139
|
+
subject = MockQueryingModel.new
|
|
140
|
+
expect(subject.all_validation_errors.to_hash).to eq({})
|
|
141
|
+
subject.validate
|
|
142
|
+
expect(subject.all_validation_errors.to_hash).to eq({field: ["can't be blank"]})
|
|
143
|
+
subject.additional_errors.add(:field, "ANOTHER")
|
|
144
|
+
expect(subject.all_validation_errors.to_hash).to eq({field: ["can't be blank", "ANOTHER"]})
|
|
145
|
+
expect(subject.all_validation_errors.to_hash).to eq({field: ["can't be blank", "ANOTHER"]}) # Should not add errors twice
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
|
|
3
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "spec_helper"
|
|
7
|
+
|
|
8
|
+
describe Apes::PaginationCursor do
|
|
9
|
+
def create_cursor(value = nil, direction = "next", count = nil, use_offset = false)
|
|
10
|
+
Apes::PaginationCursor.new({count: count, page: JWT.encode({aud: "pagination", sub: {value: value, use_offset: use_offset, direction: direction}}, Apes::RuntimeConfiguration.jwt_token, "HS256")})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def decode_cursor(cursor)
|
|
14
|
+
JWT.decode(cursor, Apes::RuntimeConfiguration.jwt_token, true, {algorithm: "HS256", verify_aud: true, aud: "pagination"}).dig(0, "sub")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#initialize" do
|
|
18
|
+
it "should load a JWT token from data" do
|
|
19
|
+
subject = create_cursor("1", "previous", 3, true)
|
|
20
|
+
|
|
21
|
+
expect(subject.value).to eq("1")
|
|
22
|
+
expect(subject.direction).to eq("previous")
|
|
23
|
+
expect(subject.size).to eq(3)
|
|
24
|
+
expect(subject.use_offset).to be_truthy
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should ensure good defaults" do
|
|
28
|
+
subject = Apes::PaginationCursor.new({count: 3, page: "FOO"})
|
|
29
|
+
|
|
30
|
+
expect(subject.value).to be_nil
|
|
31
|
+
expect(subject.direction).to eq("next")
|
|
32
|
+
expect(subject.size).to eq(3)
|
|
33
|
+
expect(subject.use_offset).to be_falsey
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should sanitize values" do
|
|
38
|
+
subject = create_cursor("1", "other", -1)
|
|
39
|
+
|
|
40
|
+
expect(subject.direction).to eq("next")
|
|
41
|
+
expect(subject.size).to eq(Apes::PaginationCursor::DEFAULT_SIZE)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "#operator" do
|
|
46
|
+
it "should return the right operator" do
|
|
47
|
+
subject = create_cursor("", "next", -1)
|
|
48
|
+
expect(subject.operator(:asc)).to eq(">")
|
|
49
|
+
expect(subject.operator(:desc)).to eq("<")
|
|
50
|
+
|
|
51
|
+
subject = create_cursor("", "previous", -1)
|
|
52
|
+
expect(subject.operator(:asc)).to eq("<")
|
|
53
|
+
expect(subject.operator(:desc)).to eq(">")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "#might_exist?" do
|
|
58
|
+
it "should check if a pagination link might exist" do
|
|
59
|
+
subject = create_cursor("", "next", -1)
|
|
60
|
+
expect(subject.might_exist?("first", [])).to be_truthy
|
|
61
|
+
expect(subject.might_exist?("next", [])).to be_falsey
|
|
62
|
+
expect(subject.might_exist?("prev", [])).to be_falsey
|
|
63
|
+
expect(subject.might_exist?("first", [1])).to be_truthy
|
|
64
|
+
expect(subject.might_exist?("next", [1])).to be_truthy
|
|
65
|
+
expect(subject.might_exist?("prev", [1])).to be_falsey
|
|
66
|
+
|
|
67
|
+
subject = create_cursor("1", "next", -1)
|
|
68
|
+
expect(subject.might_exist?("prev", [1])).to be_truthy
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#save" do
|
|
73
|
+
describe "when NOT using offset based pagination" do
|
|
74
|
+
it "should generate a valid token for direction next" do
|
|
75
|
+
cursor = create_cursor(1, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "next")
|
|
76
|
+
subject = decode_cursor(cursor)
|
|
77
|
+
expect(subject).to eq({"value" => 2, "use_offset" => false, "direction" => "next", "size" => 25})
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should generate a valid token for direction prev" do
|
|
81
|
+
cursor = create_cursor(1, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "previous")
|
|
82
|
+
subject = decode_cursor(cursor)
|
|
83
|
+
expect(subject).to eq({"value" => 1, "use_offset" => false, "direction" => "previous", "size" => 25})
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should generate a valid token for direction first" do
|
|
87
|
+
cursor = create_cursor(1, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "first")
|
|
88
|
+
subject = decode_cursor(cursor)
|
|
89
|
+
expect(subject).to eq({"value" => nil, "use_offset" => false, "direction" => "next", "size" => 25})
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "when using offset based pagination" do
|
|
94
|
+
it "should generate a valid token for direction next" do
|
|
95
|
+
cursor = create_cursor(30, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "next", use_offset: true)
|
|
96
|
+
subject = decode_cursor(cursor)
|
|
97
|
+
expect(subject).to eq({"value" => 55, "use_offset" => true, "direction" => "next", "size" => 25})
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should generate a valid token for direction prev" do
|
|
101
|
+
cursor = create_cursor(30, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "previous", use_offset: true)
|
|
102
|
+
subject = decode_cursor(cursor)
|
|
103
|
+
expect(subject).to eq({"value" => 5, "use_offset" => true, "direction" => "previous", "size" => 25})
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should generate a valid token for direction first" do
|
|
107
|
+
cursor = create_cursor(30, "next", -1).save([OpenStruct.new(id: 1), OpenStruct.new(id: 2)], "first", use_offset: true)
|
|
108
|
+
subject = decode_cursor(cursor)
|
|
109
|
+
expect(subject).to eq({"value" => nil, "use_offset" => true, "direction" => "next", "size" => 25})
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
|
|
3
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require "spec_helper"
|
|
7
|
+
|
|
8
|
+
describe Apes::RuntimeConfiguration do
|
|
9
|
+
describe ".root" do
|
|
10
|
+
it "should get the information from RubyGems" do
|
|
11
|
+
expect(Apes::RuntimeConfiguration.root).to be_a(String)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe ".rails_root" do
|
|
16
|
+
it "should get the information from Rails" do
|
|
17
|
+
stub_const("Rails", {root: "/ABC"}.ensure_access(:dotted))
|
|
18
|
+
expect(Apes::RuntimeConfiguration.rails_root).to eq("/ABC")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should fallback to a default" do
|
|
22
|
+
allow(Rails).to receive(:root).and_raise(RuntimeError)
|
|
23
|
+
|
|
24
|
+
expect(Apes::RuntimeConfiguration.rails_root).to be_nil
|
|
25
|
+
expect(Apes::RuntimeConfiguration.rails_root("DEFAULT")).to eq("DEFAULT")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe ".gems_root" do
|
|
30
|
+
it "should get the information from Rails" do
|
|
31
|
+
expect(Apes::RuntimeConfiguration.gems_root).to be_a(String)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should fallback to a default" do
|
|
35
|
+
allow(Gem).to receive(:loaded_specs).and_raise(RuntimeError)
|
|
36
|
+
|
|
37
|
+
expect(Apes::RuntimeConfiguration.gems_root).to be_nil
|
|
38
|
+
expect(Apes::RuntimeConfiguration.gems_root("DEFAULT")).to eq("DEFAULT")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe ".environment" do
|
|
43
|
+
it "should get the information from Rails secrets" do
|
|
44
|
+
stub_const("Rails", {env: "FOO"}.ensure_access(:dotted))
|
|
45
|
+
expect(Apes::RuntimeConfiguration.environment).to eq("FOO")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should fallback to a default" do
|
|
49
|
+
allow(Rails).to receive(:env).and_raise(RuntimeError)
|
|
50
|
+
expect(Apes::RuntimeConfiguration.environment).to eq("development")
|
|
51
|
+
expect(Apes::RuntimeConfiguration.environment("DEFAULT")).to eq("DEFAULT")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe ".environment" do
|
|
56
|
+
it "should check if Rails is in development" do
|
|
57
|
+
stub_const("Rails", {env: "development"}.ensure_access(:dotted))
|
|
58
|
+
expect(Apes::RuntimeConfiguration.development?).to be_truthy
|
|
59
|
+
|
|
60
|
+
stub_const("Rails", {env: "FOO"}.ensure_access(:dotted))
|
|
61
|
+
expect(Apes::RuntimeConfiguration.development?).to be_falsey
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe ".jwt_token" do
|
|
66
|
+
it "should get the information from Rails secrets" do
|
|
67
|
+
stub_const("Rails", {application: {secrets: {jwt: "SECRET"}}}.ensure_access(:dotted))
|
|
68
|
+
expect(Apes::RuntimeConfiguration.jwt_token).to eq("SECRET")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should fallback to a default" do
|
|
72
|
+
expect(Apes::RuntimeConfiguration.jwt_token).to eq("secret")
|
|
73
|
+
expect(Apes::RuntimeConfiguration.jwt_token("DEFAULT")).to eq("DEFAULT")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe ".cors_source" do
|
|
78
|
+
it "should get the information from Rails secrets" do
|
|
79
|
+
stub_const("Rails", {application: {secrets: {cors_source: "SOURCE"}}}.ensure_access(:dotted))
|
|
80
|
+
expect(Apes::RuntimeConfiguration.cors_source).to eq("SOURCE")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should fallback to a default" do
|
|
84
|
+
expect(Apes::RuntimeConfiguration.cors_source).to eq("localhost")
|
|
85
|
+
expect(Apes::RuntimeConfiguration.cors_source("DEFAULT")).to eq("DEFAULT")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe ".timestamp_formats" do
|
|
90
|
+
it "should get the information from Rails secrets" do
|
|
91
|
+
stub_const("Rails", {application: {config: {timestamp_formats: {a: 1}}}}.ensure_access(:dotted))
|
|
92
|
+
expect(Apes::RuntimeConfiguration.timestamp_formats).to eq({a: 1})
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should fallback to a default" do
|
|
96
|
+
expect(Apes::RuntimeConfiguration.timestamp_formats).to eq({})
|
|
97
|
+
expect(Apes::RuntimeConfiguration.timestamp_formats("DEFAULT")).to eq("DEFAULT")
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|