• James Lopez's avatar
    Update specs to cope with new label types and priorities · 891e5f48
    James Lopez authored
    Fixed all related specs and also changed the logic to handle edge cases. This includes exporting and exporting of group labels, which will get associated with the new group (if any) or they will become normal project labels otherwise.
    
    Found other issues to do with not being able to import all labels at once in the beginning of the JSON - code was much simpler when we import all labels and milestones associated to a project first, then the associations will find the already created labels instead of creating them from the associations themselves.
    891e5f48
json_hash_builder.rb 4.49 KB
module Gitlab
  module ImportExport
    # Generates a hash that conforms with http://apidock.com/rails/Hash/to_json
    # and its peculiar options.
    class JsonHashBuilder
      def self.build(model_objects, attributes_finder)
        new(model_objects, attributes_finder).build
      end

      def initialize(model_objects, attributes_finder)
        @model_objects = model_objects
        @attributes_finder = attributes_finder
      end

      def build
        process_model_objects(@model_objects)
      end

      private

      # Called when the model is actually a hash containing other relations (more models)
      # Returns the config in the right format for calling +to_json+
      #
      # +model_object_hash+ - A model relationship such as:
      #   {:merge_requests=>[:merge_request_diff, :notes]}
      def process_model_objects(model_object_hash)
        json_config_hash = {}
        current_key = model_object_hash.keys.first

        model_object_hash.values.flatten.each do |model_object|
          @attributes_finder.parse(current_key) { |hash| json_config_hash[current_key] ||= hash }
          handle_model_object(current_key, model_object, json_config_hash)
        end

        json_config_hash
      end

      # Creates or adds to an existing hash an individual model or list
      #
      # +current_key+ main model that will be a key in the hash
      # +model_object+ model or list of models to include in the hash
      # +json_config_hash+ the original hash containing the root model
      def handle_model_object(current_key, model_object, json_config_hash)
        model_or_sub_model = model_object.is_a?(Hash) ? process_model_objects(model_object) : model_object

        if json_config_hash[current_key]
          add_model_value(current_key, model_or_sub_model, json_config_hash)
        else
          create_model_value(current_key, model_or_sub_model, json_config_hash)
        end
      end

      # Constructs a new hash that will hold the configuration for that particular object
      # It may include exceptions or other attribute detail configuration, parsed by +@attributes_finder+
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def create_model_value(current_key, value, json_config_hash)
        json_config_hash[current_key] = parse_hash(value) || { include: value }
      end

      # Calls attributes finder to parse the hash and add any attributes to it
      #
      # +value+ existing model to be included in the hash
      # +parsed_hash+ the original hash
      def parse_hash(value)
        return nil if already_contains_methods?(value)

        @attributes_finder.parse(value) do |hash|
          { include: hash_or_merge(value, hash) }
        end
      end

      def already_contains_methods?(value)
        value.is_a?(Hash) && value.values.detect { |val| val[:methods]}
      end

      # Adds new model configuration to an existing hash with key +current_key+
      # It may include exceptions or other attribute detail configuration, parsed by +@attributes_finder+
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def add_model_value(current_key, value, json_config_hash)
        @attributes_finder.parse(value) { |hash| value = { value => hash } }

        add_to_array(current_key, json_config_hash, value)
      end

      # Adds new model configuration to an existing hash with key +current_key+
      # it creates a new array if it was previously a single value
      #
      # +current_key+ main model that will be a key in the hash
      # +value+ existing model to be included in the hash
      # +json_config_hash+ the original hash containing the root model
      def add_to_array(current_key, json_config_hash, value)
        old_values = json_config_hash[current_key][:include]

        json_config_hash[current_key][:include] = ([old_values] + [value]).compact.flatten
      end

      # Construct a new hash or merge with an existing one a model configuration
      # This is to fulfil +to_json+ requirements.
      #
      # +hash+ hash containing configuration generated mainly from +@attributes_finder+
      # +value+ existing model to be included in the hash
      def hash_or_merge(value, hash)
        value.is_a?(Hash) ? value.merge(hash) : { value => hash }
      end
    end
  end
end