import { URL } from 'url'; import { MiddlewareFn } from 'type-graphql'; import { RetailerKind } from '@/types'; import { makeValidURL } from '@/repositories/PageRepository/utils'; import { ContextType } from '@/types'; import { AbstractRetailerProductData } from '@/entities/RetailerProduct/AbstractRetailerProductData'; const getRetailerNameFromUrl = (url: string) => { const { host } = new URL(makeValidURL(url)); const hostArray = host.split('.'); return hostArray.slice(-2, -1)[0]; }; export const getRetailerFromUrl = (url: string): RetailerKind | string => { try { new URL(makeValidURL(url)); } catch (e) { throw new Error(`Could not get retailer from url: ${url}`); } try { return getRetailerKindFromUrl(url); } catch (e) { return getRetailerNameFromUrl(url); } }; export const getRetailerKindFromUrl = (url: string): RetailerKind => { const retailer = getRetailerNameFromUrl(url); switch (retailer) { case 'westelm': return RetailerKind.WEST_ELM; case 'luluandgeorgia': return RetailerKind.LULU_AND_GEORGIA; case 'crateandbarrel': return RetailerKind.CRATE_AND_BARREL; case 'urbanoutfitters': return RetailerKind.URBAN_OUTFITTERS; case 'potterybarn': return RetailerKind.POTTERY_BARN; case 'worldmarket': return RetailerKind.WORLD_MARKET; case 'allmodern': return RetailerKind.ALL_MODERN; case 'birchlane': return RetailerKind.BIRCH_LANE; case 'jossandmain': return RetailerKind.JOSS_AND_MAIN; case 'wayfair': return RetailerKind.WAYFAIR; case 'amazon': return RetailerKind.AMAZON; case 'ashleyfurniture': return RetailerKind.ASHLEY_FURNITURE; case 'bellacor': return RetailerKind.BELLACOR; case 'burkedecor': return RetailerKind.BURKE_DECOR; case 'englishelm': return RetailerKind.ENGLISH_ELM; case 'graysonluxury': return RetailerKind.GRAYS_ON_LUXURY; case 'highfashionhome': return RetailerKind.HIGH_FASHION_HOME; case 'houzz': return RetailerKind.HOUZZ; case 'homeshoppingmalls': return RetailerKind.HSM; case 'interiorhomescapes': return RetailerKind.INTERIOR_HOME_SCAPES; case 'jcpenney': return RetailerKind.JC_PENNY; case 'macys': return RetailerKind.MACYS; case 'neimanmarcus': return RetailerKind.NEIMAN_MARCUS; case 'onekingslane': return RetailerKind.ONE_KINGS_LANE; case '1stopbedrooms': return RetailerKind.ONE_STOP_BEDROOMS; case 'paynesgray': return RetailerKind.PAYNES_GRAY; case 'perigold': return RetailerKind.PERIGOLD; case 'pier1': return RetailerKind.PIER1_IMPORTS; case 'raymourflanigan': return RetailerKind.RAYMOUR_AND_FLANIGAN; case 'rcwilley': return RetailerKind.RC_WILLEY; case 'scoutandnimble': return RetailerKind.SCOUT_AND_NIMBLE; case 'sears': return RetailerKind.SEARS; case 'target': return RetailerKind.TARGET; case 'amara': return RetailerKind.AMARA; case 'bloomingdales': return RetailerKind.BLOOMINGDALES; case 'colemanfurniture': return RetailerKind.COLEMAN_FURNITURE; case 'dotandbo': return RetailerKind.DOT_AND_BO; case 'emmamason': return RetailerKind.EMMA_MASON; case 'kathykuohome': return RetailerKind.KATHY_KUO_HOME; case 'kohls': return RetailerKind.KOHLS; case 'laylagrayce': return RetailerKind.LAYLA_GRAYCE; case 'makerandmoss': return RetailerKind.MAKER_AND_MOSS; case 'saksfifthavenue': return RetailerKind.SAKS_FIFTH_AVENUE; case 'topmodern': return RetailerKind.TOP_MODERN; case 'walmart': return RetailerKind.WALMART; case 'overstock': return RetailerKind.OVERSTOCK; default: throw new Error( `Unrecognized url when getting retailer: ${url} (hint: retailer:${retailer})` ); } }; const PREFERRED_RETAILERS_ORDER: string[] = [ RetailerKind.WEST_ELM, RetailerKind.LULU_AND_GEORGIA, RetailerKind.CRATE_AND_BARREL, RetailerKind.URBAN_OUTFITTERS, RetailerKind.POTTERY_BARN, RetailerKind.WORLD_MARKET, RetailerKind.ALL_MODERN, RetailerKind.BIRCH_LANE, RetailerKind.JOSS_AND_MAIN, RetailerKind.WAYFAIR, ]; export const sortRetailers = (retailers: T[]): T[] => { const preferredRetailers: T[] = []; const otherRetailers: T[] = []; for (const retailer of retailers) { if (PREFERRED_RETAILERS_ORDER.includes(retailer)) { preferredRetailers.push(retailer); } else { otherRetailers.push(retailer); } } preferredRetailers.sort( (a, b) => PREFERRED_RETAILERS_ORDER.indexOf(a) - PREFERRED_RETAILERS_ORDER.indexOf(b) ); return [...preferredRetailers, ...otherRetailers]; }; /** * Filter retailer by URL domain using a whitelist. */ export const filterRetailerByUrlUsingWhitelist = ( url: string, retailerDomainWhitelistSet: Set ): boolean => { const retailerItemUrl = new URL(makeValidURL(url)); const reversedHostname = retailerItemUrl.hostname.split('.').reverse(); const retailerItemUrlSecondLevelDomain = reversedHostname[1]; return retailerDomainWhitelistSet.has(`${retailerItemUrlSecondLevelDomain}`); }; /** * Returns a middleware that filters retailers using a given whitelist. * * Only use this middleware when the field/resolver returns * an array of objects that inherit from `AbstractRetailerProductData`. */ export const getFilterRetailersUsingWhitelistMiddleware = (retailerDomainWhitelistSet: Set): MiddlewareFn => async (_, next) => { const retailers: AbstractRetailerProductData[] = await next(); return retailers.filter((retailer) => filterRetailerByUrlUsingWhitelist( retailer.url, retailerDomainWhitelistSet ) ); }; const RETAILER_DOMAIN_WHITELIST = [ '1800lighting', '1stopbedrooms', '1stoplighting', '2modern', 'abbyson', 'abodehomewares', 'abt', 'aetnafurniturestores', 'afastores', 'affordablelamps', 'afw', 'albeebaby', 'allmodern', 'alliancefurnishings', 'alpinefurniture', 'amazon', 'amberinteriordesign', 'americanhomefurniture', 'appliancesconnection', 'arearugs', 'ashleyfurniture', 'babyletto', 'bbqguys', 'beallsflorida', 'bedbathandbeyond', 'bedroomfurniturediscounts', 'beeslighting', 'belk', 'bellacor', 'bestbuy', 'bigboxfurniture', 'bigkitchen', 'birchlane', 'bisonoffice', 'bjs', 'bloomingdales', 'boscovs', 'build', 'burkedecor', 'buybuybaby', 'casagear', 'casaone', 'christmascentral', 'cityfurniture', 'classic2modern', 'colemanfurniture', 'containerstore', 'crateandbarrel', 'cymax', 'decormarket', 'decorplanet', 'decorprice', 'denvermodern', 'destinationlighting', 'dillards', 'distinctive-decor', 'dotandbo', 'dynamichomedecor', 'ebedding4you', 'emmamason', 'englishelm', 'eurway', 'food52', 'franceandson', 'furniturecart', 'furniturepick', 'futonland', 'gardeners', 'gardner-white', 'gilt', 'goinglighting', 'gordonelectricsupply', 'graciousstyle', 'graysonliving', 'graysonluxury', 'hammacher', 'havertys', 'hayneedle', 'hedgeapple', 'highfashionhome', 'homedepot', 'homegallerystores', 'homethangs', 'homethreads', 'horchow', 'houseandhold', 'houzz', 'hsn', 'incrediblerugsanddecor', 'inmod', 'interiorhomescapes', 'jacksonvillefurnituremart', 'jcpenney', 'jennifertaylorhome', 'joann', 'jossandmain', 'kardiel', 'kathykuohome', 'kbauthority', 'kirklands', 'kitchensource', 'kitsuperstore', 'kmart', 'kohls', 'lafurniturestore', 'lampsexpo', 'lampsplus', 'lampsusa', 'laylagrayce', 'lexmod', 'lightingnewyork', 'lightology', 'livingspaces', 'lordandtaylor', 'lowes', 'luluandgeorgia', 'lumens', 'lunawarehouse', 'luxedecor', 'macys', 'madisonlighting', 'maisonette', 'makerandmoss', 'markandday', 'massiano', 'mattressfirm', 'meadowblu', 'memoky', 'michaels', 'mmfurniture', 'moderndigsfurniture', 'modernnursery', 'modishstore', 'modloft', 'myknobs', 'nathanjames', 'neimanmarcus', 'newegg', 'nfoutlet', 'nordstrom', 'nordstromrack', 'northerntool', 'northlineexpress', 'nyfurnitureoutlets', 'officechairsunlimited', 'officedepot', 'officefurniture', 'officefurniture2go', 'officesupply', 'ojcommerce', 'onekingslane', 'opentip', 'overstock', 'patioliving', 'paynesgray', 'perigold', 'pier1', 'plowhearth', 'plushrugs', 'potterybarnkids', 'quill', 'qvc', 'raymourflanigan', 'rcwilley', 'recreation-outfitters', 'reedsfurniture', 'riverbendhome', 'roomstogo', 'royaldesign', 'ruelala', 'rugs', 'rugs-direct', 'rugsabound', 'rugstown', 'rugstudio', 'rugzz', 'saksfifthavenue', 'saksoff5th', 'samsclub', 'scoutandnimble', 'sears', 'sevencolonial', 'shadesoflight', 'shop', 'smartfurniture', 'sohomod', 'southforklighting', 'sportique', 'standsandmounts', 'staples', 'studiolx', 'sundialhome', 'superarearugs', 'swell', 'target', 'theclassyhome', 'thelightshop', 'thetot', 'theyamazakihome', 'topmodern', 'topographyhome', 'totallyfurniture', 'touchofclass', 'tovfurniture', 'tractorsupply', 'umbra', 'unlimitedfurnituregroup', 'urbanoutfitters', 'us-mattress', 'verishop', 'vevano', 'walmart', 'wayfair', 'westelm', 'worldmarket', 'zinhome', 'zoro', 'zulily', 'edloefinch', 'inspireq', 'zgallerie', 'homelybrand', 'usfireplacestore', 'bestnest', 'dfohome', 'nfm', 'zimlay', 'potterybarn', 'hsm', ] as const; export const RETAILER_DOMAIN_WHITELIST_SET = new Set( RETAILER_DOMAIN_WHITELIST );