scrivito_sdk 1.0.0 → 1.1.0.rc1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/README.md +1 -1
  4. data/app/controllers/scrivito/legacy_redirect_controller.rb +11 -0
  5. data/app/controllers/scrivito/objs_controller.rb +15 -5
  6. data/app/controllers/scrivito/webservice_controller.rb +4 -3
  7. data/app/controllers/scrivito/workspaces_controller.rb +24 -29
  8. data/app/helpers/scrivito_helper.rb +82 -37
  9. data/app/views/scrivito/objs/binary_no_cache.json.jbuilder +1 -0
  10. data/app/views/scrivito/objs/search.json.jbuilder +5 -5
  11. data/app/views/scrivito/webservice/error.json.jbuilder +2 -2
  12. data/config/ca-bundle.crt +1 -1
  13. data/config/precedence_routes.rb +51 -48
  14. data/config/routes.rb +1 -14
  15. data/lib/assets/javascripts/scrivito_ui.js +2447 -266
  16. data/lib/assets/stylesheets/scrivito.css +1 -1
  17. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  18. data/lib/generators/scrivito/install/install_generator.rb +12 -0
  19. data/lib/generators/scrivito/install/templates/config/initializers/scrivito.rb +3 -0
  20. data/lib/scrivito/attribute_definition.rb +13 -1
  21. data/lib/scrivito/attribute_deserializer.rb +4 -4
  22. data/lib/scrivito/attribute_value_renderer.rb +79 -0
  23. data/lib/scrivito/backend/obj_data_cache.rb +15 -15
  24. data/lib/scrivito/backend/obj_data_from_rest.rb +1 -21
  25. data/lib/scrivito/backend/obj_query.rb +2 -2
  26. data/lib/scrivito/basic_obj.rb +17 -6
  27. data/lib/scrivito/binary.rb +13 -5
  28. data/lib/scrivito/cache/chainable.rb +10 -5
  29. data/lib/scrivito/cache/file_store.rb +4 -0
  30. data/lib/scrivito/cache/ram_store.rb +4 -0
  31. data/lib/scrivito/child_list_tag.rb +16 -14
  32. data/lib/scrivito/client_error.rb +10 -0
  33. data/lib/scrivito/cms_backend.rb +66 -291
  34. data/lib/scrivito/cms_data_cache.rb +7 -9
  35. data/lib/scrivito/cms_dispatch_controller.rb +1 -10
  36. data/lib/scrivito/cms_field_tag.rb +2 -1
  37. data/lib/scrivito/cms_rest_api.rb +2 -0
  38. data/lib/scrivito/cms_routing.rb +26 -22
  39. data/lib/scrivito/configuration.rb +38 -0
  40. data/lib/scrivito/connection_manager.rb +69 -0
  41. data/lib/scrivito/controller_actions.rb +2 -2
  42. data/lib/scrivito/controller_helper.rb +2 -2
  43. data/lib/scrivito/date_attribute.rb +4 -1
  44. data/lib/scrivito/deprecation.rb +5 -4
  45. data/lib/scrivito/editing_context.rb +2 -2
  46. data/lib/scrivito/errors.rb +17 -0
  47. data/lib/scrivito/image_tag.rb +2 -2
  48. data/lib/scrivito/link_parser.rb +10 -5
  49. data/lib/scrivito/meta_data_collection.rb +11 -0
  50. data/lib/scrivito/obj_collection.rb +1 -1
  51. data/lib/scrivito/obj_facet_value.rb +19 -7
  52. data/lib/scrivito/obj_params_parser.rb +43 -14
  53. data/lib/scrivito/obj_search_builder.rb +1 -2
  54. data/lib/scrivito/obj_search_enumerator/batch.rb +22 -0
  55. data/lib/scrivito/obj_search_enumerator/batch_iterator.rb +36 -0
  56. data/lib/scrivito/obj_search_enumerator/query_executor.rb +35 -0
  57. data/lib/scrivito/obj_search_enumerator.rb +218 -93
  58. data/lib/scrivito/obj_update_params_parser.rb +1 -0
  59. data/lib/scrivito/preset_routes.rb +25 -0
  60. data/lib/scrivito/revision.rb +13 -11
  61. data/lib/scrivito/route.rb +62 -0
  62. data/lib/scrivito/routing_extensions.rb +92 -0
  63. data/lib/scrivito/sdk_engine.rb +4 -0
  64. data/lib/scrivito/task.rb +77 -0
  65. data/lib/scrivito/type_computer.rb +3 -3
  66. data/lib/scrivito/ui_config.rb +4 -0
  67. data/lib/scrivito/user.rb +24 -18
  68. data/lib/scrivito/workspace/publish_checker.rb +2 -3
  69. data/lib/scrivito/workspace.rb +38 -6
  70. data/lib/scrivito/workspace_data.rb +0 -14
  71. metadata +14 -10
  72. data/lib/scrivito/content_service.rb +0 -121
  73. data/lib/scrivito/content_state.rb +0 -109
  74. data/lib/scrivito/content_state_caching.rb +0 -47
  75. data/lib/scrivito/content_state_visitor.rb +0 -19
  76. data/lib/scrivito/obj_data_from_service.rb +0 -63
  77. data/lib/scrivito/workspace_data_from_service.rb +0 -43
