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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/app/api_maker/api_helpers/api_maker_helpers.rb +5 -0
  3. data/app/api_maker/services/can_can/load_abilities.rb +30 -0
  4. data/app/api_maker/services/devise/sign_in.rb +64 -0
  5. data/app/api_maker/services/devise/sign_out.rb +9 -0
  6. data/app/api_maker/services/models/find_or_create_by.rb +18 -0
  7. data/app/channels/api_maker/subscriptions_channel.rb +33 -2
  8. data/app/controllers/api_maker/base_controller.rb +7 -3
  9. data/app/controllers/api_maker/commands_controller.rb +26 -4
  10. data/app/controllers/api_maker/session_statuses_controller.rb +1 -1
  11. data/app/services/api_maker/abilities_loader.rb +104 -0
  12. data/app/services/api_maker/application_service.rb +2 -1
  13. data/app/services/api_maker/base_command.rb +248 -0
  14. data/app/services/api_maker/collection_command_service.rb +29 -15
  15. data/app/services/api_maker/collection_loader.rb +124 -0
  16. data/app/services/api_maker/command_failed_error.rb +3 -0
  17. data/app/services/api_maker/command_response.rb +17 -6
  18. data/app/services/api_maker/command_service.rb +3 -3
  19. data/app/services/api_maker/create_command.rb +11 -26
  20. data/app/services/api_maker/create_command_service.rb +3 -3
  21. data/app/services/api_maker/database_type.rb +9 -0
  22. data/app/services/api_maker/deep_merge_params.rb +26 -0
  23. data/app/services/api_maker/deserializer.rb +35 -0
  24. data/app/services/api_maker/destroy_command.rb +15 -21
  25. data/app/services/api_maker/destroy_command_service.rb +3 -3
  26. data/app/services/api_maker/generate_react_native_api_service.rb +3 -19
  27. data/app/services/api_maker/include_helpers.rb +17 -0
  28. data/app/services/api_maker/index_command.rb +8 -88
  29. data/app/services/api_maker/index_command_service.rb +5 -5
  30. data/app/services/api_maker/js_method_namer_service.rb +1 -1
  31. data/app/services/api_maker/locals_from_controller.rb +14 -0
  32. data/app/services/api_maker/member_command_service.rb +15 -13
  33. data/app/services/api_maker/model_classes_java_script_generator_service.rb +37 -0
  34. data/app/services/api_maker/model_content_generator_service.rb +17 -21
  35. data/app/services/api_maker/models/save.rb +29 -0
  36. data/app/services/api_maker/models_finder_service.rb +6 -2
  37. data/app/services/api_maker/models_generator_service.rb +6 -43
  38. data/app/services/api_maker/move_components_to_routes.rb +50 -0
  39. data/app/services/api_maker/primary_id_for_model.rb +6 -0
  40. data/app/services/api_maker/reset_indexed_db_service.rb +36 -0
  41. data/app/services/api_maker/routes_file_reloader.rb +20 -0
  42. data/app/services/api_maker/select_columns_on_collection.rb +78 -0
  43. data/app/services/api_maker/select_parser.rb +32 -0
  44. data/app/services/api_maker/service_command.rb +27 -0
  45. data/app/services/api_maker/service_command_service.rb +14 -0
  46. data/app/services/api_maker/simple_model_errors.rb +52 -0
  47. data/app/services/api_maker/update_command.rb +8 -24
  48. data/app/services/api_maker/update_command_service.rb +3 -3
  49. data/app/services/api_maker/valid_command.rb +4 -13
  50. data/app/services/api_maker/valid_command_service.rb +3 -3
  51. data/app/services/api_maker/validation_errors_generator_service.rb +146 -0
  52. data/app/views/api_maker/_data.html.erb +17 -11
  53. data/config/routes.rb +0 -2
  54. data/lib/api_maker/ability.rb +22 -7
  55. data/lib/api_maker/ability_loader.rb +9 -6
  56. data/lib/api_maker/base_collection_instance.rb +15 -0
  57. data/lib/api_maker/base_resource.rb +135 -9
  58. data/lib/api_maker/base_service.rb +14 -0
  59. data/lib/api_maker/collection_serializer.rb +95 -34
  60. data/lib/api_maker/command_spec_helper.rb +41 -11
  61. data/lib/api_maker/configuration.rb +31 -4
  62. data/lib/api_maker/expect_to_able_to_helper.rb +31 -0
  63. data/lib/api_maker/individual_command.rb +24 -9
  64. data/lib/api_maker/javascript/model-template.js.erb +39 -25
  65. data/lib/api_maker/javascript/models.js.erb +6 -0
  66. data/lib/api_maker/loader.rb +1 -1
  67. data/lib/api_maker/memory_storage.rb +1 -1
  68. data/lib/api_maker/model_extensions.rb +34 -18
  69. data/lib/api_maker/permitted_params_argument.rb +5 -1
  70. data/lib/api_maker/preloader.rb +71 -32
  71. data/lib/api_maker/preloader_base.rb +108 -0
  72. data/lib/api_maker/preloader_belongs_to.rb +34 -33
  73. data/lib/api_maker/preloader_has_many.rb +45 -39
  74. data/lib/api_maker/preloader_has_one.rb +30 -47
  75. data/lib/api_maker/railtie.rb +3 -11
  76. data/lib/api_maker/relationship_preloader.rb +42 -0
  77. data/lib/api_maker/resource_routing.rb +18 -4
  78. data/lib/api_maker/result_parser.rb +34 -20
  79. data/lib/api_maker/serializer.rb +53 -22
  80. data/lib/api_maker/spec_helper/browser_logs.rb +14 -0
  81. data/lib/api_maker/spec_helper/execute_collection_command.rb +46 -0
  82. data/lib/api_maker/spec_helper/execute_member_command.rb +52 -0
  83. data/lib/api_maker/spec_helper/expect_no_browser_errors.rb +18 -0
  84. data/lib/api_maker/spec_helper/wait_for_expect.rb +20 -0
  85. data/lib/api_maker/spec_helper/wait_for_flash_message.rb +21 -0
  86. data/lib/api_maker/spec_helper.rb +112 -48
  87. data/lib/api_maker/version.rb +1 -1
  88. data/lib/api_maker.rb +7 -3
  89. metadata +108 -89
  90. data/README.md +0 -476
  91. data/app/controllers/api_maker/devise_controller.rb +0 -60
  92. data/lib/api_maker/base_command.rb +0 -81
  93. data/lib/api_maker/javascript/api.js +0 -92
  94. data/lib/api_maker/javascript/base-model.js +0 -543
  95. data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +0 -16
  96. data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +0 -47
  97. data/lib/api_maker/javascript/bootstrap/card.jsx +0 -79
  98. data/lib/api_maker/javascript/bootstrap/checkbox.jsx +0 -127
  99. data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +0 -105
  100. data/lib/api_maker/javascript/bootstrap/live-table.jsx +0 -168
  101. data/lib/api_maker/javascript/bootstrap/money-input.jsx +0 -136
  102. data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +0 -80
  103. data/lib/api_maker/javascript/bootstrap/select.jsx +0 -168
  104. data/lib/api_maker/javascript/bootstrap/string-input.jsx +0 -203
  105. data/lib/api_maker/javascript/cable-connection-pool.js +0 -169
  106. data/lib/api_maker/javascript/cable-subscription-pool.js +0 -111
  107. data/lib/api_maker/javascript/cable-subscription.js +0 -33
  108. data/lib/api_maker/javascript/collection.js +0 -186
  109. data/lib/api_maker/javascript/commands-pool.js +0 -123
  110. data/lib/api_maker/javascript/custom-error.js +0 -14
  111. data/lib/api_maker/javascript/deserializer.js +0 -35
  112. data/lib/api_maker/javascript/devise.js.erb +0 -113
  113. data/lib/api_maker/javascript/error-logger.js +0 -119
  114. data/lib/api_maker/javascript/event-connection.jsx +0 -24
  115. data/lib/api_maker/javascript/event-created.jsx +0 -26
  116. data/lib/api_maker/javascript/event-destroyed.jsx +0 -26
  117. data/lib/api_maker/javascript/event-emitter-listener.jsx +0 -32
  118. data/lib/api_maker/javascript/event-listener.jsx +0 -41
  119. data/lib/api_maker/javascript/event-updated.jsx +0 -26
  120. data/lib/api_maker/javascript/form-data-to-object.js +0 -70
  121. data/lib/api_maker/javascript/included.js +0 -39
  122. data/lib/api_maker/javascript/key-value-store.js +0 -47
  123. data/lib/api_maker/javascript/logger.js +0 -23
  124. data/lib/api_maker/javascript/model-name.js +0 -21
  125. data/lib/api_maker/javascript/models-response-reader.js +0 -43
  126. data/lib/api_maker/javascript/paginate.jsx +0 -128
  127. data/lib/api_maker/javascript/params.js +0 -68
  128. data/lib/api_maker/javascript/resource-route.jsx +0 -75
  129. data/lib/api_maker/javascript/resource-routes.jsx +0 -36
  130. data/lib/api_maker/javascript/result.js +0 -25
  131. data/lib/api_maker/javascript/session-status-updater.js +0 -113
  132. data/lib/api_maker/javascript/sort-link.jsx +0 -88
  133. data/lib/api_maker/javascript/updated-attribute.jsx +0 -60
  134. data/lib/api_maker/preloader_through.rb +0 -101
  135. data/lib/api_maker/relationship_includer.rb +0 -42
