ikea-availability-checker
Version:
ikea product in-store availability checker and product search
154 lines (153 loc) • 7.46 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.IngkaApi = exports.BASE_URL_DEFAULT = exports.PRODUCT_AVAILABILITY = void 0;
const axios_1 = __importStar(require("axios"));
const stores_1 = require("./stores");
const ingkaErrors_1 = require("./ingkaErrors");
// clientids taken from IKEA.tld websites they may change in the future
const CLIENT_ID_US = "da465052-7912-43b2-82fa-9dc39cdccef8";
var PRODUCT_AVAILABILITY;
(function (PRODUCT_AVAILABILITY) {
PRODUCT_AVAILABILITY["HIGH_IN_STOCK"] = "HIGH_IN_STOCK";
PRODUCT_AVAILABILITY["LOW_IN_STOCK"] = "LOW_IN_STOCK";
PRODUCT_AVAILABILITY["OUT_OF_STOCK"] = "OUT_OF_STOCK";
})(PRODUCT_AVAILABILITY || (exports.PRODUCT_AVAILABILITY = PRODUCT_AVAILABILITY = {}));
exports.BASE_URL_DEFAULT = "https://api.ingka.ikea.com";
class IngkaApi {
constructor(clientId = CLIENT_ID_US, options = {}) {
this.cache = new Map();
this.client = axios_1.default.create({
timeout: 5000,
baseURL: exports.BASE_URL_DEFAULT,
headers: {
"x-client-id": clientId || CLIENT_ID_US,
accept: "application/json;version=1",
},
...options,
});
}
parseAvailabilitiesResponse(responseData) {
// the INGKA API response can contain items which don’t belong to a store
// but to general storage units or unknown things. We should filter
// here just for items that list availabilities for a specific store
// indicated by the "classUnitType" "STO".
const itemsWithStores = responseData.availabilities.filter((item) => { var _a; return ((_a = item.classUnitKey) === null || _a === void 0 ? void 0 : _a.classUnitType) === "STO"; });
// filter for Items that have a "availableStocks" property
const rawStockInfoItems = itemsWithStores
.filter((item) => item.buyingOption !== null)
.filter((item) => (0, stores_1.findOneById)(item.classUnitKey.classUnitCode));
// remove all stores which are not known
return rawStockInfoItems.map((item) => {
var _a, _b, _c;
const stockInfo = {
buCode: item.classUnitKey.classUnitCode,
productId: item.itemKey.itemNo,
stock: 0,
store: (0, stores_1.findOneById)(item.classUnitKey.classUnitCode),
};
if (item.availableForCashCarry) {
const availability = item.buyingOption.cashCarry.availability;
stockInfo.stock = parseInt(String(availability === null || availability === void 0 ? void 0 : availability.quantity), 10);
stockInfo.createdAt = new Date((_a = availability === null || availability === void 0 ? void 0 : availability.updateDateTime) !== null && _a !== void 0 ? _a : "");
stockInfo.probability = availability === null || availability === void 0 ? void 0 : availability.probability.thisDay.messageType;
}
if ((_b = item.buyingOption.cashCarry.availability) === null || _b === void 0 ? void 0 : _b.restocks) {
stockInfo.restockDate = new Date((_c = item.buyingOption.cashCarry.availability) === null || _c === void 0 ? void 0 : _c.restocks[0].earliestDate);
}
return stockInfo;
});
}
async getStoreProductAvailability(countryCode, productId, buCode) {
var _a;
const key = countryCode + productId;
if (!this.cache.has(key)) {
this.cache.set(key, this.getAvailabilities(countryCode, [productId]));
}
return (_a = this.cache.get(key)) === null || _a === void 0 ? void 0 : _a.then(arr => arr.find(item => { var _a; return ((_a = item.store) === null || _a === void 0 ? void 0 : _a.buCode) === buCode; }));
}
handleResponseError(response) {
var _a, _b, _c;
if (!((_a = response.data.errors) === null || _a === void 0 ? void 0 : _a.length))
return;
const message = ((_c = (((_b = response.data) === null || _b === void 0 ? void 0 : _b.errors) || [])[0]) === null || _c === void 0 ? void 0 : _c.message) || 'Unknown response error';
throw new ingkaErrors_1.IngkaResponseError(message, response);
}
validateResponseStructure(data) {
if (!data ||
!data.availabilities ||
!Array.isArray(data.availabilities) ||
!data.availabilities.every((obj) => typeof obj === "object")) {
throw new ingkaErrors_1.IngkaParseError(`Unexpected response data structure detected`, data);
}
return true;
}
buildAvailabilityUri(countryCode, unitType = 'ru') {
return `cia/availabilities/${unitType}/${countryCode}`;
}
async getAvailabilities(
/** a single supported country code */
countryCode,
/** one or multiple ikea article ids */
itemNos,
/** value of expand parameter */
expand = ["StoresList", "Restocks"],
/** optional additional request configuration settings */
options = {}) {
const uri = this.buildAvailabilityUri(countryCode);
options = options || {};
options.params = {
// StoresList,Restocks,SalesLocations
expand: (expand || ["StoresList", "Restocks"]).join(","),
itemNos: (Array.isArray(itemNos) ? itemNos : [itemNos]).join(","),
...(options.params || {}),
};
return this.client.get(uri, options)
.then(response => {
this.handleResponseError(response);
this.validateResponseStructure(response.data);
return this.parseAvailabilitiesResponse(response.data);
})
.catch((err) => {
var _a;
if ((0, axios_1.isAxiosError)(err)) {
const message = ((_a = err.response) === null || _a === void 0 ? void 0 : _a.data.message) || 'Unknown Response error';
throw new ingkaErrors_1.IngkaHttpError(message, err);
}
throw err;
});
}
}
exports.IngkaApi = IngkaApi;