@@ -210,6 +210,7 @@ module Scrivito
210
210
  self.default_image_transformation = {}
211
211
  self.choose_homepage_callback = -> (env) { Obj.root }
212
212
  self.find_user_proc = nil
213
+ self.inject_preset_routes = true
213
214
  reset_editing_auth_callback!
214
215
  end
215
216
 
@@ -231,11 +232,48 @@ module Scrivito
231
232
 
232
233
 
233
234
  # @api public
235
+ # @deprecated The legacy routing is deprecated and will be removed in the next major
236
+ # version of Scrivito.
234
237
  #
235
238
  # Scrivito changed its routing to a slug first url scheme. This configuration option
236
239
  # allows you to switch back to the old id first url scheme.
237
240
  attr_accessor :legacy_routing
238
241
 
242
+ def legacy_routing=(enabled)
243
+ if enabled
244
+ Scrivito::Deprecation.warn(%[
245
+ The legacy routing is deprecated and will be removed in the next major version of Scrivito.
246
+ Please use the new scrivito_route api to replicate the legacy routes:
247
+ scrivito_route '/', using: 'homepage', via: :all
248
+ scrivito_route '(/):id/*slug', using: 'slug_id', via: :all
249
+ scrivito_route '/*permalink', using: 'permalink', via: all
250
+ ])
251
+ end
252
+ @legacy_routing = enabled
253
+ end
254
+
255
+ # @api public
256
+ #
257
+ # The +inject_preset_routes+ configuration is enabled by default and adds the default
258
+ # Scrivito routing to your application routes. It can be disabled by setting it to
259
+ # +false+ which lets you use {RoutingExtensions#scrivito_route scrivito_route} to
260
+ # customize the routing used by Scrivito.
261
+ attr_accessor :inject_preset_routes
262
+
263
+ def scrivito_route_enabled?
264
+ inject_preset_routes == false
265
+ end
266
+
267
+ def with_scrivito_route_enabled
268
+ begin
269
+ initial_value = inject_preset_routes
270
+ self.inject_preset_routes = false
271
+ yield
272
+ ensure
273
+ self.inject_preset_routes = initial_value
274
+ end
275
+ end
276
+
239
277
  attr_accessor :choose_homepage_callback, :check_batch_size
240
278
 
241
279
  #
@@ -61,11 +61,18 @@ module Scrivito
61
61
  end
62
62
  end
63
63
 
64
+ # for testing purposes
65
+ def connect_count
66
+ @connect_count || 0
67
+ end
68
+
64
69
  private
65
70
 
66
71
  attr_accessor :connection
67
72
 
68
73
  def ensure_started(timeout)
74
+ cleanup_dead_connection
75
+
69
76
  if @connection && @connection.started?
70
77
  configure_timeout(@connection, timeout)
71
78
  else
@@ -76,13 +83,75 @@ module Scrivito
76
83
  conn.cert_store = self.class.cert_store
77
84
  end
78
85
  configure_timeout(conn, timeout)
86
+
87
+ # keep_alive_timeout is not a timeout as in "wait no longer than X",
88
+ # but specifies how long a connection will be reused if it was idle.
89
+ # therefore the 'timeout' parameter is not applied here.
90
+ # instead we use a large value to maximize connection reuse.
91
+ # (ELB uses a keep alive timeout of 60 seconds).
92
+ conn.keep_alive_timeout = 59
93
+
79
94
  retry_twice_on_socket_error do
