Class: RockautoApi::Client

Inherits:
Object
  • Object
show all
Includes:
Endpoints::Account, Endpoints::Fitment, Endpoints::Orders, Endpoints::PartCategories, Endpoints::PartSearch, Endpoints::Tools, Endpoints::Vehicles
Defined in:
lib/rockauto_api/client.rb

Constant Summary collapse

CATALOG_API_URL =
"https://www.rockauto.com/catalog/catalogapi.php"
PARTSEARCH_URL =
"https://www.rockauto.com/en/partsearch/"
ORDERSTATUS_URL =
"https://www.rockauto.com/orderstatus/"
BASE_URL =
"https://www.rockauto.com"
MOBILE_HEADERS =
{
  "User-Agent" => "Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1",
  "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  "Accept-Language" => "en-US,en;q=0.9",
  "Sec-Fetch-Site" => "same-origin",
  "Sec-Fetch-Mode" => "navigate",
  "Sec-Fetch-Dest" => "document"
}.freeze
DESKTOP_HEADERS =
{
  "User-Agent" => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
  "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
  "Accept-Language" => "en-US,en;q=0.9",
  "Sec-Ch-Ua" => '"Chromium";v="139", "Not;A=Brand";v="99"',
  "Sec-Fetch-Site" => "same-origin",
  "Sec-Fetch-Mode" => "navigate",
  "Sec-Fetch-Dest" => "document"
}.freeze
INITIAL_COOKIES =
{
  "idlist" => "0",
  "mkt_US" => "true",
  "mkt_CA" => "false",
  "mkt_MX" => "false",
  "year_2005" => "true",
  "ck" => "1"
}.freeze

Constants included from Endpoints::Account

Endpoints::Account::LOGIN_URL, Endpoints::Account::ORDER_HISTORY_URL, Endpoints::Account::PROFILE_URL

Constants included from Endpoints::Vehicles

Endpoints::Vehicles::BASE_CATALOG_URL

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Endpoints::Account

#add_external_order, #authenticated?, #get_account_activity, #get_order_history, #get_saved_addresses, #get_saved_vehicles, #login, #logout

Methods included from Endpoints::Orders

#lookup_order_status, #request_order_list

Methods included from Endpoints::Tools

#get_tool_categories, #get_tools_by_category

Methods included from Endpoints::Fitment

#get_fitment_for_part

Methods included from Endpoints::PartSearch

#get_manufacturers, #get_part_groups, #get_part_types, #search_parts_by_number, #what_is_part_called

Methods included from Endpoints::PartCategories

#get_part_categories, #get_parts_by_category

Methods included from Endpoints::Vehicles

#get_engines_for_vehicle, #get_makes, #get_models_for_make_year, #get_years_for_make

Constructor Details

#initialize(mobile: nil, cache: nil) ⇒ Client

Returns a new instance of Client.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rockauto_api/client.rb', line 40

def initialize(mobile: nil, cache: nil)
  mobile = RockautoApi.configuration&.default_mobile if mobile.nil?
  headers = mobile ? MOBILE_HEADERS : DESKTOP_HEADERS

  @conn = Faraday.new(url: BASE_URL) do |f|
    f.request :url_encoded
    f.use :cookie_jar
    f.adapter Faraday.default_adapter
    f.options.timeout = RockautoApi.configuration&.request_timeout || 30
    headers.each { |k, v| f.headers[k] = v }
  end

  set_initial_cookies
  @cache = cache || RockautoApi.configuration&.cache || RockautoApi::Cache.new
  @session_initialized = false
  @nck_token = nil
  @jnck_token = nil
  @authenticated = false
end

Instance Attribute Details

#authenticatedObject (readonly)

Returns the value of attribute authenticated.



38
39
40
# File 'lib/rockauto_api/client.rb', line 38

def authenticated
  @authenticated
end

#cacheObject (readonly)

Returns the value of attribute cache.



38
39
40
# File 'lib/rockauto_api/client.rb', line 38

def cache
  @cache
end

Instance Method Details

#call_catalog_api(function, payload) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/rockauto_api/client.rb', line 76

def call_catalog_api(function, payload)
  init_session!

  data = {
    "func" => function,
    "payload" => payload.is_a?(String) ? payload : payload.to_json,
    "api_json_request" => "1",
    "sctchecked" => "1",
    "scbeenloaded" => "false",
    "curCartGroupID" => ""
  }
  data["_jnck"] = @jnck_token if @jnck_token

  resp = Faraday.new(url: BASE_URL) do |f|
    f.request :url_encoded
    f.use :cookie_jar
    f.adapter Faraday.default_adapter
    f.options.timeout = RockautoApi.configuration&.request_timeout || 30
    f.headers["X-Requested-With"] = "XMLHttpRequest"
    f.headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
    f.headers["User-Agent"] = MOBILE_HEADERS["User-Agent"]
    f.headers["Referer"] = "#{BASE_URL}/"
    @conn.headers["Cookie"].to_s.split(";").each do |cookie|
      name, val = cookie.strip.split("=", 2)
      f.headers["Cookie"] = "#{f.headers['Cookie']}; #{name}=#{val}" if name && val
    end
  end.post("catalog/catalogapi.php", data)

  JSON.parse(resp.body)
