Coverage for mindsdb / integrations / handlers / bigcommerce_handler / bigcommerce_api_client.py: 0%

134 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-21 00:36 +0000

1from http import HTTPStatus 

2from urllib.parse import urljoin 

3 

4import requests 

5 

6 

7DEFAULT_LIMIT = 999999999 

8 

9 

10class BigCommerceAPIClient: 

11 def __init__(self, url: str, access_token: str): 

12 # we have to use both endpoints: v2/ and v3/, so delete it from base url 

13 self.base_url = url.rstrip("/") 

14 self.base_url = self.base_url.rstrip("v2") 

15 self.base_url = self.base_url.rstrip("v3") 

16 if not self.base_url.endswith("/"): 

17 self.base_url += "/" 

18 

19 self.access_token = access_token 

20 self.session = requests.Session() 

21 self.session.headers.update( 

22 { 

23 "X-Auth-Token": self.access_token, 

24 "Content-Type": "application/json", 

25 "Accept": "application/json", 

26 } 

27 ) 

28 

29 def get_products( 

30 self, 

31 filter: dict = None, 

32 sort_condition: dict = None, 

33 limit: int = None, 

34 ): 

35 # doc: https://developer.bigcommerce.com/docs/rest-catalog/products#get-all-products 

36 params = { 

37 "limit": limit or DEFAULT_LIMIT, 

38 } 

39 if filter is not None: 

40 params.update(filter) 

41 if sort_condition is not None: 

42 params.update(sort_condition) 

43 return self._make_request_v3("GET", "catalog/products", params=params) 

44 

45 def get_customers( 

46 self, 

47 filter: dict = None, 

48 sort_condition: dict = None, 

49 limit: int = None, 

50 ): 

51 # doc: https://developer.bigcommerce.com/docs/rest-management/customers#get-all-customers 

52 params = { 

53 "limit": limit or DEFAULT_LIMIT, 

54 } 

55 if filter: 

56 params.update(filter) 

57 if sort_condition: 

58 params["sort"] = sort_condition 

59 return self._make_request_v3("GET", "customers", params=params) 

60 

61 def get_orders( 

62 self, 

63 filter: dict = None, 

64 sort_condition: str = None, 

65 limit: int = None, 

66 ): 

67 # doc: https://developer.bigcommerce.com/docs/rest-management/orders#get-all-orders 

68 params = {"limit": limit or DEFAULT_LIMIT} 

69 if filter: 

70 params.update(filter) 

71 if sort_condition: 

72 params["sort"] = sort_condition 

73 

74 return self._make_request_v2("GET", "orders", params=params) 

75 

76 def get_orders_count(self) -> int: 

77 response = self._make_request_v2("GET", "orders/count") 

78 return response["count"] 

79 

80 def get_customers_count(self) -> int: 

81 response = self._make_request("GET", urljoin(self.base_url, "v3/customers"), params={"limit": 1}) 

82 return response["meta"]["pagination"]["total"] 

83 

84 def get_products_count(self) -> int: 

85 response = self._make_request("GET", urljoin(self.base_url, "v3/products"), params={"limit": 1}) 

86 return response["meta"]["pagination"]["total"] 

87 

88 def get_categories( 

89 self, 

90 filter: dict = None, 

91 limit: int = None, 

92 ): 

93 # doc: https://developer.bigcommerce.com/docs/rest-catalog/category-trees/categories#get-all-categories 

94 params = { 

95 "limit": limit or DEFAULT_LIMIT, 

96 } 

97 if filter is not None: 

98 params.update(filter) 

99 return self._make_request_v3("GET", "catalog/trees/categories", params=params) 

100 

101 def get_categories_count(self) -> int: 

102 response = self._make_request("GET", urljoin(self.base_url, "v3/catalog/trees/categories"), params={"limit": 1}) 

103 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

104 

105 def get_pickups( 

106 self, 

107 filter: dict = None, 

108 limit: int = None, 

109 ): 

110 # doc: https://developer.bigcommerce.com/docs/rest-management/pickup#get-pickups 

111 params = { 

112 "limit": limit or DEFAULT_LIMIT, 

113 } 

114 if filter is not None: 

115 params.update(filter) 

116 return self._make_request_v3("GET", "orders/pickups", params=params) 

117 

118 def get_pickups_count(self) -> int: 

119 response = self._make_request("GET", urljoin(self.base_url, "v3/pickups"), params={"limit": 1}) 

120 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

121 

122 def get_promotions( 

123 self, 

124 filter: dict = None, 

125 sort_condition: dict = None, 

126 limit: int = None, 

127 ): 