80
95
  conn.start
81
96
  end
97
+
98
+ increase_connect_count
82
99
  @connection = conn
83
100
  end
84
101
  end
85
102
 
103
+ def increase_connect_count
104
+ @connect_count ||= 0
105
+ @connect_count += 1
106
+ end
107
+
108
+ def cleanup_dead_connection
109
+ ensure_finished if connection_error?
110
+ end
111
+
112
+ # test if the http server closed the connection
113
+ # (i.e. due to a server-side keep alive timeout)
114
+ #
115
+ # if the server closes a connection, the socket of the connection
116
+ # is at EOF (End-Of-File). Using the connection would cause an error.
117
+ # Unfortunatly net/http does not detect this.
118
+ #
119
+ # Running into an error would be okay-ish for idempotent http requests,
120
+ # since retry is possible, but for non-idempotent requests (POST) the error
121
+ # would raise to the user.
122
+ #
123
+ # So we have to hack our way around net/http to access the socket directly.
124
+ # In order to do that, internals of net/http are accessed.
125
+ # If these internals change in future ruby versions, this method should
126
+ # simply returns `nil` (i.e. connection error are not detected)
127
+ # but not raise errors.
128
+ def connection_error?
129
+ return unless @connection
130
+
131
+ # compare: http://git.io/vltBR
132
+ socket = @connection.instance_variable_get("@socket")
133
+ return unless socket
134
+
135
+ # compare: http://git.io/vltRu
136
+ io = socket.try(:io)
137
+ return unless io && io.respond_to?(:read_nonblock)
138
+
139
+ begin
140
+ io.read_nonblock(1)
141
+
142
+ # if read_nonblock returns, there is unexpected data in the socket
143
+ # this indicates a connection problem, since http servers should never
144
+ # send data unless the client requests it.
145
+ true
146
+ rescue IO::WaitReadable, IO::WaitWritable
147
+ # these "errors" indicate the connection is still usable
148
+ false
149
+ rescue => e
150
+ # other errors indicate the connection has a problem
151
+ true
152
+ end
153
+ end
154
+
86
155
  def ensure_finished
87
156
  @connection.finish if @connection && @connection.started?
88
157
  @connection = nil
@@ -163,8 +163,8 @@ module ControllerActions
163
163
  #
164
164
  def deliver_file
165
165
  if binary = @obj.binary
166
- url = BinaryRouting.new(request, scrivito_engine).resolved_binary_obj_url(@obj, binary)
167
- redirect_to CmsRouting.match_protocol(url, request)
166
+ binary_routing = BinaryRouting.new(request, scrivito_engine)
167
+ redirect_to binary_routing.resolved_binary_obj_url(@obj, binary)
168
168
  else
169
169
  render text: 'Empty Blob', status: 404
170
170
  end
@@ -20,7 +20,7 @@ module Scrivito
20
20
  # @note +scrivito_path+ is also a helper method.
21
21
  #
22
22
  def scrivito_path(target, options = {})
23
- CmsRouting.new(request, main_app, scrivito_engine).path_or_url(target, "path", options)
23
+ CmsRouting.new(request, self, scrivito_engine).path_or_url(target, "path", options)
24
24
  end
25
25
 
26
26
  #
@@ -38,7 +38,7 @@ module Scrivito
38
38
  # @note +scrivito_url+ is also a helper method.
39
39
  #
40
40
  def scrivito_url(target, options = {})
41
- CmsRouting.new(request, main_app, scrivito_engine).path_or_url(target, "url", options)
41
+ CmsRouting.new(request, self, scrivito_engine).path_or_url(target, "url", options)
42
42
  end
43
43
 
44
44
  #
@@ -17,7 +17,10 @@ module Scrivito
17
17
 
18
18
  DateTime.iso8601(iso8601_date_time).in_time_zone
19
19
  rescue ArgumentError
20
- raise "The value is not a valid ISO 8601 date time: #{iso8601_date_time.inspect}"
20
+ raise Scrivito::ClientError.new(
21
+ "The value is not a valid ISO 8601 date time: #{iso8601_date_time.inspect}",
22
+ 400
23
+ )
21
24
  end
