api_maker 0.0.1 → 0.0.2
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 +4 -4
- data/app/api_maker/api_helpers/api_maker_helpers.rb +5 -0
- data/app/api_maker/services/can_can/load_abilities.rb +30 -0
- data/app/api_maker/services/devise/sign_in.rb +64 -0
- data/app/api_maker/services/devise/sign_out.rb +9 -0
- data/app/api_maker/services/models/find_or_create_by.rb +18 -0
- data/app/channels/api_maker/subscriptions_channel.rb +33 -2
- data/app/controllers/api_maker/base_controller.rb +7 -3
- data/app/controllers/api_maker/commands_controller.rb +26 -4
- data/app/controllers/api_maker/session_statuses_controller.rb +1 -1
- data/app/services/api_maker/abilities_loader.rb +104 -0
- data/app/services/api_maker/application_service.rb +2 -1
- data/app/services/api_maker/base_command.rb +248 -0
- data/app/services/api_maker/collection_command_service.rb +29 -15
- data/app/services/api_maker/collection_loader.rb +124 -0
- data/app/services/api_maker/command_failed_error.rb +3 -0
- data/app/services/api_maker/command_response.rb +17 -6
- data/app/services/api_maker/command_service.rb +3 -3
- data/app/services/api_maker/create_command.rb +11 -26
- data/app/services/api_maker/create_command_service.rb +3 -3
- data/app/services/api_maker/database_type.rb +9 -0
- data/app/services/api_maker/deep_merge_params.rb +26 -0
- data/app/services/api_maker/deserializer.rb +35 -0
- data/app/services/api_maker/destroy_command.rb +15 -21
- data/app/services/api_maker/destroy_command_service.rb +3 -3
- data/app/services/api_maker/generate_react_native_api_service.rb +3 -19
- data/app/services/api_maker/include_helpers.rb +17 -0
- data/app/services/api_maker/index_command.rb +8 -88
- data/app/services/api_maker/index_command_service.rb +5 -5
- data/app/services/api_maker/js_method_namer_service.rb +1 -1
- data/app/services/api_maker/locals_from_controller.rb +14 -0
- data/app/services/api_maker/member_command_service.rb +15 -13
- data/app/services/api_maker/model_classes_java_script_generator_service.rb +37 -0
- data/app/services/api_maker/model_content_generator_service.rb +17 -21
- data/app/services/api_maker/models/save.rb +29 -0
- data/app/services/api_maker/models_finder_service.rb +6 -2
- data/app/services/api_maker/models_generator_service.rb +6 -43
- data/app/services/api_maker/move_components_to_routes.rb +50 -0
- data/app/services/api_maker/primary_id_for_model.rb +6 -0
- data/app/services/api_maker/reset_indexed_db_service.rb +36 -0
- data/app/services/api_maker/routes_file_reloader.rb +20 -0
- data/app/services/api_maker/select_columns_on_collection.rb +78 -0
- data/app/services/api_maker/select_parser.rb +32 -0
- data/app/services/api_maker/service_command.rb +27 -0
- data/app/services/api_maker/service_command_service.rb +14 -0
- data/app/services/api_maker/simple_model_errors.rb +52 -0
- data/app/services/api_maker/update_command.rb +8 -24
- data/app/services/api_maker/update_command_service.rb +3 -3
- data/app/services/api_maker/valid_command.rb +4 -13
- data/app/services/api_maker/valid_command_service.rb +3 -3
- data/app/services/api_maker/validation_errors_generator_service.rb +146 -0
- data/app/views/api_maker/_data.html.erb +17 -11
- data/config/routes.rb +0 -2
- data/lib/api_maker/ability.rb +22 -7
- data/lib/api_maker/ability_loader.rb +9 -6
- data/lib/api_maker/base_collection_instance.rb +15 -0
- data/lib/api_maker/base_resource.rb +135 -9
- data/lib/api_maker/base_service.rb +14 -0
- data/lib/api_maker/collection_serializer.rb +95 -34
- data/lib/api_maker/command_spec_helper.rb +41 -11
- data/lib/api_maker/configuration.rb +31 -4
- data/lib/api_maker/expect_to_able_to_helper.rb +31 -0
- data/lib/api_maker/individual_command.rb +24 -9
- data/lib/api_maker/javascript/model-template.js.erb +39 -25
- data/lib/api_maker/javascript/models.js.erb +6 -0
- data/lib/api_maker/loader.rb +1 -1
- data/lib/api_maker/memory_storage.rb +1 -1
- data/lib/api_maker/model_extensions.rb +34 -18
- data/lib/api_maker/permitted_params_argument.rb +5 -1
- data/lib/api_maker/preloader.rb +71 -32
- data/lib/api_maker/preloader_base.rb +108 -0
- data/lib/api_maker/preloader_belongs_to.rb +34 -33
- data/lib/api_maker/preloader_has_many.rb +45 -39
- data/lib/api_maker/preloader_has_one.rb +30 -47
- data/lib/api_maker/railtie.rb +3 -11
- data/lib/api_maker/relationship_preloader.rb +42 -0
- data/lib/api_maker/resource_routing.rb +18 -4
- data/lib/api_maker/result_parser.rb +34 -20
- data/lib/api_maker/serializer.rb +53 -22
- data/lib/api_maker/spec_helper/browser_logs.rb +14 -0
- data/lib/api_maker/spec_helper/execute_collection_command.rb +46 -0
- data/lib/api_maker/spec_helper/execute_member_command.rb +52 -0
- data/lib/api_maker/spec_helper/expect_no_browser_errors.rb +18 -0
- data/lib/api_maker/spec_helper/wait_for_expect.rb +20 -0
- data/lib/api_maker/spec_helper/wait_for_flash_message.rb +21 -0
- data/lib/api_maker/spec_helper.rb +112 -48
- data/lib/api_maker/version.rb +1 -1
- data/lib/api_maker.rb +7 -3
- metadata +108 -89
- data/README.md +0 -476
- data/app/controllers/api_maker/devise_controller.rb +0 -60
- data/lib/api_maker/base_command.rb +0 -81
- data/lib/api_maker/javascript/api.js +0 -92
- data/lib/api_maker/javascript/base-model.js +0 -543
- data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +0 -16
- data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +0 -47
- data/lib/api_maker/javascript/bootstrap/card.jsx +0 -79
- data/lib/api_maker/javascript/bootstrap/checkbox.jsx +0 -127
- data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +0 -105
- data/lib/api_maker/javascript/bootstrap/live-table.jsx +0 -168
- data/lib/api_maker/javascript/bootstrap/money-input.jsx +0 -136
- data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +0 -80
- data/lib/api_maker/javascript/bootstrap/select.jsx +0 -168
- data/lib/api_maker/javascript/bootstrap/string-input.jsx +0 -203
- data/lib/api_maker/javascript/cable-connection-pool.js +0 -169
- data/lib/api_maker/javascript/cable-subscription-pool.js +0 -111
- data/lib/api_maker/javascript/cable-subscription.js +0 -33
- data/lib/api_maker/javascript/collection.js +0 -186
- data/lib/api_maker/javascript/commands-pool.js +0 -123
- data/lib/api_maker/javascript/custom-error.js +0 -14
- data/lib/api_maker/javascript/deserializer.js +0 -35
- data/lib/api_maker/javascript/devise.js.erb +0 -113
- data/lib/api_maker/javascript/error-logger.js +0 -119
- data/lib/api_maker/javascript/event-connection.jsx +0 -24
- data/lib/api_maker/javascript/event-created.jsx +0 -26
- data/lib/api_maker/javascript/event-destroyed.jsx +0 -26
- data/lib/api_maker/javascript/event-emitter-listener.jsx +0 -32
- data/lib/api_maker/javascript/event-listener.jsx +0 -41
- data/lib/api_maker/javascript/event-updated.jsx +0 -26
- data/lib/api_maker/javascript/form-data-to-object.js +0 -70
- data/lib/api_maker/javascript/included.js +0 -39
- data/lib/api_maker/javascript/key-value-store.js +0 -47
- data/lib/api_maker/javascript/logger.js +0 -23
- data/lib/api_maker/javascript/model-name.js +0 -21
- data/lib/api_maker/javascript/models-response-reader.js +0 -43
- data/lib/api_maker/javascript/paginate.jsx +0 -128
- data/lib/api_maker/javascript/params.js +0 -68
- data/lib/api_maker/javascript/resource-route.jsx +0 -75
- data/lib/api_maker/javascript/resource-routes.jsx +0 -36
- data/lib/api_maker/javascript/result.js +0 -25
- data/lib/api_maker/javascript/session-status-updater.js +0 -113
- data/lib/api_maker/javascript/sort-link.jsx +0 -88
- data/lib/api_maker/javascript/updated-attribute.jsx +0 -60
- data/lib/api_maker/preloader_through.rb +0 -101
- data/lib/api_maker/relationship_includer.rb +0 -42
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
class ApiMaker::ResultParser
|
|
2
|
-
attr_reader :ability, :
|
|
2
|
+
attr_reader :ability, :api_maker_args
|
|
3
3
|
|
|
4
4
|
def self.parse(*args)
|
|
5
5
|
ApiMaker::ResultParser.new(*args).result
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def initialize(object, ability: nil,
|
|
8
|
+
def initialize(object, ability: nil, api_maker_args: nil, controller: nil)
|
|
9
9
|
@object = object
|
|
10
10
|
@ability = ability || controller&.__send__(:current_ability)
|
|
11
|
-
@
|
|
11
|
+
@api_maker_args = api_maker_args || controller&.__send__(:api_maker_args) || {}
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def result
|
|
@@ -17,32 +17,46 @@ class ApiMaker::ResultParser
|
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
|
+
def parse_active_record(object)
|
|
21
|
+
serializer = ApiMaker::Serializer.new(ability: ability, api_maker_args: api_maker_args, model: object)
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
api_maker_type: :model,
|
|
25
|
+
model_name: serializer.resource.collection_name,
|
|
26
|
+
serialized: parse_object(serializer.as_json)
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def parse_array(object)
|
|
31
|
+
object.map { |value| parse_object(value) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_hash(object)
|
|
35
|
+
result = {}
|
|
36
|
+
object.each do |key, value|
|
|
37
|
+
result[key] = parse_object(value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
|
|
20
43
|
def parse_object(object)
|
|
21
44
|
if object.is_a?(Hash)
|
|
22
|
-
|
|
23
|
-
object.each do |key, value|
|
|
24
|
-
result[key] = parse_object(value)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
result
|
|
45
|
+
parse_hash(object)
|
|
28
46
|
elsif object.is_a?(Array)
|
|
29
|
-
object
|
|
30
|
-
elsif object.class.name == "Money"
|
|
47
|
+
parse_array(object)
|
|
48
|
+
elsif object.class.name == "Money" # rubocop:disable Style/ClassEqualityComparison
|
|
31
49
|
{api_maker_type: :money, amount: object.cents, currency: object.currency.iso_code}
|
|
32
50
|
elsif object.is_a?(Date)
|
|
33
|
-
object.iso8601
|
|
51
|
+
{api_maker_type: :date, value: object.iso8601}
|
|
34
52
|
elsif object.is_a?(Time)
|
|
35
|
-
object.utc.iso8601
|
|
53
|
+
{api_maker_type: :time, value: object.utc.iso8601}
|
|
36
54
|
elsif object.is_a?(ApiMaker::CollectionSerializer) || object.is_a?(ApiMaker::Serializer)
|
|
37
55
|
parse_object(object.as_json)
|
|
56
|
+
elsif object.is_a?(Class) && object < ApiMaker::BaseResource
|
|
57
|
+
{api_maker_type: :resource, name: object.short_name}
|
|
38
58
|
elsif object.is_a?(ActiveRecord::Base)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
api_maker_type: :model,
|
|
43
|
-
model_name: serializer.resource.collection_name,
|
|
44
|
-
serialized: parse_object(serializer.as_json)
|
|
45
|
-
}
|
|
59
|
+
parse_active_record(object)
|
|
46
60
|
else
|
|
47
61
|
object
|
|
48
62
|
end
|
data/lib/api_maker/serializer.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
class ApiMaker::Serializer
|
|
2
|
-
attr_reader :ability, :
|
|
2
|
+
attr_reader :ability, :api_maker_args, :locals, :model
|
|
3
|
+
|
|
4
|
+
delegate :id, to: :model
|
|
3
5
|
|
|
4
6
|
def self.resource_for(klass)
|
|
5
7
|
ApiMaker::MemoryStorage.current.resource_for_model(klass)
|
|
@@ -11,15 +13,16 @@ class ApiMaker::Serializer
|
|
|
11
13
|
ApiMaker::MemoryStorage.current.resource_for_model(klass)
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
def initialize(ability: nil,
|
|
15
|
-
@args = args
|
|
16
|
-
@model = model
|
|
16
|
+
def initialize(ability: nil, api_maker_args: {}, locals: nil, model:, select: nil)
|
|
17
17
|
@ability = ability
|
|
18
|
+
@api_maker_args = api_maker_args
|
|
19
|
+
@locals = locals || api_maker_args&.dig(:locals) || {}
|
|
20
|
+
@model = model
|
|
18
21
|
@select = select
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def attributes
|
|
22
|
-
|
|
25
|
+
profile("attributes") do
|
|
23
26
|
result = {}
|
|
24
27
|
attributes_to_read.each do |attribute, data|
|
|
25
28
|
if (if_name = data.dig(:args, :if))
|
|
@@ -35,14 +38,18 @@ class ApiMaker::Serializer
|
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def attributes_to_read
|
|
38
|
-
@attributes_to_read ||=
|
|
41
|
+
@attributes_to_read ||= profile("attributes_to_read") do
|
|
42
|
+
@select || resource.default_select
|
|
43
|
+
end
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
def attribute_value(attribute)
|
|
42
|
-
|
|
43
|
-
resource_instance.
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
profile("attribute_value #{attribute}") do
|
|
48
|
+
if resource_instance.respond_to?(attribute)
|
|
49
|
+
resource_instance.__send__(attribute)
|
|
50
|
+
else
|
|
51
|
+
model.__send__(attribute)
|
|
52
|
+
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
|
|
@@ -50,37 +57,61 @@ class ApiMaker::Serializer
|
|
|
50
57
|
result.fetch(*args, &blk)
|
|
51
58
|
end
|
|
52
59
|
|
|
60
|
+
def load_ability(ability_name, value)
|
|
61
|
+
@abilities ||= {}
|
|
62
|
+
@abilities[ability_name] = value
|
|
63
|
+
end
|
|
64
|
+
|
|
53
65
|
def relationships
|
|
54
66
|
@relationships ||= {}
|
|
55
67
|
end
|
|
56
68
|
|
|
57
69
|
def resource
|
|
58
|
-
@resource ||=
|
|
70
|
+
@resource ||= profile("resource") do
|
|
71
|
+
ApiMaker::MemoryStorage.current.resource_for_model(model.class)
|
|
72
|
+
end
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
def resource_instance
|
|
62
|
-
@resource_instance ||=
|
|
76
|
+
@resource_instance ||= profile("resource_instance") do
|
|
77
|
+
resource.new(ability: ability, api_maker_args: api_maker_args, locals: locals, model: model)
|
|
78
|
+
end
|
|
63
79
|
end
|
|
64
80
|
|
|
65
81
|
def result
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
82
|
+
result = {a: attributes}
|
|
83
|
+
result[:b] = @abilities if @abilities # Only use b-key if any abilities was loaded
|
|
84
|
+
result[:r] = @relationships if @relationships # Only preload relationships if set
|
|
85
|
+
result
|
|
71
86
|
end
|
|
72
87
|
|
|
73
|
-
def as_json(
|
|
74
|
-
|
|
88
|
+
def as_json(options = nil)
|
|
89
|
+
profile("as_json") do
|
|
90
|
+
if options && options[:result_parser]
|
|
91
|
+
ApiMaker::ResultParser.new(result, ability: ability, api_maker_args: api_maker_args).result
|
|
92
|
+
else
|
|
93
|
+
result
|
|
94
|
+
end
|
|
95
|
+
end
|
|
75
96
|
end
|
|
76
97
|
|
|
77
|
-
def to_json(
|
|
78
|
-
|
|
98
|
+
def to_json(options = nil)
|
|
99
|
+
profile("to_json") do
|
|
100
|
+
JSON.generate(as_json(options))
|
|
101
|
+
end
|
|
79
102
|
end
|
|
80
103
|
|
|
81
104
|
def inspect
|
|
82
|
-
"
|
|
105
|
+
profile("inspect") do
|
|
106
|
+
"<ApiMaker::Serializer id=\"#{model.id}\" model=\"#{model.class.name}\" relationships=\"#{relationships}\">"
|
|
107
|
+
end
|
|
83
108
|
end
|
|
84
109
|
|
|
85
110
|
alias to_s inspect
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def profile(message, &blk)
|
|
115
|
+
ApiMaker::Configuration.profile(-> { "Serializer for #{model.class.name}: #{message}" }, &blk)
|
|
116
|
+
end
|
|
86
117
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class ApiMaker::SpecHelper::ExecuteCollectionCommand < ApiMaker::ApplicationService
|
|
2
|
+
include RSpec::Mocks::ExampleMethods
|
|
3
|
+
|
|
4
|
+
attr_reader :api_maker_args, :args, :command, :model_class
|
|
5
|
+
|
|
6
|
+
def initialize(ability: nil, api_maker_args: {}, args: {}, command:, fake_controller: nil, model_class:)
|
|
7
|
+
@ability = ability
|
|
8
|
+
@api_maker_args = api_maker_args
|
|
9
|
+
@args = args
|
|
10
|
+
@command = command
|
|
11
|
+
@fake_controller = fake_controller
|
|
12
|
+
@model_class = model_class
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def perform
|
|
16
|
+
helper_command
|
|
17
|
+
helper.execute!
|
|
18
|
+
succeed! helper_command.result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def ability
|
|
22
|
+
@ability ||= ApiMaker::Ability.new(api_maker_args: api_maker_args)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fake_controller
|
|
26
|
+
@fake_controller ||= instance_double(
|
|
27
|
+
ApiMaker::BaseController,
|
|
28
|
+
api_maker_args: api_maker_args,
|
|
29
|
+
current_ability: ability
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def helper
|
|
34
|
+
@helper ||= ApiMaker::CommandSpecHelper.new(
|
|
35
|
+
collection: model_class,
|
|
36
|
+
command: command,
|
|
37
|
+
controller: fake_controller
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def helper_command
|
|
42
|
+
@helper_command ||= helper.add_command(
|
|
43
|
+
args: args
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class ApiMaker::SpecHelper::ExecuteMemberCommand < ApiMaker::ApplicationService
|
|
2
|
+
include RSpec::Mocks::ExampleMethods
|
|
3
|
+
|
|
4
|
+
attr_reader :api_maker_args, :args, :command, :model
|
|
5
|
+
|
|
6
|
+
def initialize(ability: nil, api_maker_args: nil, collection: nil, command:, fake_controller: nil, model:, args: {})
|
|
7
|
+
@ability = ability
|
|
8
|
+
@api_maker_args = api_maker_args
|
|
9
|
+
@args = args
|
|
10
|
+
@collection = collection
|
|
11
|
+
@command = command
|
|
12
|
+
@fake_controller = fake_controller
|
|
13
|
+
@model = model
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def perform
|
|
17
|
+
helper_command
|
|
18
|
+
helper.execute!
|
|
19
|
+
succeed! helper_command.result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ability
|
|
23
|
+
@ability ||= ApiMaker::Ability.new(api_maker_args: api_maker_args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def collection
|
|
27
|
+
@collection ||= model.class.where(id: model.id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def fake_controller
|
|
31
|
+
@fake_controller ||= instance_double(
|
|
32
|
+
ApiMaker::BaseController,
|
|
33
|
+
api_maker_args: api_maker_args,
|
|
34
|
+
current_ability: ability
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def helper
|
|
39
|
+
@helper ||= ApiMaker::CommandSpecHelper.new(
|
|
40
|
+
collection: collection,
|
|
41
|
+
command: command,
|
|
42
|
+
controller: fake_controller
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def helper_command
|
|
47
|
+
@helper_command ||= helper.add_command(
|
|
48
|
+
primary_key: model.id,
|
|
49
|
+
args: args
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module ApiMaker::SpecHelper::ExpectNoBrowserErrors
|
|
2
|
+
def expect_no_browser_errors
|
|
3
|
+
logs = browser_logs
|
|
4
|
+
.map(&:to_s)
|
|
5
|
+
.reject { |log| log.include?("Warning: Can't perform a React state update on an unmounted component.") }
|
|
6
|
+
.reject { |log| log.include?("DEBUG: ") }
|
|
7
|
+
.join("\n")
|
|
8
|
+
|
|
9
|
+
expect_no_browser_window_errors
|
|
10
|
+
return if logs.blank? || logs.exclude?("SEVERE ")
|
|
11
|
+
|
|
12
|
+
# Lets try one more time - just in case browser window error got registered meanwhile
|
|
13
|
+
sleep 0.4
|
|
14
|
+
expect_no_browser_window_errors
|
|
15
|
+
|
|
16
|
+
raise logs
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ApiMaker::SpecHelper::WaitForExpect
|
|
2
|
+
# Waits for an expect to not fail - this is great because it whines
|
|
3
|
+
def wait_for_expect(*args)
|
|
4
|
+
last_error = nil
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
wait_for_browser(*args) do
|
|
8
|
+
yield
|
|
9
|
+
true
|
|
10
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
|
11
|
+
last_error = e
|
|
12
|
+
false
|
|
13
|
+
end
|
|
14
|
+
rescue WaitUtil::TimeoutError => e
|
|
15
|
+
raise last_error if last_error
|
|
16
|
+
|
|
17
|
+
raise e
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ApiMaker::SpecHelper::WaitForFlashMessage
|
|
2
|
+
def wait_for_flash_message(expected_message, delay_sec: 0.2, timeout_sec: 6)
|
|
3
|
+
received_messages = []
|
|
4
|
+
|
|
5
|
+
WaitUtil.wait_for_condition("wait for flash message", timeout_sec: timeout_sec, delay_sec: delay_sec) do
|
|
6
|
+
expect_no_browser_errors
|
|
7
|
+
current_message = flash_message_text
|
|
8
|
+
received_messages << current_message
|
|
9
|
+
|
|
10
|
+
if current_message.is_a?(Array)
|
|
11
|
+
current_message.include?(expected_message)
|
|
12
|
+
else
|
|
13
|
+
current_message == expected_message
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
expect_no_browser_errors
|
|
18
|
+
rescue WaitUtil::TimeoutError
|
|
19
|
+
expect(received_messages.uniq.reject(&:blank?)).to eq include expected_message
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
require "waitutil"
|
|
2
|
+
|
|
3
|
+
module ApiMaker::SpecHelper # rubocop:disable Metrics/ModuleLength
|
|
4
|
+
autoload :ExecuteCollectionCommand, "#{__dir__}/spec_helper/execute_collection_command"
|
|
5
|
+
autoload :ExecuteMemberCommand, "#{__dir__}/spec_helper/execute_member_command"
|
|
6
|
+
|
|
7
|
+
require_relative "spec_helper/browser_logs"
|
|
8
|
+
require_relative "spec_helper/expect_no_browser_errors"
|
|
9
|
+
require_relative "spec_helper/wait_for_expect"
|
|
10
|
+
require_relative "spec_helper/wait_for_flash_message"
|
|
11
|
+
include BrowserLogs
|
|
12
|
+
include ExpectNoBrowserErrors
|
|
13
|
+
include WaitForExpect
|
|
14
|
+
include WaitForFlashMessage
|
|
15
|
+
|
|
16
|
+
class JavaScriptError < RuntimeError; end
|
|
17
|
+
class SelectorNotFoundError < RuntimeError; end
|
|
18
|
+
class SelectorFoundError < RuntimeError; end
|
|
19
|
+
|
|
20
|
+
def browser_firefox?
|
|
21
|
+
capabilities = page.driver.browser.try(:capabilities)
|
|
22
|
+
capabilities && capabilities[:browser_name] == "firefox"
|
|
23
|
+
end
|
|
24
|
+
|
|
2
25
|
def chrome_logs
|
|
3
26
|
page.driver.browser.manage.logs.get(:browser)
|
|
4
27
|
end
|
|
5
28
|
|
|
6
|
-
def
|
|
7
|
-
|
|
29
|
+
def confirm_accept
|
|
30
|
+
page.driver.browser.switch_to.alert.accept
|
|
31
|
+
end
|
|
8
32
|
|
|
33
|
+
def expect_no_browser_window_errors
|
|
9
34
|
errors = execute_script("if (window.errorLogger) { return window.errorLogger.getErrors() }")
|
|
10
35
|
return if !errors.is_a?(Array) || errors.empty?
|
|
11
36
|
|
|
@@ -15,81 +40,112 @@ module ApiMaker::SpecHelper
|
|
|
15
40
|
custom_trace += last_error.fetch("backtrace") if last_error["backtrace"].is_a?(Array)
|
|
16
41
|
custom_trace += caller
|
|
17
42
|
|
|
18
|
-
error =
|
|
43
|
+
error = JavaScriptError.new("#{last_error["errorClass"]}: #{last_error["message"]}")
|
|
19
44
|
error.set_backtrace(custom_trace)
|
|
20
45
|
|
|
21
46
|
raise error
|
|
22
47
|
end
|
|
23
48
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
49
|
+
def expect_no_errors
|
|
50
|
+
expect_no_flash_errors
|
|
51
|
+
expect_no_browser_errors
|
|
52
|
+
end
|
|
28
53
|
|
|
29
|
-
|
|
54
|
+
def expect_to_be_able_to(ability, model, permissions)
|
|
55
|
+
permissions.each do |permission|
|
|
56
|
+
# Test access through 'can?'
|
|
57
|
+
expect(ability).to be_able_to permission, model
|
|
30
58
|
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
# Test access through 'accessible_by'
|
|
60
|
+
if model.is_a?(ActiveRecord::Base) && model.persisted?
|
|
61
|
+
readable_models = model.class.where(id: model).accessible_by(ability, permission)
|
|
62
|
+
expect(readable_models).to eq [model]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
33
65
|
end
|
|
34
66
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
def expect_not_to_be_able_to(ability, model, permissions)
|
|
68
|
+
permissions.each do |permission|
|
|
69
|
+
# Test access through 'can?'
|
|
70
|
+
expect(ability).not_to be_able_to permission, model
|
|
71
|
+
|
|
72
|
+
# Test access through 'accessible_by'
|
|
73
|
+
if model.is_a?(ActiveRecord::Base) && model.persisted?
|
|
74
|
+
readable_models = model.class.where(id: model).accessible_by(ability, permission)
|
|
75
|
+
expect(readable_models).to be_empty
|
|
76
|
+
end
|
|
77
|
+
end
|
|
38
78
|
end
|
|
39
79
|
|
|
40
80
|
def js_fill_in(element_id, with:)
|
|
41
81
|
page.execute_script("document.querySelector(#{element_id.to_json}).value = #{with.to_json}")
|
|
42
82
|
end
|
|
43
83
|
|
|
84
|
+
def model_row_selector(model)
|
|
85
|
+
".#{model.model_name.singular.dasherize}-row[data-model-id='#{model.id}']"
|
|
86
|
+
end
|
|
87
|
+
|
|
44
88
|
def pretty_html
|
|
45
89
|
require "htmlbeautifier"
|
|
46
90
|
HtmlBeautifier.beautify(page.html)
|
|
47
91
|
end
|
|
48
92
|
|
|
93
|
+
def recorded_browser_logs
|
|
94
|
+
@recorded_browser_logs || []
|
|
95
|
+
end
|
|
96
|
+
|
|
49
97
|
def reset_indexeddb
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
logs_text = chrome_logs.map(&:message).join("\n")
|
|
65
|
-
logs_text.include?("\"All databases was deleted\"")
|
|
66
|
-
end
|
|
98
|
+
ApiMaker::ResetIndexedDbService.execute!(context: self)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def wait_for_action_cable_to_connect
|
|
102
|
+
sleep 0.5
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def wait_for_and_find(selector, *args)
|
|
106
|
+
element = find(selector, *args)
|
|
107
|
+
expect_no_browser_errors
|
|
108
|
+
element
|
|
109
|
+
rescue Capybara::ElementNotFound => e
|
|
110
|
+
expect_no_browser_errors
|
|
111
|
+
raise ApiMaker::SpecHelper::SelectorNotFoundError, e.message
|
|
67
112
|
end
|
|
68
113
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
114
|
+
def wait_for_attribute_row(attribute: nil, identifier: nil, label: nil, value: nil, **opts)
|
|
115
|
+
raise "No attribute or identifier given" if !attribute && !identifier
|
|
116
|
+
raise "No label or value was given" if !label && !value
|
|
117
|
+
|
|
118
|
+
tr_selector = ".component-api-maker-attribute-row"
|
|
119
|
+
tr_selector << "[data-attribute='#{attribute.camelize(:lower)}']" if attribute
|
|
120
|
+
tr_selector << "[data-identifier='#{identifier.camelize}']" if identifier
|
|
121
|
+
|
|
122
|
+
wait_for_selector "#{tr_selector} > .attribute-row-label", exact_text: label, **opts if label
|
|
123
|
+
wait_for_selector "#{tr_selector} > .attribute-row-value", exact_text: value, **opts if value
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def wait_for_browser(delay_sec: 0.2, message: "wait for browser", timeout_sec: 6)
|
|
127
|
+
WaitUtil.wait_for_condition(message, timeout_sec: timeout_sec, delay_sec: delay_sec) do
|
|
128
|
+
expect_no_browser_errors
|
|
72
129
|
yield
|
|
73
130
|
end
|
|
74
131
|
end
|
|
75
132
|
|
|
76
|
-
def
|
|
77
|
-
|
|
133
|
+
def wait_for_path(expected_path, **args)
|
|
134
|
+
args[:ignore_query] = true unless args.key?(:ignore_query)
|
|
78
135
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
current_message == expected_message
|
|
85
|
-
end
|
|
86
|
-
rescue WaitUtil::TimeoutError
|
|
87
|
-
expect(received_messages.uniq.reject(&:blank?)).to eq include expected_message
|
|
88
|
-
end
|
|
136
|
+
expect(page).to have_current_path(expected_path, args)
|
|
137
|
+
expect_no_browser_errors
|
|
138
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
|
139
|
+
expect_no_browser_errors
|
|
140
|
+
raise e
|
|
89
141
|
end
|
|
90
142
|
|
|
91
|
-
def wait_for_selector(selector)
|
|
92
|
-
|
|
143
|
+
def wait_for_selector(selector, *args)
|
|
144
|
+
expect(page).to have_selector selector, *args
|
|
145
|
+
expect_no_browser_errors
|
|
146
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
|
147
|
+
expect_no_browser_errors
|
|
148
|
+
raise ApiMaker::SpecHelper::SelectorNotFoundError, e.message
|
|
93
149
|
end
|
|
94
150
|
|
|
95
151
|
def wait_for_selectors(*selectors)
|
|
@@ -97,4 +153,12 @@ module ApiMaker::SpecHelper
|
|
|
97
153
|
wait_for_selector(selector)
|
|
98
154
|
end
|
|
99
155
|
end
|
|
156
|
+
|
|
157
|
+
def wait_for_no_selector(selector, *args)
|
|
158
|
+
expect(page).to have_no_selector selector, *args
|
|
159
|
+
expect_no_browser_errors
|
|
160
|
+
rescue RSpec::Expectations::ExpectationNotMetError => e
|
|
161
|
+
expect_no_browser_errors
|
|
162
|
+
raise ApiMaker::SpecHelper::SelectorFoundError, e.message
|
|
163
|
+
end
|
|
100
164
|
end
|
data/lib/api_maker/version.rb
CHANGED
data/lib/api_maker.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require "api_maker/engine"
|
|
2
2
|
|
|
3
|
+
require "active_record_better_dependent_error_messages"
|
|
3
4
|
require "active_record_query_fixer"
|
|
5
|
+
require "cancancan"
|
|
4
6
|
require "ransack"
|
|
5
7
|
require "service_pattern"
|
|
6
8
|
|
|
@@ -11,22 +13,24 @@ module ApiMaker
|
|
|
11
13
|
|
|
12
14
|
autoload :Ability, "#{dir}/ability"
|
|
13
15
|
autoload :AbilityLoader, "#{dir}/ability_loader"
|
|
14
|
-
autoload :
|
|
16
|
+
autoload :BaseCollectionInstance, "#{dir}/base_collection_instance"
|
|
15
17
|
autoload :BaseResource, "#{dir}/base_resource"
|
|
18
|
+
autoload :BaseService, "#{dir}/base_service"
|
|
16
19
|
autoload :CollectionSerializer, "#{dir}/collection_serializer"
|
|
17
20
|
autoload :CommandSpecHelper, "#{dir}/command_spec_helper"
|
|
18
21
|
autoload :Configuration, "#{dir}/configuration"
|
|
22
|
+
autoload :ExpectToBeAbleToHelper, "#{dir}/expect_to_able_to_helper"
|
|
19
23
|
autoload :IndividualCommand, "#{dir}/individual_command"
|
|
20
24
|
autoload :Loader, "#{dir}/loader"
|
|
21
25
|
autoload :MemoryStorage, "#{dir}/memory_storage"
|
|
22
26
|
autoload :ModelExtensions, "#{dir}/model_extensions"
|
|
23
27
|
autoload :PermittedParamsArgument, "#{dir}/permitted_params_argument"
|
|
24
28
|
autoload :Preloader, "#{dir}/preloader"
|
|
29
|
+
autoload :PreloaderBase, "#{dir}/preloader_base"
|
|
25
30
|
autoload :PreloaderBelongsTo, "#{dir}/preloader_belongs_to"
|
|
26
31
|
autoload :PreloaderHasMany, "#{dir}/preloader_has_many"
|
|
27
32
|
autoload :PreloaderHasOne, "#{dir}/preloader_has_one"
|
|
28
|
-
autoload :
|
|
29
|
-
autoload :RelationshipIncluder, "#{dir}/relationship_includer"
|
|
33
|
+
autoload :RelationshipPreloader, "#{dir}/relationship_preloader"
|
|
30
34
|
autoload :ResourceRouting, "#{dir}/resource_routing"
|
|
31
35
|
autoload :ResultParser, "#{dir}/result_parser"
|
|
32
36
|
autoload :Serializer, "#{dir}/serializer"
|