UNPKG

archive-search

Version:

Small, zero dependency, Promise based library to search through archive.org's api for the browser and node.

202 lines (171 loc) 6.13 kB
/* eslint-disable no-undef */ const checkType = require("../helpers/checkType"); const defaultOptions = require("../helpers/defaultOptions"); class Search { /** * Class with methods to search archive.org's api (browser version) * @constructor - initializes defaults */ constructor() { this.searchBaseUrl = "https://archive.org/advancedsearch.php"; // url from helpers/defaultOptions.js this.searchDefaults = defaultOptions; this.searchBy = "creator"; this.metaBaseUrl = "https://archive.org/metadata/"; } /** * check options object syntax, * override default search options and build new search url. * @param {array} options - Which fields to return from api. */ setOptions(options) { if (!checkType.isObject(options)) { throw new TypeError("setOptions() expected an Object"); } const possibleOptions = ["fields", "max", "sortBy", "searchBy"]; Object.keys(options).forEach((option) => { if (possibleOptions.indexOf(option) === -1) { throw new Error(`Invalid syntax for options check field: ${option}`); } }); if (!checkType.isArray(options.fields)) { throw new Error("Invalid syntax for options"); } // add identifier field as this is needed for second api call this.searchDefaults = encodeURI("&fl[]=identifier"); const { fields, sortBy, max, searchBy } = options; fields.forEach((field) => { this.searchDefaults += encodeURI(`&fl[]=${field}`); }); if (sortBy) { this.searchDefaults += this.constructor.setSortBy(sortBy); } if (max) { this.searchDefaults += `&rows=${max}&page=1`; } if (searchBy) { this.searchBy = searchBy; } // needed to get results back from api in JSON format this.searchDefaults += "&output=json"; } /** * set fields to sort results by. * @static * @param {object} sortBy - The fields to sort by and whether to sort asc or desc. * @return {string} encoded url string */ static setSortBy(sortBy) { let sortUrl = ""; Object.keys(sortBy).forEach((sort) => { sortUrl += `&sort[]=${sort}+${sortBy[sort]}`; }); return encodeURI(sortUrl); } /** * build the search url from function paramaters. * @param {string} searchBy - The type of search to make. * @param {string} searchTerm - The search term. * @return {string} url string */ constructSearchUrl(searchBy, searchTerm) { return `${this.searchBaseUrl}?q=${searchBy}%3A${searchTerm}${this.searchDefaults}`; } /** * add jsonp to api calls, appends script built from * request url to the document head * @param {string} url - The constcted search url. * @param {Function} callback - Callback to be executed when data is return from api. */ static jsonp(url, callback) { // create a unique callback name for each request const callbackName = `jsonp_callback_${Math.round(150000 * Math.random())}`; // add callback function to window object window[callbackName] = (data) => { callback(data); // cleanup after callback fires delete window[callbackName]; // eslint-disable-next-line no-use-before-define document.body.removeChild(script); }; // create script and append it to document const script = document.createElement("script"); script.src = `${url + (url.indexOf("?") >= 0 ? "&" : "?")}callback=${callbackName}`; // handle request errors script.onerror = () => { throw new Error("Bad request, check query params"); }; document.body.appendChild(script); } /** * make http GET request based on passed in url. * returns a promise * @async @static * @param {string} constructUrlFromParams - The search url to make GET request with. * @return {Promise} returns api call as a Promise */ static makeSearch(constructUrlFromParams) { return new Promise((resolve) => { this.jsonp(constructUrlFromParams, (data) => { const { numFound, docs } = data.response; // if number found is zero throw error if (numFound === 0) throw new Error("no results, please update query params"); resolve(docs); }); }); } /** * search archive.org by artist. * adds search term to url and calls makeSearch function * @param {string} searchTerm - The search term. * @return {function} returns makeSearch function which returns a Promise */ search(searchTerm, options = []) { if (!checkType.isString(searchTerm)) { throw new TypeError("search() expected a string"); } // if user passes in options object, add options to search url if (options.length !== 0) this.setOptions(options); // format search input const formatSearchTerm = searchTerm.replace(/\s+/g, "+").toLowerCase(); const constructUrlFromParams = this.constructSearchUrl(this.searchBy, formatSearchTerm); console.log("constructUrlFromParams", constructUrlFromParams); // return Promise from makeSearch() return this.constructor.makeSearch(constructUrlFromParams); } /** * pulls data from a specific collection based on identifier. * @param {string} identifier - The collection to pull data from. * @return {Promise} returns a Promise */ metaSearch(identifier) { const url = `${this.metaBaseUrl}${identifier}`; return new Promise((resolve) => { this.constructor.jsonp(url, (data) => { const { files, server, dir, metadata, reviews } = data; const responseObject = { metadata, reviews, files: [], }; Object.keys(files).forEach((key) => { const { format, name } = files[key]; if (format !== "Metadata" && format !== "JSON") { responseObject.files.push({ ...files[key], name, format, link: `https://${server}${dir}/${name.replace(/ /g, "%20")}`, }); } }); resolve(responseObject); }); }); } } const archiveSearch = new Search(); module.exports = { archiveSearch, Search, };