22
25
 
23
26
  def self.serialize_for_backend(attribute_value)
@@ -5,14 +5,15 @@ module Scrivito
5
5
  ActiveSupport::Deprecation.warn(message, source)
6
6
  end
7
7
 
8
- def warn_method(depricated_method_name, new_method_name = nil)
9
- warn(warn_method_message(depricated_method_name, new_method_name), caller(1))
8
+ def warn_method(deprecated_method_name, new_method_name = nil)
9
+ warn(warn_method_message(deprecated_method_name, new_method_name), caller(1))
10
10
  end
11
11
 
12
12
  private
13
13
 
14
- def warn_method_message(depricated_method_name, new_method_name = nil)
15
- message = "The method #{depricated_method_name} will be removed from the Scrivito SDK. "
14
+ def warn_method_message(deprecated_method_name, new_method_name = nil)
15
+ message = "The method #{deprecated_method_name} is deprecated and will be removed "\
16
+ "from the Scrivito SDK in the next major version. "
16
17
  message << "Please use #{new_method_name} instead" if new_method_name
17
18
  message
18
19
  end
@@ -77,8 +77,8 @@ class EditingContext
77
77
 
78
78
  def to_params
79
79
  {
80
- '_scrivito_display_mode' => display_mode,
81
- '_scrivito_workspace_id' => selected_workspace.id,
80
+ _scrivito_display_mode: display_mode,
81
+ _scrivito_workspace_id: selected_workspace.id,
82
82
  }
83
83
  end
84
84
 
@@ -4,8 +4,25 @@ module Scrivito
4
4
  class ScrivitoError < StandardError
5
5
  end
6
6
 
7
+ class ApplicationError < ScrivitoError
8
+ def http_code
9
+ 412
10
+ end
11
+
12
+ def as_json
13
+ {message: message, message_for_editor: message}
14
+ end
15
+ end
16
+
7
17
  # @api public
8
18
  class ResourceNotFound < ScrivitoError
19
+ def http_code
20
+ 404
21
+ end
22
+
23
+ def as_json
24
+ {message: message}
25
+ end
9
26
  end
10
27
 
11
28
  # @api public
@@ -37,13 +37,13 @@ class ImageTag < Struct.new(:view_context)
37
37
  BinaryRouting.new(request, scrivito_engine)
38
38
  .binary_obj_url(obj_or_widget, attribute_value, image_options)
39
39
  else
40
- path = CmsRouting.new(request, main_app, scrivito_engine, image_options)
40
+ path = CmsRouting.new(request, view_context, scrivito_engine, image_options)
41
41
  .path_or_url(attribute_value, :path)
42
42
  path unless path == CmsRouting::LINK_TO_EMPTY_BLOB
43
43
  end
44
44
  end
45
45
 
46
- delegate :request, :main_app, :scrivito_engine, to: :view_context
46
+ delegate :request, :scrivito_engine, to: :view_context
47
47
  end
48
48
 
49
49
  end
@@ -63,9 +63,9 @@ module Scrivito
63
63
  end
64
64
 
65
65
  def find_obj(uri)
66
- return nil unless application_uri?(uri) && application_route?(uri)
66
+ return nil unless application_uri?(uri)
67
67
 
68
- route_params = route(uri)
68
+ route_params = obj_route_params(uri)
69
69
 
70
70
  if id = route_params[:id]
71
71
  Obj.find(id)
@@ -75,9 +75,14 @@ module Scrivito
75
75
  rescue Scrivito::ResourceNotFound
76
76
  end
77
77
 
78
- def application_route?(uri)
79
- route_params = route(uri)
80
- route_params && route_params[:controller] == 'scrivito/cms_dispatch'
78
+ def obj_route_params(uri)
79
+ dispatch_route_for(sdk_route(uri)) || dispatch_route_for(route(uri)) || {}
80
+ end
81
+
82
+ def dispatch_route_for(route_params)
83
+ if route_params && route_params[:controller] == 'scrivito/cms_dispatch'
84
+ route_params
85
+ end
81
86
  end
82
87
 
83
88
  def sdk_route(uri)
@@ -38,6 +38,17 @@ module Scrivito
38
38
  # [+iptc_state+] The state in which the image was produced.
39
39
  # [+iptc_country_name+] The country in which the image was produced.