rescue Faraday::Error => e
  raise NetworkError, "API request failed: #{e.message}"
end

#get(path) ⇒ Object



174
175
176
177
178
179
180
# File 'lib/rockauto_api/client.rb', line 174

def get(path)
  init_session!
  resp = @conn.get(path)
  resp.body
rescue Faraday::Error => e
  raise NetworkError, "GET #{path} failed: #{e.message}"
end

#init_session!Object



66
67
68
69
70
71
72
73
74
# File 'lib/rockauto_api/client.rb', line 66

def init_session!
  return if @session_initialized

  resp = @conn.get("/")
  @nck_token = Parsers::HtmlHelpers.extract_javascript_variable(resp.body, "_nck")
  @nck_token ||= Parsers::HtmlHelpers.extract_csrf_token(resp.body, "_nck")
  @jnck_token = @nck_token ? CGI.escape(@nck_token) : nil
  @session_initialized = true
end


140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/rockauto_api/client.rb', line 140

def navnode_fetch_payload(make:, nodetype:, label: nil, href: nil, year: nil, model: nil, carcode: nil)
  jsn = {
    "tab" => "catalog",
    "make" => make,
    "nodetype" => nodetype,
    "jsdata" => {
      "markets" => [
        {"c" => "US", "y" => "Y", "i" => "Y"},
        {"c" => "CA", "y" => "Y", "i" => "Y"},
        {"c" => "MX", "y" => "Y", "i" => "Y"}
      ],
      "mktlist" => "US,CA,MX",
      "showForMarkets" => {"US" => true, "CA" => true, "MX" => true},
      "importanceByMarket" => {"US" => "Y", "CA" => "Y", "MX" => "Y"},
      "Show" => 1
    },
    "loaded" => false,
    "expand_after_load" => true,
    "fetching" => true
  }

  jsn["year"] = year.to_s if year
  jsn["model"] = model if model
  jsn["carcode"] = carcode if carcode
  jsn["label"] = label if label
  jsn["href"] = href if href
  jsn["labelset"] = true if label || href
  jsn["jump_to_after_expand"] = true if nodetype == "make"
  jsn["dont_change_url"] = true if nodetype == "make"
  jsn["has_more_auto_open_steps"] = true if nodetype == "make"

  { "jsn" => jsn, "max_group_index" => 388 }
end

#post(path, body = nil) ⇒ Object



182
183
184
185
186
187
188
# File 'lib/rockauto_api/client.rb', line 182

def post(path, body = nil)
  init_session!
  resp = @conn.post(path, body)
  resp.body
rescue Faraday::Error => e
  raise NetworkError, "POST #{path} failed: #{e.message}"
end

#post_with_csrf(url, form_data) ⇒ Object



109
110
111
112
113
114
115
116
# File 'lib/rockauto_api/client.rb', line 109

def post_with_csrf(url, form_data)
  init_session!
  page_resp = @conn.get(url)
  nck = Parsers::HtmlHelpers.extract_csrf_token(page_resp.body)
  form_data["_nck"] = nck if nck
  resp = @conn.post(url, form_data)
  resp.body
end

#set_initial_cookiesObject



60
61
62
63
64
# File 'lib/rockauto_api/client.rb', line 60

def set_initial_cookies
  INITIAL_COOKIES.each do |name, value|
    @conn.headers["Cookie"] = "#{@conn.headers['Cookie']}; #{name}=#{value}" unless @conn.headers["Cookie"].to_s.include?("#{name}=")
  end
end

#simulate_navigation_context(make: nil, year: nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/rockauto_api/client.rb', line 118

def simulate_navigation_context(make: nil, year: nil)
  return unless make || year

  if make
    @conn.headers["Referer"] = "#{BASE_URL}/en/catalog/"
  end

  if year
    @conn.headers["Cookie"] = "#{@conn.headers['Cookie']}; year_#{year}=true"
  end

  if make
    payload = navnode_fetch_payload(
      make: make,
      nodetype: "make",
      label: make,
      href: "#{BASE_URL}/en/catalog/#{make.downcase}"
    )
    call_catalog_api("navnode_fetch", payload)
  end
end