@@ -1,14 +1,14 @@
1
1
  class ApiMaker::ResultParser
2
- attr_reader :ability, :args
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, args: nil, controller: 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
- @args = args || controller&.__send__(:api_maker_args) || {}
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
- result = {}
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.map { |value| parse_object(value) }
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
- serializer = ApiMaker::Serializer.new(ability: ability, args: args, model: object)
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
@@ -1,5 +1,7 @@
1
1
  class ApiMaker::Serializer
2
- attr_reader :ability, :args, :model
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, args: {}, model:, select: 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
- ApiMaker::Configuration.profile("attributes") do
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 ||= @select || resource.default_select
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
- if resource_instance.respond_to?(attribute)
43
- resource_instance.__send__(attribute)
44
- else
45
- model.__send__(attribute)
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 ||= ApiMaker::MemoryStorage.current.resource_for_model(model.class)
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 ||= resource.new(ability: ability, args: args, model: model)
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
- @result ||= begin
67
- result = {a: attributes}
68
- result[:r] = @relationships if @relationships # Only include relationships if set
69
- result
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(_options = nil)
74
- result
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(_options = nil)
78
- JSON.generate(as_json)
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
- "<ApiMaker::Serializer id=\"#{model.id}\" model=\"#{model.class.name}\" relationships=\"#{relationships}\">"
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,14 @@
1
+ module ApiMaker::SpecHelper::BrowserLogs
2
+ def browser_logs
3
+ logs = if browser_firefox?
4
+ []
5
+ else
6
+ chrome_logs
7
+ end
8
+
9
+ @recorded_browser_logs ||= []
10
+ @recorded_browser_logs += logs
11
+
12
+ logs
13
+ end
14
+ 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
- module ApiMaker::SpecHelper
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 expect_no_chrome_window_errors
7
- sleep 1
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 = RuntimeError.new("#{last_error["errorClass"]}: #{last_error["message"]}")
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 expect_no_chrome_errors
25
- logs = chrome_logs.map(&:to_s)
26
- logs = logs.reject { |log| log.include?("Warning: Can't perform a React state update on an unmounted component.") }
27
- return if !logs || !logs.join("\n").include?("SEVERE ")
49
+ def expect_no_errors
50
+ expect_no_flash_errors
51
+ expect_no_browser_errors
52
+ end
28
53
 
