@sickrin/openalex-sdk
Version:
A TypeScript SDK for interacting with the OpenAlex API - forked and enhanced version
242 lines (222 loc) • 8.6 kB
text/typescript
import { AxiosResponse } from 'axios';
import { Authors, GroupByAuthor } from 'src/types/author';
import { AuthorFilterParameters } from 'src/types/authorFilterParameters';
import { GroupBySource } from 'src/types/source';
import { SourceFilterParameters } from 'src/types/sourceFilterParameters';
import { GroupByTopics, TopicsFilterParameters } from 'src/types/topic';
import { GroupByWorks, SortByWork, Works } from 'src/types/work';
import { WorkFilterParameters } from 'src/types/workFilterParameters';
import { GroupByInstitution } from '../types/institution';
import { InstitutionFilterParameters } from '../types/institutionFilterParameters';
import { GET } from './http';
/**
* The function `buildUrl` builds the URL based on the search parameters.
* @param {string} baseUrl - The `baseUrl` parameter is a string that represents the base URL.
* @param {string} search - The `search` parameter is a string that represents the search query.
* @param {string} searchField - The `searchField` parameter is a string that represents the field to search in.
* @param {WorkFilterParameters | AuthorFilterParameters | SourceFilterParameters | InstitutionFilterParameters | TopicsFilterParameters} filter - The `filter` parameter is an object that represents the filter parameters.
* @param {GroupByWorks} group_by - The `group_by` parameter is a string that represents the field to group by.
* @param {SortByWork} sortBy - The `sortBy` parameter is an object that represents the sort parameters.
* @returns {string} a string that represents the URL.
*/
export function buildUrl(
baseUrl: string,
endPoints: 'works' | 'authors' | 'sources' | 'institutions' | 'topics',
search?: string,
searchField?: string,
filter?:
| WorkFilterParameters
| AuthorFilterParameters
| SourceFilterParameters
| InstitutionFilterParameters
| TopicsFilterParameters,
group_by?:
| GroupByWorks
| GroupBySource
| GroupByAuthor
| GroupByInstitution
| GroupByTopics,
sortBy?: SortByWork,
): string {
let filterParams = '';
let SearchParams = '';
let GroupByParams = '';
let SortParams = '';
if (filter) filterParams = filterBuilder(filter);
if (group_by) GroupByParams = `&group_by=${group_by}`;
if (sortBy)
SortParams =
sortBy.order === 'desc'
? `&sort=${sortBy.field}:${sortBy.order}`
: `&sort=${sortBy.field}`;
if (search && searchField)
filterParams += filter
? `,${searchField}.search:${search}`
: `${searchField}.search:${search}`;
if (search && !searchField) SearchParams = `&search=${search}`;
if (searchField || filter) filterParams = `&filter=${filterParams}`;
return `${baseUrl}/${endPoints}?${filterParams}${SearchParams}${GroupByParams}${SortParams}`;
}
function filterBuilder(
filter:
| WorkFilterParameters
| AuthorFilterParameters
| SourceFilterParameters
| InstitutionFilterParameters
| TopicsFilterParameters,
) {
let filterString = '';
const filterObject = getPaths(filter);
for (const key in filterObject) {
if (Array.isArray(filterObject[key])) {
filterString = `${filterString}${key}:${filterObject[key].join('|')},`;
} else {
filterString = `${filterString}${key}:${filterObject[key]},`;
}
}
filterString = filterString.slice(0, -1);
return filterString;
}
/**
* The function `getPaths` gets the paths of an object.
* @param {object} obj - The `obj` parameter is an object.
* @param {string[]} path - The `path` parameter is an array of strings.
* @param {object} result - The `result` parameter is an object.
* @returns an object that represents the paths.
* @example
* const obj = {
* a: {
* b: {
* c: 1,
* d: 2,
* },
* e: 3,
* };
* const paths = getPaths(obj);
* console.log(paths);
* // Output: { 'a.b.c': 1, 'a.b.d': 2, 'a.e': 3 }
*/
export function getPaths(
obj: { [x: string]: any },
path: string[] = [],
result: { [key: string]: any } = {},
) {
for (const key in obj) {
const newPath = [...path, key];
if (Array.isArray(obj[key])) {
obj[key].forEach((item: any) => {
if (typeof item === 'object' && item !== null) {
getPaths(item, newPath, result);
} else {
const joinedPath = newPath.join('.');
if (!result[joinedPath]) {
result[joinedPath] = [];
}
result[joinedPath].push(item);
}
});
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
getPaths(obj[key], newPath, result);
} else {
const joinedPath = newPath.join('.');
if (!result[joinedPath]) {
result[joinedPath] = [];
}
result[joinedPath].push(obj[key]);
}
}
return result;
}
/**
* The function `appendCursorToUrl` appends the cursor to the URL.
* @param {string} url - The `url` parameter is a string that represents the URL.
* @param {number} perPage - The `perPage` parameter is a number that represents the number of works per page.
* @param {string} cursor - The `cursor` parameter is a string that represents the cursor.
* @param {boolean} retrieveAllPages - The `retrieveAllPages` parameter is a boolean that represents whether to retrieve all pages.
* @returns {string} a string that represents the URL with the cursor appended.
*/
export function appendCursorToUrl(
url: string,
perPage?: number,
cursor?: string,
retrieveAllPages?: boolean,
): string {
url = perPage ? `${url}&per_page=${perPage}` : url;
url =
cursor && !retrieveAllPages ? `${url}&cursor=${cursor}` : `${url}&cursor=*`;
return url;
}
/**
* The function `getCursorByPage` take a page number and a URL and returns the cursor.
* @param {number} page - The `page` parameter is a number that represents the page number.
* @param {string} url - The `url` parameter is a string that represents the URL.
* @param {number} perPage - The `perPage` parameter is a number that represents the number of works per page.
* @returns a string that represents the cursor.
*/
export async function getCursorByPage(
url: string,
page: number = 1,
perPage: number = 25,
): Promise<string> {
if (page === 1) return '*';
let remainingPages = (page - 1) * perPage;
let cursorPage;
if (remainingPages <= 200) {
cursorPage = remainingPages;
remainingPages = 0;
} else {
cursorPage = 200;
remainingPages = remainingPages - 200;
}
let new_url = appendCursorToUrl(url, cursorPage, '*', false);
let response: AxiosResponse<Works | Authors> = await GET(new_url);
if (response.status !== 200) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
let cursor = response.data.meta.next_cursor;
while (remainingPages > 0) {
if (remainingPages <= 200) {
cursorPage = remainingPages;
remainingPages = 0;
} else {
cursorPage = 200;
remainingPages = remainingPages - 200;
}
new_url = appendCursorToUrl(url, cursorPage, cursor, false);
response = await GET(new_url);
if (response.status === 200) {
cursor = response.data.meta.next_cursor;
} else {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
}
return cursor;
}
/**
* The function `calculatePages` calculates the total number of pages based on the total number of works and the number of works per page.
* @param {number} pageSize - The `pageSize` parameter is a number that represents the number of works per page.
* @param {number} total - The `total` parameter is a number that represents the total number of works.
* @returns {number} a number that represents the total number of pages.
*/
export function calculatePages(pageSize: number, total: number): number {
const totalPages = Math.ceil(total / pageSize);
return totalPages;
}
/**
* The function `convertAbstractArrayToString` converts an abstract array to a string.
* @param {object} abstract - The `abstract` parameter is an object that represents the abstract array.
* @returns a string that represents the abstract array as a string.
*/
export function convertAbstractArrayToString(abstract: {
[key: string]: number[];
}): string {
const entries = Object.entries(abstract).flatMap(([key, value]) =>
value.map((v) => ({ key, value: v })),
);
// Sort the array of objects by the value property
const sortedEntries = entries.sort((a, b) => a.value - b.value);
// Extract the key from each sorted object
const keys = sortedEntries.map((entry) => entry.key);
// Join these keys into a single string separated by spaces
return keys.join(' ');
}