@yuna0x0/anilist-node
Version:
A lightweight Node.js wrapper for the AniList API
105 lines (86 loc) • 3.43 kB
JavaScript
const mediaFilterType = require("../types/mediaFilterTypes");
const activityFilterType = require("../types/activityFilterTypes");
/**
* Validates the filter object for searching.
* @private
* @param {String} type The type of query that the filter would be applied to.
* @param {Object} filterObj A key/value paired filter object.
* @returns Will throw an error if the filter object is bad (ie: Non-existent key or incorrect value type).
* Returns nothing upon success.
*/
const validateFilters = function (type, filterObj) {
const filterKeys = Object.keys(filterObj);
let filterTest;
if (type === "activity") {
filterTest = activityFilterType;
}
if (type === "anime" || type === "manga") {
filterTest = mediaFilterType;
}
if (!filterTest) {
throw new Error(`Filter type not recognized by internal function. Bad type: ${type}.`);
}
for (let x = 0; x < filterKeys.length; x++) {
let myKey = filterKeys[x];
let myValue = filterObj[myKey];
let filterType = filterTest[myKey];
if (!filterType) {
throw new Error(`Filter key not recognized. Bad key: ${myKey}.`);
}
if (myValue instanceof Array) {
let typeDef = typeof filterType[0];
myValue.forEach((value) => {
if (typeof value !== typeDef) {
throw new Error(`Value (${value}) type does not match filter type in array key (${myKey})!`);
}
});
continue;
}
if (typeof myValue !== filterType) {
throw new Error(`Value (${myValue}) type does not match filter type in key (${myKey})!`);
}
}
};
/**
* Generate a query header with filter options.
* @private
* @param {String} searchType The search type to build the query with
* @param {Object} filterObj A simple key-value pair object for setting filters.
* @returns {String} Returns the query header if the filter is valid. Else, it will throw an error.
*/
const filterBuilder = function (searchType, filterObj = null) {
if (typeof filterObj !== "object") {
throw new Error(`Filter object used is of a non-object type! Expected an object but got ${filterObj} instead.`);
}
if (filterObj) {
validateFilters(searchType, filterObj);
}
let baseQuery = `query ($page: Int, $perPage: Int, $search: String) {
Page (page: $page, perPage: $perPage) { pageInfo { total currentPage lastPage hasNextPage perPage } `;
if (searchType === "activity") {
baseQuery = `query ($page: Int, $perPage: Int, $id: Int) {
Page (page: $page, perPage: $perPage) { pageInfo { total currentPage lastPage hasNextPage perPage }
activities (id: $id`;
} else if (searchType === "anime") {
baseQuery += "media (type: ANIME, search: $search";
} else if (searchType === "manga") {
baseQuery += "media (type: MANGA, search: $search";
}
if (filterObj) {
let filterKeys = Object.keys(filterObj);
for (let x = 0; x < filterKeys.length; x++) {
let filterValue = filterObj[filterKeys[x]];
// If a value is completely uppercase, we are going to assume it's an ENUM
if (filterValue instanceof Array) {
filterValue = `[${filterValue
.map((value) => typeof value === "string" && value !== value.toUpperCase() ? `"${value}"` : value)
.join(", ")}]`;
} else if (typeof filterValue === "string" && filterValue !== filterValue.toUpperCase()) {
filterValue = `"${filterValue}"`;
}
baseQuery += `, ${filterKeys[x]}: ${filterValue}`;
}
}
return `${baseQuery}) {`;
};
module.exports = filterBuilder;