@kenxirwin/alma-search
Version:
Search ExLibris's Alma API for bibliographic records
168 lines (153 loc) • 4.75 kB
JavaScript
export default class SearchBibs {
constructor(params) {
const expectedKeys = ['baseUrl', 'apiKey'];
for (const key of expectedKeys) {
if (!params.hasOwnProperty(key)) {
throw new Error(`Missing required option: ${key}`);
} else if (typeof params[key] !== 'string') {
throw new Error(
`Option ${key} must be a string; received ${typeof params[key]}`
);
} else if (params[key].trim() === '') {
throw new Error(`Option ${key} cannot be an empty string`);
} else {
this[key] = params[key].trim();
}
}
}
/*
idLookup -- use any of these Alma Ids:
'mms_id', 'holdings_id', 'ie_id', 'representation_id',
'nz_mms_id', 'cz_mms_id', 'other_system_id',
Accepts comma-separated values (no spaces) for multiple items
*/
async idLookup(searchParams = {}) {
try {
this.validateBibParams(searchParams);
} catch (error) {
throw new Error(`Validation failed: ${error.message}`);
}
const apiPath = '/almaws/v1/bibs';
const url = new URL(this.baseUrl + apiPath);
for (const [key, value] of Object.entries(searchParams)) {
if (value !== undefined && value !== null) {
url.searchParams.set(key, value);
}
}
console.log(`Request URL: ${url.toString()}`);
return await this.executeFetch(url);
}
async holdingsByMmsId(mms_id) {
const apiPath = `/almaws/v1/bibs/${mms_id}/holdings`;
const url = new URL(this.baseUrl + apiPath);
console.log(`Request URL: ${url.toString()}`);
return this.executeFetch(url);
}
// this can be used for any Alma link -- the API key will be added
async followLink(inputUrl) {
const url = new URL(inputUrl);
console.log(`Request URL: ${url.toString()}`);
return this.executeFetch(url);
}
async holdingsItemsByMmsId(mms_id, options) {
const holdingId = options.hasOwnProperty('holding_id')
? options.holding_id
: 'all';
const apiPath = `/almaws/v1/bibs/${mms_id}/holdings/${holdingId}/items`;
const queryStringParams = [
'limit',
'offset',
'expand',
'user_id',
'current_library',
'current_location',
'q',
'order_by',
'direction',
'create_date_from',
'create_date_to',
'modify_date_from',
'modify_date_to',
'receive_date_from',
'receive_date_to',
'expected_receive_date_from',
'expected_receive_date_to',
'view',
];
const url = new URL(this.baseUrl + apiPath);
queryStringParams.forEach((param) => {
if (options.hasOwnProperty(param)) {
for (const key in options) {
if (queryStringParams.includes(key)) {
url.searchParams.set(key, options[key]);
}
}
}
});
console.log(`Request URL: ${url.toString()}`);
return this.executeFetch(url);
}
validateBibParams(params) {
const expectOneOf = [
'mms_id',
'holdings_id',
'ie_id',
'representation_id',
'nz_mms_id',
'cz_mms_id',
'other_system_id',
];
let count = 0;
for (const key of expectOneOf) {
if (params.hasOwnProperty(key)) {
if (typeof params[key] !== 'string') {
throw new Error(
`Option ${key} must be a string; received ${typeof params[key]}`
);
} else if (params[key].trim() === '') {
throw new Error(`Option ${key} cannot be an empty string`);
} else {
this[key] = params[key].trim();
count++;
}
}
}
if (count === 0) {
throw new Error(
`At least one of the following options must be provided: ${expectOneOf.join(
', '
)}`
);
}
}
async barcodeLookup(barcode) {
//accept integer or string of integers
if (barcode.toString().match(/^\d+$/) === null) {
throw new Error(
`Barcode must be a string of digits; received ${barcode}`
);
}
const apiPath = '/almaws/v1/items';
const url = new URL(this.baseUrl + apiPath);
url.searchParams.set('item_barcode', barcode);
console.log(`Request URL: ${url.toString()}`);
return await this.executeFetch(url);
}
async executeFetch(url) {
url.searchParams.set('apikey', this.apiKey);
console.log(`Requesting: ${url.toString()}`);
const response = await fetch(url.toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
if (!response.ok) {
const resString = JSON.stringify(response, null, 2);
throw new Error(`Network response was not ok: (${resString})`);
}
const data = await response.json();
return data;
}
}