module DeepUnrest::Read
Public Class Methods
create_read_mappings(params, user, addr = [])
click to toggle source
# File lib/deep_unrest/read.rb, line 5 def self.create_read_mappings(params, user, addr = []) return unless params params.map do |k, v| resource_addr = [*addr, k] uuid = SecureRandom.uuid v[:uuid] = uuid [{ klass: k.singularize.classify.constantize, policy: "#{k.singularize.classify}Policy".constantize, resource: "#{k.singularize.classify}Resource".constantize, scope_type: :index, addr: resource_addr, key: k.camelize(:lower), uuid: uuid, query: DeepUnrest.deep_underscore_keys(v) }, *create_read_mappings(v[:include], user, [*resource_addr, :include])] end.flatten.compact end
execute_queries(ctx, mappings, parent_context = {}, included = [], meta = [], addr = [])
click to toggle source
# File lib/deep_unrest/read.rb, line 191 def self.execute_queries(ctx, mappings, parent_context = {}, included = [], meta = [], addr = []) mappings.select { |m| m[:addr].size == 1 }.each do |item| item[:results] = execute_query(ctx, item, mappings, parent_context, included, meta, addr) end [included, meta] end
execute_query(ctx, item, mappings, parent_context, included, meta, addr, parent = nil)
click to toggle source
# File lib/deep_unrest/read.rb, line 183 def self.execute_query(ctx, item, mappings, parent_context, included, meta, addr, parent = nil) if get_query_type(item) == :list query_list(ctx, item, mappings, parent_context, included, meta, addr, parent) else query_item(ctx, item, mappings, parent_context, included, meta, addr, parent) end end
format_processor_results(resource_klass, processor_result)
click to toggle source
# File lib/deep_unrest/read.rb, line 105 def self.format_processor_results(resource_klass, processor_result) results = processor_result.resource_set.resource_klasses[resource_klass] || {} results.values.map {|r| r[:resource] } end
format_response(mappings)
click to toggle source
# File lib/deep_unrest/read.rb, line 198 def self.format_response(mappings) response = {} mappings.each do |mapping| DeepUnrest.set_attr(response, mapping[:addr], mapping[:serialized_result]) end response end
get_paginator(query, _parent)
click to toggle source
# File lib/deep_unrest/read.rb, line 93 def self.get_paginator(query, _parent) opts = query.dig(:paginate) || {} params = ActionController::Parameters.new(opts) case params[:type] when :offset OffsetPaginator.new(params) else PagedPaginator.new(params) end end
get_query_type(item)
click to toggle source
# File lib/deep_unrest/read.rb, line 177 def self.get_query_type(item) return :detail unless plural?(item[:key]) :list end
plural?(str)
click to toggle source
# File lib/deep_unrest/read.rb, line 24 def self.plural?(str) str.pluralize == str && str.singularize != str end
query_item(ctx, mapping, mappings, parent_context, included, meta, addr, _parent)
click to toggle source
# File lib/deep_unrest/read.rb, line 65 def self.query_item(ctx, mapping, mappings, parent_context, included, meta, addr, _parent) query = resolve_conditions(mapping[:query].deep_dup, parent_context) raise DeepUnrest::InvalidQuery unless query[:id] || query[:find] if query.key?(:if) return unless query[:if][:attribute] == query[:if][:matches] end record = if query.key?(:id) mapping[:scope].find(query[:id]) if query.key?(:id) else mapping[:scope].find_by!(query[:find]) end next_addr = [*addr, mapping[:key]] result = { **mapping, addr: next_addr, record: record } included << result recurse_included_queries(ctx, result, mappings, parent_context, included, meta, [*next_addr, :include]) end
query_list(ctx, item, mappings, parent_context, included, meta, addr, parent)
click to toggle source
# File lib/deep_unrest/read.rb, line 110 def self.query_list(ctx, item, mappings, parent_context, included, meta, addr, parent) base_query = item[:query].deep_dup extension = base_query.dig(:extend, parent&.fetch(:record)&.id&.to_s&.underscore) || {} query = resolve_conditions(base_query.deep_merge(extension), parent_context) paginator = get_paginator(query, parent) resource = item[:resource] # monkey patch the resource to only show authorized records old_records = resource.method(:records) resource.define_singleton_method(:records) { |ctx| item[:scope].merge(old_records.call(ctx)) } # transform sort value casing for rails sort_criteria = query[:sort]&.map { |s| s.clone.merge(field: s[:field].underscore) } serializer = JSONAPI::ResourceSerializer.new(resource) processor = JSONAPI::Processor.new(resource, :find, filters: query[:filter] || {}, context: ctx, sort_criteria: sort_criteria, serializer: serializer, paginator: paginator) jsonapi_result = processor.process resource_results = format_processor_results(resource, jsonapi_result) # un-monkey patch the resource :records method resource.define_singleton_method(:records, old_records) meta << { addr: [*addr, item[:key], 'meta'], serialized_result: { paginationParams: jsonapi_result.pagination_params, recordCount: jsonapi_result.record_count, sort: query[:sort], paginate: query[:paginate], filter: DeepUnrest.deep_camelize_keys(query[:filter]) } } # make sure to return empty array if no results are found for this node if resource_results.empty? meta << { addr: [*addr, item[:key], 'data'], serialized_result: [] } end resource_results.each_with_index do |record, i| next_addr = [*addr, item[:key], 'data[]', i] result = { **item, addr: next_addr, record: record._model } included << result recurse_included_queries(ctx, result, mappings, parent_context, included, meta, [*next_addr, :include]) end rescue StandardError => e # un-monkey patch the resource :records method resource.define_singleton_method(:records, old_records) end
read(ctx, params, user)
click to toggle source
# File lib/deep_unrest/read.rb, line 206 def self.read(ctx, params, user) # create mappings for assembly / disassembly mappings = create_read_mappings(params.to_unsafe_h, user) # authorize user for requested scope(s) DeepUnrest.authorization_strategy.authorize(mappings, user) # collect authorized scopes DeepUnrest.collect_authorized_scopes(mappings, user) # read data data, meta = execute_queries(ctx, mappings) # serialize using JSONAPI resource serializers serialize_results(ctx, data) # assemble results into something resembling shape of request format_response([*data, *meta]) end
recurse_included_queries(ctx, item, mappings, parent_context, included, meta, addr)
click to toggle source
# File lib/deep_unrest/read.rb, line 54 def self.recurse_included_queries(ctx, item, mappings, parent_context, included, meta, addr) return unless item[:query].key?(:include) item[:query][:include].each do |_k, v| next_context = parent_context.clone next_context[item[:key].singularize] = item[:record] next_mapping = mappings.find { |m| m[:uuid] == v[:uuid] }.clone execute_query(ctx, next_mapping, mappings, next_context, included, meta, addr, item) end end
resolve_conditions(query, parent_context)
click to toggle source
# File lib/deep_unrest/read.rb, line 34 def self.resolve_conditions(query, parent_context) if query.is_a? Array query.each { |item| resolve_conditions(item, parent_context) } elsif query.is_a? Hash query.each do |k, v| next unless v.is_a? Hash if v[:from_context] name, attr = v[:from_context].split('.') next unless parent_context[name] query[k] = parent_context[name].send(attr.underscore) else resolve_conditions(v, parent_context) end end end query end
serialize_results(ctx, data)
click to toggle source
# File lib/deep_unrest/read.rb, line 28 def self.serialize_results(ctx, data) data.each do |item| item[:serialized_result] = DeepUnrest.serialize_result(ctx, item) end end