29
- expect_no_chrome_window_errors
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
- puts logs.join("\n")
32
- expect(logs).to eq nil
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 expect_no_errors
36
- expect_no_flash_errors
37
- expect_no_chrome_errors
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
- execute_script "
51
- indexedDB.databases().then(function(databases) {
52
- var promises = []
53
- for(var database of databases) {
54
- promises.push(indexedDB.deleteDatabase(database.name))
55
- }
56
-
57
- Promise.all(promises).then(function() {
58
- console.error('All databases was deleted')
59
- })
60
- })
61
- "
62
-
63
- wait_for_condition do
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 wait_for_chrome(delay_sec: 0.5, timeout_sec: 6)
70
- WaitUtil.wait_for_condition("wait for chrome", timeout_sec: timeout_sec, delay_sec: delay_sec) do
71
- expect_no_chrome_errors
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 wait_for_flash_message(expected_message, delay_sec: 0.5, timeout_sec: 10)
77
- received_messages = []
133
+ def wait_for_path(expected_path, **args)
134
+ args[:ignore_query] = true unless args.key?(:ignore_query)
78
135
 
79
- begin
80
- WaitUtil.wait_for_condition("wait for flash message", timeout_sec: timeout_sec, delay_sec: delay_sec) do
81
- expect_no_chrome_errors
82
- current_message = flash_message_text
83
- received_messages << current_message
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
- wait_for_chrome { page.has_selector?(selector) }
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
@@ -1,3 +1,3 @@
1
1
  module ApiMaker
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
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 :BaseCommand, "#{dir}/base_command"
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 :PreloaderThrough, "#{dir}/preloader_through"
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"