<commit_before># typed: strict
# frozen_string_literal: true

require "active_support/inflector"
require "hash_diff"

module ShopifyAPI
  module RestWrappers
    class Base
      extend T::Sig
      extend T::Helpers
      abstract!

      @has_one = T.let({}, T::Hash[Symbol, Class])
      @has_many = T.let({}, T::Hash[Symbol, Class])
      @paths = T.let([], T::Array[T::Hash[Symbol, T.any(T::Array[Symbol], String, Symbol)]])

      sig { returns(T::Hash[Symbol, T.untyped]) }
      attr_accessor :original_state

      sig do
        params(
          session: T.nilable(Auth::Session),
          from_hash: T.nilable(T::Hash[Symbol, T.untyped]),
        ).void
      end
      def initialize(session: nil, from_hash: nil)
        @original_state = T.let({}, T::Hash[Symbol, T.untyped])
        @forced_nils = T.let({}, T::Hash[String, T::Boolean])

        session ||= ShopifyAPI::Context.active_session

        client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

        @session = T.let(T.must(session), Auth::Session)
        @client = T.let(client, Clients::Rest::Admin)

        from_hash&.each do |key, value|
          instance_variable_set("@#{key}", value)
        end
      end

      class << self
        extend T::Sig

        sig do
          params(
            session: T.nilable(Auth::Session),
            ids: T::Hash[Symbol, String],
            params: T::Hash[Symbol, T.untyped],
          ).returns(T::Array[Base])
        end
        def base_find(session: nil, ids: {}, params: {})
          session ||= ShopifyAPI::Context.active_session

          client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

          path = T.must(get_path(http_method: :get, operation: :get, ids: ids))
          response = client.get(path: path, query: params.to_h { |k, v| [k, v.is_a?(Array) ? v.join(",") : v] }.compact)

          create_instances_from_response(response: response, session: T.must(session))
        end

        sig { returns(String) }
        def class_name
          T.must(name).demodulize.underscore
        end

        sig { returns(String) }
        def primary_key
          "id"
        end

        sig { returns(String) }
        def json_body_name
          class_name.underscore
        end

        sig { params(attribute: Symbol).returns(T::Boolean) }
        def has_many?(attribute)
          @has_many.include?(attribute)
        end

        sig { params(attribute: Symbol).returns(T::Boolean) }
        def has_one?(attribute)
          @has_one.include?(attribute)
        end

        sig do
          params(
            http_method: Symbol,
            operation: Symbol,
            entity: T.nilable(Base),
            ids: T::Hash[Symbol, T.any(Integer, String)]
          ).returns(T.nilable(String))
        end
        def get_path(http_method:, operation:, entity: nil, ids: {})
          match = T.let(nil, T.nilable(String))
          max_ids = T.let(-1, Integer)
          @paths.each do |path|
            next if http_method != path[:http_method] || operation != path[:operation]
            path_ids = T.cast(path[:ids], T::Array[Symbol])

            url_ids = ids.transform_keys(&:to_sym)
            path_ids.each do |id|
              if url_ids[id].nil? && (entity_id = entity&.public_send(id))
                url_ids[id] = entity_id
              end
            end

            url_ids.compact!

            # We haven't found all the required ids or we have a more specific match
            next if !(path_ids - url_ids.keys).empty? || path_ids.length <= max_ids

            max_ids = path_ids.length
            match = T.cast(path[:path], String).gsub(/(<([^>]+)>)/) do
              url_ids[T.unsafe(Regexp.last_match)[2].to_sym]
            end
          end

          match
        end

        sig do
          params(
            http_method: Symbol,
            operation: T.any(String, Symbol),
            session: T.nilable(Auth::Session),
            path_ids: T::Hash[Symbol, String],
            params: T::Hash[Symbol, T.untyped],
            body: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]),
            entity: T.untyped,
          ).returns(T.untyped)
        end
        def request(http_method:, operation:, session:, path_ids: {}, params: {}, body: nil, entity: nil)
          client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

          path = get_path(http_method: http_method, operation: operation.to_sym, ids: path_ids)

          case http_method
          when :get
            client.get(path: T.must(path), query: params.compact)
          when :post
            client.post(path: T.must(path), query: params.compact, body: body || {})
          when :put
            client.put(path: T.must(path), query: params.compact, body: body || {})
          when :delete
            client.delete(path: T.must(path), query: params.compact)
          end
        end

        sig { params(response: Clients::HttpResponse, session: Auth::Session).returns(T::Array[Base]) }
        def create_instances_from_response(response:, session:)
          objects = []

          body = T.cast(response.body, T::Hash[String, T.untyped])

          if body.key?(class_name)
            objects << create_instance(data: body[class_name], session: session)
          elsif body.key?(class_name.pluralize)
            body[class_name.pluralize].each do |entry|
              objects << create_instance(data: entry, session: session)<commit_msg>Make base request return HttpResponse (#146)<commit_after># typed: strict
# frozen_string_literal: true

require "active_support/inflector"
require "hash_diff"

module ShopifyAPI
  module RestWrappers
    class Base
      extend T::Sig
      extend T::Helpers
      abstract!

      @has_one = T.let({}, T::Hash[Symbol, Class])
      @has_many = T.let({}, T::Hash[Symbol, Class])
      @paths = T.let([], T::Array[T::Hash[Symbol, T.any(T::Array[Symbol], String, Symbol)]])

      sig { returns(T::Hash[Symbol, T.untyped]) }
      attr_accessor :original_state

      sig do
        params(
          session: T.nilable(Auth::Session),
          from_hash: T.nilable(T::Hash[Symbol, T.untyped]),
        ).void
      end
      def initialize(session: nil, from_hash: nil)
        @original_state = T.let({}, T::Hash[Symbol, T.untyped])
        @forced_nils = T.let({}, T::Hash[String, T::Boolean])

        session ||= ShopifyAPI::Context.active_session

        client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

        @session = T.let(T.must(session), Auth::Session)
        @client = T.let(client, Clients::Rest::Admin)

        from_hash&.each do |key, value|
          instance_variable_set("@#{key}", value)
        end
      end

      class << self
        extend T::Sig

        sig do
          params(
            session: T.nilable(Auth::Session),
            ids: T::Hash[Symbol, String],
            params: T::Hash[Symbol, T.untyped],
          ).returns(T::Array[Base])
        end
        def base_find(session: nil, ids: {}, params: {})
          session ||= ShopifyAPI::Context.active_session

          client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

          path = T.must(get_path(http_method: :get, operation: :get, ids: ids))
          response = client.get(path: path, query: params.to_h { |k, v| [k, v.is_a?(Array) ? v.join(",") : v] }.compact)

          create_instances_from_response(response: response, session: T.must(session))
        end

        sig { returns(String) }
        def class_name
          T.must(name).demodulize.underscore
        end

        sig { returns(String) }
        def primary_key
          "id"
        end

        sig { returns(String) }
        def json_body_name
          class_name.underscore
        end

        sig { params(attribute: Symbol).returns(T::Boolean) }
        def has_many?(attribute)
          @has_many.include?(attribute)
        end

        sig { params(attribute: Symbol).returns(T::Boolean) }
        def has_one?(attribute)
          @has_one.include?(attribute)
        end

        sig do
          params(
            http_method: Symbol,
            operation: Symbol,
            entity: T.nilable(Base),
            ids: T::Hash[Symbol, T.any(Integer, String)]
          ).returns(T.nilable(String))
        end
        def get_path(http_method:, operation:, entity: nil, ids: {})
          match = T.let(nil, T.nilable(String))
          max_ids = T.let(-1, Integer)
          @paths.each do |path|
            next if http_method != path[:http_method] || operation != path[:operation]
            path_ids = T.cast(path[:ids], T::Array[Symbol])

            url_ids = ids.transform_keys(&:to_sym)
            path_ids.each do |id|
              if url_ids[id].nil? && (entity_id = entity&.public_send(id))
                url_ids[id] = entity_id
              end
            end

            url_ids.compact!

            # We haven't found all the required ids or we have a more specific match
            next if !(path_ids - url_ids.keys).empty? || path_ids.length <= max_ids

            max_ids = path_ids.length
            match = T.cast(path[:path], String).gsub(/(<([^>]+)>)/) do
              url_ids[T.unsafe(Regexp.last_match)[2].to_sym]
            end
          end

          match
        end

        sig do
          params(
            http_method: Symbol,
            operation: T.any(String, Symbol),
            session: T.nilable(Auth::Session),
            path_ids: T::Hash[Symbol, String],
            params: T::Hash[Symbol, T.untyped],
            body: T.nilable(T::Hash[T.any(Symbol, String), T.untyped]),
            entity: T.untyped,
          ).returns(Clients::HttpResponse)
        end
        def request(http_method:, operation:, session:, path_ids: {}, params: {}, body: nil, entity: nil)
          client = ShopifyAPI::Clients::Rest::Admin.new(session: session)

          path = get_path(http_method: http_method, operation: operation.to_sym, ids: path_ids)

          case http_method
          when :get
            client.get(path: T.must(path), query: params.compact)
          when :post
            client.post(path: T.must(path), query: params.compact, body: body || {})
          when :put
            client.put(path: T.must(path), query: params.compact, body: body || {})
          when :delete
            client.delete(path: T.must(path), query: params.compact)
          else
            raise Errors::InvalidHttpRequestError, "Invalid HTTP method: #{http_method}"
          end
        end

        sig { params(response: Clients::HttpResponse, session: Auth::Session).returns(T::Array[Base]) }
        def create_instances_from_response(response:, session:)
          objects = []

          body = T.cast(response.body, T::Hash[String, T.untyped])

          if body.key?(class_name)
            objects << create_instance(data: body[class_name], session: session)
          elsif body.key?(class_name.pluralize)
            body[class_name.pluralize].each do |entry|
              objects << create_instance(data: entry, session: session)
{'fraction_non_alphanumeric': 0.13257132571325714, 'fraction_numerical': 0.0006300063000630006, 'mean_word_length': 2.137210615471485, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7304272', 'n_tokens_mistral': 3392, 'n_tokens_neox': 3338, 'n_words': 719}