40
40
  # [+iptc_country_code+] The code of the country in which the image was produced.
41
+ #
42
+ # == Searching for meta data
43
+ #
44
+ # Meta data is taken into account when searching. See the
45
+ # {Scrivito::ObjSearchEnumerator ObjSearchEnumerator} for details.
46
+ #
47
+ # @example Search for all JPEGs
48
+ # Image.where('blob:content_type', :equals, 'image/jpeg')
49
+ #
50
+ # @example Search for wide images
51
+ # Image.where('blob:width', :is_greater_than, 1024)
41
52
  class MetaDataCollection
42
53
  def initialize(attributes)
43
54
  @attributes = ActiveSupport::HashWithIndifferentAccess.new(attributes)
@@ -85,7 +85,7 @@ module Scrivito
85
85
  # @return [ObjSearchEnumerator]
86
86
  # @api public
87
87
  def all
88
- ObjSearchEnumerator.new(workspace).batch_size(1000)
88
+ ObjSearchEnumerator.new(workspace, 1000)
89
89
  end
90
90
 
91
91
  # Returns an {ObjSearchEnumerator} of all {BasicObj Obj}s with the given +obj_class+.
@@ -1,6 +1,9 @@
1
1
  module Scrivito
2
+ #
2
3
  # Instances of this class represent the result of a faceted search.
3
- # @api beta
4
+ #
5
+ # @api public
6
+ #
4
7
  class ObjFacetValue
5
8
 
6
9
  def initialize(value, total, included_objs = [])
@@ -9,32 +12,41 @@ module Scrivito
9
12
  @included_objs = included_objs
10
13
  end
11
14
 
12
- # @api beta
15
+ #
13
16
  # The value of the attribute name of this ObjFacetValue.
14
17
  #
18
+ # @api public
15
19
  # @return [String]
20
+ #
16
21
  def name
17
22
  @name
18
23
  end
19
24
 
20
- # @api beta
25
+ #
21
26
  # Total number of Objs available that have this value.
22
27
  #
23
- # Note that this refers to all Objs,
24
- # not just the Objs included in this search.
28
+ # @api public
29
+ #
30
+ # Note that this refers to all Objs, not just the Objs included in this search.
25
31
  # Also note that the count is approximate.
32
+ #
26
33
  # @return [Integer]
34
+ #
27
35
  def count
28
36
  @count
29
37
  end
30
38
 
31
- # @api beta
39
+ #
32
40
  # The Objs that were included in this search.
33
41
  #
34
- # if you did not specify +include_objs+ in your facet options,
42
+ # @api public
43
+ #
44
+ # If you did not specify +include_objs+ in your facet options,
35
45
  # an empty array is returned.
36
46
  # The Objs are ordered by relevance.
47
+ #
37
48
  # @return [Array<BasicObj>]
49
+ #
38
50
  def included_objs
39
51
  @included_objs
40
52
  end
@@ -28,26 +28,55 @@ module Scrivito
28
28
  def parse_widgetlist_params(params)
29
29
  params.map do |widget_id_or_params|
30
30
  if widget_id_or_params.is_a?(Hash)
31
- action, widget_params = widget_id_or_params.flatten
32
- case action
33
- when 'create' then Widget.new(widget_params, context.slice(:scrivito_user))
34
- when 'copy'
35
- widget_id = widget_params['widget_id']
36
- widget = Workspace.find(widget_params['workspace_id'])
37
- .objs.find(widget_params['obj_id'])
38
- .widgets[widget_id]
39
- raise ResourceNotFound, "Could not find Widget with id #{widget_id}" unless widget
40
- widget.copy
41
- else raise UnknownWidgetAction
42
- end
31
+ parse_widgetlist_params_with_action(widget_id_or_params)
43
32
  else
44
- obj.widgets[widget_id_or_params]
33
+ widget = obj.widgets[widget_id_or_params]
34
+ raise_widget_not_found_error(widget_id_or_params) unless widget
35
+ widget
45
36
  end
46
37
  end
47
38
  end
48
39
 
