ManageIQ/inventory_refresh

View on GitHub
lib/inventory_refresh/inventory_collection/serialization.rb

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
require "active_support/core_ext/module/delegation"

module InventoryRefresh
  class InventoryCollection
    class Serialization
      delegate :all_manager_uuids,
               :all_manager_uuids=,
               :build,
               :targeted_scope,
               :data,
               :inventory_object_lazy?,
               :inventory_object?,
               :name,
               :skeletal_primary_index,
               :to => :inventory_collection

      attr_reader :inventory_collection

      # @param inventory_collection [InventoryRefresh::InventoryCollection] InventoryCollection object we want the storage
      #        for
      def initialize(inventory_collection)
        @inventory_collection = inventory_collection
      end

      # Loads InventoryCollection data from it's serialized form into existing InventoryCollection object
      #
      # @param inventory_objects_data [Hash] Serialized InventoryCollection as Hash
      # @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
      #        InventoryCollection objects
      def from_hash(inventory_objects_data, available_inventory_collections)
        targeted_scope.merge!(inventory_objects_data["manager_uuids"].map(&:symbolize_keys!)) if inventory_objects_data["manager_uuids"]

        (inventory_objects_data['data'] || []).each do |inventory_object_data|
          build(hash_to_data(inventory_object_data, available_inventory_collections).symbolize_keys!)
        end

        (inventory_objects_data['partial_data'] || []).each do |inventory_object_data|
          skeletal_primary_index.build(hash_to_data(inventory_object_data, available_inventory_collections).symbolize_keys!)
        end

        self.all_manager_uuids = inventory_objects_data['all_manager_uuids']
      end

      # Serializes InventoryCollection's data storage into Array of Hashes
      #
      # @return [Hash] Serialized InventoryCollection object into Hash
      def to_hash
        {
          :name              => name,
          # TODO(lsmola) we do not support nested references here, should we?
          :manager_uuids     => targeted_scope.primary_references.values.map(&:full_reference),
          :all_manager_uuids => all_manager_uuids,
          :data              => data.map { |x| data_to_hash(x.data) },
          :partial_data      => skeletal_primary_index.index_data.map { |x| data_to_hash(x.data) },
        }
      end

      private

      # Converts InventoryRefresh::InventoryObject or InventoryRefresh::InventoryObjectLazy into Hash
      #
      # @param value [InventoryRefresh::InventoryObject, InventoryRefresh::InventoryObjectLazy] InventoryObject or a lazy link
      # @param depth [Integer] Depth of nesting for nested lazy link
      # @return [Hash] Serialized InventoryRefresh::InventoryObjectLazy
      def lazy_relation_to_hash(value, depth = 0)
        {
          :type                        => "InventoryRefresh::InventoryObjectLazy",
          :inventory_collection_name   => value.inventory_collection.name,
          :reference                   => data_to_hash(value.reference.full_reference, depth + 1),
          :ref                         => value.reference.ref,
          :key                         => value.try(:key),
          :default                     => value.try(:default),
          :transform_nested_lazy_finds => value.try(:transform_nested_lazy_finds)
        }
      end

      # Converts Hash to InventoryRefresh::InventoryObjectLazy
      #
      # @param value [Hash] Serialized InventoryObject or a lazy link
      # @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
      #        InventoryCollection objects
      # @param depth [Integer] Depth of nesting for nested lazy link
      # @return [InventoryRefresh::InventoryObjectLazy] Lazy link created from hash
      def hash_to_lazy_relation(value, available_inventory_collections, depth = 0)
        inventory_collection = available_inventory_collections[value['inventory_collection_name'].try(:to_sym)]
        raise "Couldn't build lazy_link #{value} the inventory_collection_name was not found" if inventory_collection.blank?

        inventory_collection.lazy_find(
          hash_to_data(value['reference'], available_inventory_collections, depth + 1).symbolize_keys!,
          :ref                         => value['ref'].try(:to_sym),
          :key                         => value['key'].try(:to_sym),
          :default                     => value['default'],
          :transform_nested_lazy_finds => value['transform_nested_lazy_finds']
        )
      end

      # Converts Hash to attributes usable for building InventoryObject
      #
      # @param hash [Hash] Serialized InventoryObject data
      # @param available_inventory_collections [Array<InventoryRefresh::InventoryCollection>] List of available
      #        InventoryCollection objects
      # @param depth [Integer] Depth of nesting for nested lazy link
      # @return [Hash] Hash with data usable for building InventoryObject
      def hash_to_data(hash, available_inventory_collections, depth = 0)
        raise "Nested lazy_relation of #{inventory_collection} is too deep, left processing: #{hash}" if depth > 20

        hash.transform_values do |value|
          if value.kind_of?(Hash) && value['inventory_collection_name']
            hash_to_lazy_relation(value, available_inventory_collections, depth)
          elsif value.kind_of?(Array) && value.first.kind_of?(Hash) && value.first['inventory_collection_name']
            # TODO(lsmola) do we need to compact it sooner? What if first element is nil? On the other hand, we want to
            # deprecate this Vm HABTM assignment because it's not effective
            value.compact.map { |x| hash_to_lazy_relation(x, available_inventory_collections, depth) }
          else
            value
          end
        end
      end

      # Transforms data of the InventoryObject or Reference to InventoryObject into Hash
      #
      # @param data [Hash] Data of the InventoryObject or Reference to InventoryObject
      # @param depth [Integer] Depth of nesting for nested lazy link
      # @return [Hash] Serialized InventoryObject or Reference data into Hash
      def data_to_hash(data, depth = 0)
        raise "Nested lazy_relation of #{inventory_collection} is too deep, left processing: #{data}" if depth > 20

        data.transform_values do |value|
          if inventory_object_lazy?(value) || inventory_object?(value)
            lazy_relation_to_hash(value, depth)
          elsif value.kind_of?(Array) && (inventory_object_lazy?(value.compact.first) || inventory_object?(value.compact.first))
            value.compact.map { |x| lazy_relation_to_hash(x, depth) }
          else
            value
          end
        end
      end
    end
  end
end