128 # doc: https://developer.bigcommerce.com/docs/rest-management/promotions/promotions-bulk#get-all-promotions 

129 params = { 

130 "limit": limit or DEFAULT_LIMIT, 

131 } 

132 if filter is not None: 

133 params.update(filter) 

134 if sort_condition is not None: 

135 params.update(sort_condition) 

136 return self._make_request_v3("GET", "promotions", params=params) 

137 

138 def get_promotions_count(self) -> int: 

139 response = self._make_request("GET", urljoin(self.base_url, "v3/promotions"), params={"limit": 1}) 

140 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

141 

142 def get_wishlists( 

143 self, 

144 filter: dict = None, 

145 limit: int = None, 

146 ): 

147 # doc: https://developer.bigcommerce.com/docs/rest-management/wishlists#get-all-wishlists 

148 params = { 

149 "limit": limit or DEFAULT_LIMIT, 

150 } 

151 if filter is not None: 

152 params.update(filter) 

153 return self._make_request_v3("GET", "wishlists", params=params) 

154 

155 def get_wishlists_count(self) -> int: 

156 response = self._make_request("GET", urljoin(self.base_url, "v3/wishlists"), params={"limit": 1}) 

157 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

158 

159 def get_segments( 

160 self, 

161 filter: dict = None, 

162 limit: int = None, 

163 ): 

164 # doc: https://developer.bigcommerce.com/docs/rest-management/customer-segmentation/segments#get-all-segments 

165 params = { 

166 "limit": limit or DEFAULT_LIMIT, 

167 } 

168 if filter is not None: 

169 params.update(filter) 

170 return self._make_request_v3("GET", "segments", params=params) 

171 

172 def get_segments_count(self) -> int: 

173 response = self._make_request("GET", urljoin(self.base_url, "v3/segments"), params={"limit": 1}) 

174 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

175 

176 def get_brands( 

177 self, 

178 filter: dict = None, 

179 sort_condition: dict = None, 

180 limit: int = None, 

181 ): 

182 # doc: https://developer.bigcommerce.com/docs/rest-catalog/brands#get-all-brands 

183 params = { 

184 "limit": limit or DEFAULT_LIMIT, 

185 } 

186 if filter is not None: 

187 params.update(filter) 

188 if sort_condition is not None: 

189 params.update(sort_condition) 

190 return self._make_request_v3("GET", "catalog/brands", params=params) 

191 

192 def get_brands_count(self) -> int: 

193 response = self._make_request("GET", urljoin(self.base_url, "v3/catalog/brands"), params={"limit": 1}) 

194 return response.get("meta", {}).get("pagination", {}).get("total", 0) 

195 

196 def _make_request_v2(self, method: str, url: str, *args, **kwargs) -> list[dict]: 

197 # NOTE: v2 limit max is 250 

198 url = urljoin(urljoin(self.base_url, "v2/"), url) 

199 api_limit = 250 

200 params = kwargs.pop("params", {}) 

201 request_limit = params.get("limit", DEFAULT_LIMIT) 

202 params["limit"] = min(api_limit, request_limit) 

203 current_page = 1 

204 response_len = 1 

205 data = [] 

206 while response_len > 0 and len(data) < request_limit: 

207 params["page"] = current_page 

208 response = self._make_request(method, url, params=params, *args, **kwargs) 

209 if isinstance(response, dict): 

210 # for "get_count" requests 

211 return response 

212 current_page += 1 

213 response_len = len(response) 

214 data += response 

215 return data[:request_limit] 

216 

217 def _make_request_v3(self, method: str, url: str, *args, **kwargs) -> list[dict]: 

218 url = urljoin(urljoin(self.base_url, "v3/"), url) 

219 data = [] 

220 params = kwargs.pop("params", {}) 

221 current_page = 1 

222 total_pages = 1 

223 while current_page <= total_pages: 

224 params["page"] = current_page 

225 response = self._make_request(method, url, params=params, *args, **kwargs) 

226 current_page = response["meta"]["pagination"]["current_page"] + 1 

227 total_pages = response["meta"]["pagination"]["total_pages"] 

228 data += response["data"] 

229 return data 

230 

231 def _make_request(self, method: str, url: str, params: dict = None, data: dict = None) -> dict: 

232 response = self.session.request(method, url, params=params, json=data) 

233 

234 if response.status_code == HTTPStatus.NO_CONTENT: 

235 return [] 

236 if response.status_code != HTTPStatus.OK: 

237 raise Exception(f"Request failed with status code {response.status_code}: {response.text}") 

238 

239 return response.json()