40
+ def parse_widgetlist_params_with_action(params)
41
+ action, widget_params = params.flatten
42
+ case action
43
+ when 'create' then Widget.new(widget_params, context.slice(:scrivito_user))
44
+ when 'copy'
45
+ widget_id = widget_params['widget_id']
46
+ widget = Workspace.find(widget_params['workspace_id'])
47
+ .objs.find(widget_params['obj_id'])
48
+ .widgets[widget_id]
49
+ raise_widget_not_found_error(widget_id) unless widget
50
+ widget.copy
51
+ else raise UnknownWidgetAction
52
+ end
53
+ end
54
+
49
55
  def parse_binary_params(params)
50
- UploadedBinary.new(params) if params
56
+ return unless params
57
+
58
+ if params[:copy]
59
+ parse_copy_binary_params(params[:copy])
60
+ else
61
+ UploadedBinary.new(params)
62
+ end
63
+ end
64
+
65
+ def parse_copy_binary_params(params)
66
+ obj = Obj.find(params[:obj_id])
67
+
68
+ if widget_id = params[:widget_id]
69
+ widget = obj.widgets[widget_id]
70
+ raise_widget_not_found_error(widget_id) unless widget
71
+ end
72
+
73
+ if binary = (widget || obj)[params[:attribute_name]]
74
+ binary.copy(filename: params[:filename], content_type: params[:content_type])
75
+ end
76
+ end
77
+
78
+ def raise_widget_not_found_error(widget_id)
79
+ raise ResourceNotFound, "Could not find Widget with id #{widget_id}"
51
80
  end
52
81
  end
53
82
  end
@@ -40,8 +40,7 @@ class ObjSearchBuilder < Struct.new(:query)
40
40
  end
41
41
 
42
42
  def set_order
43
- enumerator.order(query[:order]) if query[:order]
44
- enumerator.reverse_order if query[:reverse_order]
43
+ enumerator.order(Hash[*query[:order]]) if query[:order]
45
44
  end
46
45
 
47
46
  def set_batch_size
@@ -0,0 +1,22 @@
1
+ module Scrivito
2
+ class ObjSearchEnumerator
3
+ class Batch
4
+ def initialize(objs, total, continuation, facets, previously_fetched_ids)
5
+ @objs = filter_duplicates(objs, previously_fetched_ids)
6
+ @total = total
7
+ @continuation = continuation
8
+ @facets = facets
9
+ end
10
+
11
+ attr_reader :objs, :total, :continuation, :facets
12
+
13
+ private
14
+
15
+ def filter_duplicates(objs, previously_fetched_ids)
16
+ objs.reject do |obj|
17
+ previously_fetched_ids.include?(obj.id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ module Scrivito
2
+ class ObjSearchEnumerator
3
+ class BatchIterator
4
+ def initialize(workspace, query, initial_batch)
5
+ @query = query
6
+ @executor = ObjSearchEnumerator::QueryExecutor.new(workspace)
7
+ @all_fetched_ids = Set.new
8
+ @initial_batch = initial_batch
9
+ end
10
+
11
+ attr_reader :total
12
+
13
+ def each
14
+ current_batch = initial_batch || fetch_batch
15
+
16
+ loop do
17
+ yield current_batch
18
+ break unless current_batch.continuation
19
+ current_batch = fetch_batch(current_batch.continuation)
20
+ end
21
+
22
+ @total = @all_fetched_ids.size
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :query, :executor, :initial_batch
28
+
29
+ def fetch_batch(continuation=nil)
30
+ batch = executor.call(query, continuation, @all_fetched_ids)
31
+ @all_fetched_ids += batch.objs.map(&:id)
32
+ batch
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ module Scrivito
2
+ class ObjSearchEnumerator
3
+ class QueryExecutor
4
+ def initialize(workspace)
5
+ @workspace = workspace
6
+ end
7
+
8
+ def call(query, continuation=nil, fetched_ids=[])
9
+ query['continuation'] = continuation if continuation
10
+ response = CmsBackend.instance.search_objs(workspace, query)
11
+
12
+ ObjSearchEnumerator::Batch.new(
13
+ build_objs(response),
14
+ response['total'].to_i,
15
+ response['continuation'],
16
+ response['facets'],
17
+ fetched_ids
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :workspace
24
+
25
+ def build_objs(response)
26
+ obj_ids = response['results'].map { |result| result['id'] }
27
+ if obj_ids.present?
28
+ workspace.objs.find_including_deleted(obj_ids)
29
+ else
30
+ []
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end