# frozen_string_literal: true require 'active_support/core_ext/string' require 'zuora_connect_ui/serializer/relationship' module ZuoraConnectUi # Take a resource and dump it to Google Style JSON, really fast class Serializer def initialize(resource, options = {}) @resource = resource @kind = options[:kind] @is_collection = options[:is_collection] @record_type = parse_record_type(@kind, @resource) @associations = parse_associations( options[:has_many], options[:belongs_to], options[:has_one] ) @fields = parse_fields(options[:fields]) # TODO: if an attribute given is reserved, throw an error end def serialized_json require 'oj' # Enable to show Oj working in console # Oj.default_options = { trace: true } Oj.dump(serializable_hash, mode: :compat, time_format: :ruby) end def serializable_hash return hash_for_collection if collection?(@resource) hash_for_one end def hash_for_one hash = { data: nil } return hash unless @resource hash[:data] = record_hash(@resource, @record_type) return hash if @associations.empty? hash[:data][:relationships] = get_associations([@resource]) hash end def hash_for_collection hash = { data: {} } hash[:data][:items] = @resource.each_with_object([]) do |resource, items| items << record_hash(resource, @record_type) end return hash if @associations.empty? hash[:data][:relationships] = get_associations(@resource) hash end private def key_transform(input) input.to_s.camelize(:lower).to_sym end def collection?(resource) return @is_collection unless @is_collection.nil? resource.respond_to?(:size) && !resource.respond_to?(:each_pair) end def record_hash(resource, record_type) hash = @kind.nil? ? {} : { kind: record_type } hash[:id] = resource.id @fields[record_type].each do |field| hash[key_transform(field)] = resource.public_send(field) end @associations.each do |association| field = association.id_method_name hash[key_transform(field)] = resource.public_send(field) end hash end def get_associations(resource) @associations.each_with_object({}) do |a, h| hash_key = key_transform(a.plural_type) h[hash_key] = a.serialize(resource, @fields[a.record_type]) end end def parse_fields(fields) if fields.is_a? Array Hash[@record_type, filter_associations(fields)] elsif fields.is_a? Hash fields[@record_type] = filter_associations(fields[@record_type]) fields end end def filter_associations(fields) fields.select { |field| @associations.none? { |a| a.key == field } } end def parse_associations(has_many, belongs_to, has_one) [ *(has_many || []).map { |k| create_association(k, :has_many) }, *(belongs_to || []).map { |k| create_association(k, :belongs_to) }, *(has_one || []).map { |k| create_association(k, :has_one) } ] end def create_association(key, relationship_type) Relationship.new( key: key, relationship_type: relationship_type ) end def parse_record_type(kind, resource) return key_transform(kind) if kind return key_transform(resource.class.name) unless collection?(resource) key_transform(resource.first.class.name) end # TODO: hold reserved words # see https://google.github.io/styleguide/jsoncstyleguide.xml TOP_LEVEL_RESERVED = %w[ apiVersion context id method params data error ].freeze # Reserved fields in the Data object # kind the resource's type # fields comma separated list of attributes given # etag # items if @resource is a collection, then items is the array of resources DATA_RESERVED = %w[ kind fields etag items ].freeze end end