diff --git a/app.js b/app.js index 4d5c09e2..a279b95a 100644 --- a/app.js +++ b/app.js @@ -3,7 +3,6 @@ const express = require('express') const esClient = require('./lib/elasticsearch/client') const loadConfig = require('./lib/load-config') const { preflightCheck } = require('./lib/preflight_check') -const { loadNyplCoreData } = require('./lib/load_nypl_core') const swaggerDocs = require('./swagger.v1.1.x.json') @@ -21,7 +20,7 @@ app.set('trust proxy', 'loopback') app.init = async () => { await loadConfig.loadConfig() - await loadNyplCoreData() + preflightCheck() // Load logger after running above to ensure we respect LOG_LEVEL if set diff --git a/config/production.env b/config/production.env index 31427a5e..5374f99a 100644 --- a/config/production.env +++ b/config/production.env @@ -10,7 +10,7 @@ NYPL_OAUTH_URL=https://isso.nypl.org/ ENCRYPTED_NYPL_OAUTH_ID=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAGswaQYJKoZIhvcNAQcGoFwwWgIBADBVBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDMLKVUQA58B6vprNcAIBEIAoaz0lI9EL2M9NyTuEwT8JDmPBt6aXfMiFs027DEuwsCN0wS0qWeFL1g== ENCRYPTED_NYPL_OAUTH_SECRET=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAIcwgYQGCSqGSIb3DQEHBqB3MHUCAQAwcAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyWz91LOP2YP5fg0q0CARCAQ9inO9SV1M8R0Pkkx84r7UdwlU1FxfXvIjk/z6Qs81KBAVELhby2iD5LawQyDrR9tjhuMbotS6QnydwwMR/p8+qJXHI= -NYPL_CORE_VERSION=v2.23 +NYPL_CORE_VERSION=v2.22 LOG_LEVEL=info FEATURES=on-site-edd diff --git a/config/qa.env b/config/qa.env index f27a0361..186c4579 100644 --- a/config/qa.env +++ b/config/qa.env @@ -10,7 +10,7 @@ NYPL_OAUTH_URL=https://isso.nypl.org/ ENCRYPTED_NYPL_OAUTH_ID=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAGswaQYJKoZIhvcNAQcGoFwwWgIBADBVBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDMLKVUQA58B6vprNcAIBEIAoaz0lI9EL2M9NyTuEwT8JDmPBt6aXfMiFs027DEuwsCN0wS0qWeFL1g== ENCRYPTED_NYPL_OAUTH_SECRET=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAIcwgYQGCSqGSIb3DQEHBqB3MHUCAQAwcAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyWz91LOP2YP5fg0q0CARCAQ9inO9SV1M8R0Pkkx84r7UdwlU1FxfXvIjk/z6Qs81KBAVELhby2iD5LawQyDrR9tjhuMbotS6QnydwwMR/p8+qJXHI= -NYPL_CORE_VERSION=v2.24 +NYPL_CORE_VERSION=v2.22 LOG_LEVEL=info FEATURES=on-site-edd diff --git a/config/test.env b/config/test.env index 9ab73b92..f3f39d0b 100644 --- a/config/test.env +++ b/config/test.env @@ -9,7 +9,7 @@ NYPL_OAUTH_URL=http://oauth.example.com ENCRYPTED_NYPL_OAUTH_ID=encrypted-nypl-oauth-id ENCRYPTED_NYPL_OAUTH_SECRET=encrypted-nypl-oauth-id -NYPL_CORE_VERSION=v2.23 +NYPL_CORE_VERSION=v2.21 LOG_LEVEL=error FEATURES=on-site-edd diff --git a/lib/available_delivery_location_types.js b/lib/available_delivery_location_types.js index 43183395..da9c4c3f 100644 --- a/lib/available_delivery_location_types.js +++ b/lib/available_delivery_location_types.js @@ -1,18 +1,18 @@ const logger = require('./logger') const { makeNyplDataApiClient } = require('./data-api-client') -const nyplCore = require('./load_nypl_core') class AvailableDeliveryLocationTypes { static getScholarRoomByPatronId (patronID) { // If patronID is falsy (i.e. patron is not logged in) they're just a Rearcher: if (!patronID) return Promise.resolve(['Research']) + const patronTypeMapping = require('@nypl/nypl-core-objects')('by-patron-type') return this._getPatronTypeOf(patronID) .then((patronType) => { - if (this._isUnfamiliarPatronType(patronType)) { + if (this._isUnfamiliarPatronType(patronTypeMapping, patronType)) { return } - const patronTypeData = nyplCore.patronTypes()[patronType] + const patronTypeData = patronTypeMapping[patronType] return patronTypeData.scholarRoom && patronTypeData.scholarRoom.code }) } @@ -38,8 +38,8 @@ class AvailableDeliveryLocationTypes { }) } - static _isUnfamiliarPatronType (patronType) { - if (!nyplCore.patronTypes()[patronType]) { + static _isUnfamiliarPatronType (patronTypeMapping, patronType) { + if (!patronTypeMapping[patronType]) { logger.info(`Found the Patron Type: ${patronType} is not recognizable.`) return true } else { @@ -48,4 +48,8 @@ class AvailableDeliveryLocationTypes { } } +const patronTypeMapping = require('@nypl/nypl-core-objects')('by-patron-type') + +AvailableDeliveryLocationTypes.patronTypeMapping = patronTypeMapping + module.exports = AvailableDeliveryLocationTypes diff --git a/lib/delivery-locations-resolver.js b/lib/delivery-locations-resolver.js index eafc84e5..86ac983d 100644 --- a/lib/delivery-locations-resolver.js +++ b/lib/delivery-locations-resolver.js @@ -1,18 +1,18 @@ const { itemHasRecapHoldingLocation, barcodeFromItem } = require('./util') const scsbClient = require('./scsb-client') -const nyplCore = require('./load_nypl_core') - +const recapCustomerCodes = require('@nypl/nypl-core-objects')('by-recap-customer-code') +const sierraLocations = require('@nypl/nypl-core-objects')('by-sierra-location') const logger = require('./logger') const onsiteEddCriteria = require('../data/onsite-edd-criteria.json') const { isItemNyplOwned } = require('./ownership_determination') class DeliveryLocationsResolver { static nyplCoreLocation (locationCode) { - return nyplCore.sierraLocations()[locationCode] + return sierraLocations[locationCode] } static requestableBasedOnHoldingLocation (item) { - const locationCode = DeliveryLocationsResolver.extractLocationCode(item) + const locationCode = this.extractLocationCode(item) if (!DeliveryLocationsResolver.nyplCoreLocation(locationCode)) { logger.warn(`DeliveryLocationsResolver: Unrecognized holdingLocation for ${item.uri}: ${locationCode}`) @@ -50,15 +50,21 @@ class DeliveryLocationsResolver { // Fetch Sierra delivery locations by recap code static deliveryLocationsByRecapCustomerCode (customerCode) { - if (nyplCore.recapCustomerCodes()[customerCode] && nyplCore.recapCustomerCodes()[customerCode].sierraDeliveryLocations) { - return nyplCore.recapCustomerCodes()[customerCode].sierraDeliveryLocations + if (recapCustomerCodes[customerCode] && recapCustomerCodes[customerCode].sierraDeliveryLocations) { + return recapCustomerCodes[customerCode].sierraDeliveryLocations } } // Fetch Sierra delivery locations by m2 customer code. Returns undefined if the m2 customer code is not requestable: static deliveryLocationsByM2CustomerCode (customerCode) { - if (nyplCore.m2CustomerCodes()?.[customerCode]?.sierraDeliveryLocations) { - const { sierraDeliveryLocations, requestable } = nyplCore.m2CustomerCodes()[customerCode] + let m2CustomerCodes + try { + m2CustomerCodes = require('@nypl/nypl-core-objects')('by-m2-customer-code') + } catch (e) { + + } + if (m2CustomerCodes && m2CustomerCodes[customerCode] && m2CustomerCodes[customerCode].sierraDeliveryLocations) { + const { sierraDeliveryLocations, requestable } = m2CustomerCodes[customerCode] if (requestable) { return sierraDeliveryLocations } else return undefined @@ -67,7 +73,7 @@ class DeliveryLocationsResolver { // Determine eddRequestable by recap customer code: static __eddRequestableByCustomerCode (customerCode) { - if (nyplCore.recapCustomerCodes()[customerCode]) return Boolean(nyplCore.recapCustomerCodes()[customerCode].eddRequestable) + if (recapCustomerCodes[customerCode]) return Boolean(recapCustomerCodes[customerCode].eddRequestable) } // Determine eddRequestable by on-site EDD requestability criteria (presumed on-site): @@ -166,7 +172,7 @@ class DeliveryLocationsResolver { return { id: `loc:${location.code}`, label: location.label, - sortPosition: DeliveryLocationsResolver.sortPosition(location) + sortPosition: this.sortPosition(location) } }) // Either way, sort deliveryLocation entries by name: @@ -191,14 +197,14 @@ class DeliveryLocationsResolver { } static attachRecapDeliveryInfo (item) { - const info = DeliveryLocationsResolver.getRecapDeliveryInfo(item) + const info = this.getRecapDeliveryInfo(item) item.eddRequestable = info.eddRequestable item.deliveryLocation = info.deliveryLocation return item } static attachOnsiteDeliveryInfo (item) { - const info = DeliveryLocationsResolver.getOnsiteDeliveryInfo(item) + const info = this.getOnsiteDeliveryInfo(item) item.eddRequestable = info.eddRequestable item.deliveryLocation = info.deliveryLocation return item @@ -212,15 +218,15 @@ class DeliveryLocationsResolver { const hasRecapCustomerCode = item.recapCustomerCode && item.recapCustomerCode[0] const nyplItem = isItemNyplOwned(item) if (!hasRecapCustomerCode) { - const requestableBasedOnHoldingLocation = nyplItem ? DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item) : true + const requestableBasedOnHoldingLocation = nyplItem ? this.requestableBasedOnHoldingLocation(item) : true // the length of the list of delivery locations is checked later to determine physical requestability // In case of an offsite item with no recap customer code, we want this to be based on holding location // so we put a placeholder '' in case it is requestable based on holding location deliveryLocation = requestableBasedOnHoldingLocation ? [''] : [] eddRequestable = requestableBasedOnHoldingLocation - } else if (!nyplItem || DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item)) { - deliveryLocation = DeliveryLocationsResolver.deliveryLocationsByRecapCustomerCode(item.recapCustomerCode[0]) - eddRequestable = DeliveryLocationsResolver.__eddRequestableByCustomerCode(item.recapCustomerCode[0]) + } else if (!nyplItem || this.requestableBasedOnHoldingLocation(item)) { + deliveryLocation = this.deliveryLocationsByRecapCustomerCode(item.recapCustomerCode[0]) + eddRequestable = this.__eddRequestableByCustomerCode(item.recapCustomerCode[0]) } else { deliveryLocation = [] eddRequestable = false @@ -233,7 +239,7 @@ class DeliveryLocationsResolver { eddRequestable: false, deliveryLocation: [] } - const holdingLocationCode = DeliveryLocationsResolver.extractLocationCode(item) + const holdingLocationCode = this.extractLocationCode(item) const sierraData = DeliveryLocationsResolver.nyplCoreLocation(holdingLocationCode) if (!sierraData) { // This case is mainly to satisfy a test which wants eddRequestable = false @@ -243,15 +249,15 @@ class DeliveryLocationsResolver { } // if nypl core says it's unrequestable, it can still be eddRequestable, // but its definitely not phys requestable. - deliveryInfo.eddRequestable = DeliveryLocationsResolver.eddRequestableByOnSiteCriteria(item) - if (!DeliveryLocationsResolver.requestableBasedOnHoldingLocation(item)) { + deliveryInfo.eddRequestable = this.eddRequestableByOnSiteCriteria(item) + if (!this.requestableBasedOnHoldingLocation(item)) { return deliveryInfo } // if nypl-core reports that a holding location's delivery locations // should be found by M2 code, but only if the item has an M2 customer code const deliverableToResolution = sierraData.deliverableToResolution if (deliverableToResolution === 'm2-customer-code' && item.m2CustomerCode && item.m2CustomerCode[0]) { - deliveryInfo.deliveryLocation = DeliveryLocationsResolver.deliveryLocationsByM2CustomerCode(item.m2CustomerCode[0]) + deliveryInfo.deliveryLocation = this.deliveryLocationsByM2CustomerCode(item.m2CustomerCode[0]) } // if no value, default to sierra location lookup if (!deliverableToResolution) { @@ -275,15 +281,15 @@ class DeliveryLocationsResolver { } /** - * Given an array of items (ES hits), returns the same items with `eddRequestable` & `deliveryLocations`. We verify all recap customer codes because our indexed data may be stale. + * Given an array of items (ES hits), returns the same items with `eddRequestable` & `deliveryLocations` * * @return Promise> A Promise that resolves and array of items, modified to include `eddRequestable` & `deliveryLocations` */ static attachDeliveryLocationsAndEddRequestability (items, scholarRoom) { // Extract ReCAP barcodes from items: - const recapBarcodes = DeliveryLocationsResolver.extractRecapBarcodes(items) + const recapBarcodes = this.extractRecapBarcodes(items) // Get a map from barcodes to ReCAP customercodes: - return DeliveryLocationsResolver.__recapCustomerCodesByBarcodes(recapBarcodes) + return this.__recapCustomerCodesByBarcodes(recapBarcodes) .then((barcodeToRecapCustomerCode) => { // Now map over items to affix deliveryLocations: return items.map((item) => { @@ -292,15 +298,15 @@ class DeliveryLocationsResolver { item.recapCustomerCode = [barcodeToRecapCustomerCode[barcode]] // If recap has a customer code for this barcode, map it by recap cust code: if (item.recapCustomerCode[0]) { - item = DeliveryLocationsResolver.attachRecapDeliveryInfo(item) + item = this.attachRecapDeliveryInfo(item) // Otherwise, it's an onsite item } else { - item = DeliveryLocationsResolver.attachOnsiteDeliveryInfo(item) + item = this.attachOnsiteDeliveryInfo(item) } // Establish default for Electronic Document Delivery flag: item.eddRequestable = !!item.eddRequestable - const filteredDeliveryLocationsWithScholarRoom = DeliveryLocationsResolver.filterLocations(item.deliveryLocation, scholarRoom) - item.deliveryLocation = DeliveryLocationsResolver.formatLocations(filteredDeliveryLocationsWithScholarRoom) + const filteredDeliveryLocationsWithScholarRoom = this.filterLocations(item.deliveryLocation, scholarRoom) + item.deliveryLocation = this.formatLocations(filteredDeliveryLocationsWithScholarRoom) return item }) }) diff --git a/lib/jsonld_serializers.js b/lib/jsonld_serializers.js index 026d8f1e..73ab8c85 100644 --- a/lib/jsonld_serializers.js +++ b/lib/jsonld_serializers.js @@ -1,6 +1,7 @@ 'use strict' -const nyplCore = require('./load_nypl_core') +const locations = require('@nypl/nypl-core-objects')('by-sierra-location') +const recordTypes = require('@nypl/nypl-core-objects')('by-record-types') const NyplSourceMapper = require('research-catalog-indexer/lib/utils/nypl-source-mapper') const util = require('./util.js') @@ -286,7 +287,7 @@ class ResourceSerializer extends JsonLdItemSerializer { } ResourceSerializer.getFormattedRecordType = function (recordTypeId) { - const prefLabel = nyplCore.recordTypes()[recordTypeId]?.label + const prefLabel = recordTypes[recordTypeId]?.label if (!prefLabel) return null return { '@id': recordTypeId, @@ -498,10 +499,10 @@ class AggregationSerializer extends JsonLdItemSerializer { v.label = p[1] } else if (field === 'buildingLocation') { // Build buildingLocation agg labels from nypl-core: - v.label = nyplCore.sierraLocations()[v.value]?.label + v.label = locations[v.value]?.label } else if (field === 'recordType') { // Build recordType agg labels from nypl-core: - v.label = nyplCore.recordTypes()[v.value]?.label + v.label = recordTypes[v.value]?.label // Unknown recordType? Remove it: if (!v.label) return null } else { diff --git a/lib/load_nypl_core.js b/lib/load_nypl_core.js deleted file mode 100644 index 1206bde4..00000000 --- a/lib/load_nypl_core.js +++ /dev/null @@ -1,25 +0,0 @@ -const _data = {} -const nyplCoreObjects = require('@nypl/nypl-core-objects') - -const loadNyplCoreData = () => { - const vocabularies = { - sierraLocations: 'by-sierra-location', - recordTypes: 'by-record-types', - recapCustomerCodes: 'by-recap-customer-code', - m2CustomerCodes: 'by-m2-customer-code', - patronTypes: 'by-patron-type' - } - return Promise.all(Object.keys(vocabularies).map(async (vocab) => { - const nyplCoreValues = await nyplCoreObjects(vocabularies[vocab]) - _data[vocab] = nyplCoreValues - })) -} - -module.exports = { - loadNyplCoreData, - patronTypes: () => _data.patronTypes || {}, - sierraLocations: () => _data.sierraLocations || {}, - recapCustomerCodes: () => _data.recapCustomerCodes || {}, - recordTypes: () => _data.recordTypes || {}, - m2CustomerCodes: () => _data.m2CustomerCodes || {} -} diff --git a/lib/location_label_updater.js b/lib/location_label_updater.js index 6a07e216..c23f787a 100644 --- a/lib/location_label_updater.js +++ b/lib/location_label_updater.js @@ -1,4 +1,4 @@ -const nyplCore = require('./load_nypl_core') +const sierraLocations = require('@nypl/nypl-core-objects')('by-sierra-location') class LocationLabelUpdater { constructor (responseReceived) { @@ -10,22 +10,20 @@ class LocationLabelUpdater { const resp = this.elasticSearchResponse const updatedHits = resp.hits.hits.map((bib) => { // Update locations for items: - const items = bib._source.items || [] - items.forEach((item) => { + ; (bib._source.items || []).forEach((item) => { if (item.holdingLocation && item.holdingLocation.length > 0) { item.holdingLocation = item.holdingLocation.map((loc) => { - const nyplCoreEntry = nyplCore.sierraLocations()[loc.id.replace(/^loc:/, '')] + const nyplCoreEntry = sierraLocations[loc.id.replace(/^loc:/, '')] if (nyplCoreEntry) loc.label = nyplCoreEntry.label return loc }) } }) // Update locations for holdings: - const holdings = bib._source.holdings || [] - holdings.forEach((holding) => { + ; (bib._source.holdings || []).forEach((holding) => { if (holding.location && holding.location.length > 0) { holding.location = holding.location.map((loc) => { - const nyplCoreEntry = nyplCore.sierraLocations()[loc.code.replace(/^loc:/, '')] + const nyplCoreEntry = sierraLocations[loc.code.replace(/^loc:/, '')] if (nyplCoreEntry) loc.label = nyplCoreEntry.label return loc }) diff --git a/lib/requestability_resolver.js b/lib/requestability_resolver.js index cb3b8b82..0b192d6c 100644 --- a/lib/requestability_resolver.js +++ b/lib/requestability_resolver.js @@ -2,42 +2,39 @@ const DeliveryLocationsResolver = require('./delivery-locations-resolver') const { isItemNyplOwned } = require('./ownership_determination') const { isInRecap } = require('./util') const logger = require('./logger') - class RequestabilityResolver { static fixItemRequestability (elasticSearchResponse) { elasticSearchResponse.hits.hits .forEach((hit) => { - const parentBibHasFindingAid = !!hit._source.supplementaryContent?.find((el) => el.label.toLowerCase() === 'finding aid') hit._source.items = hit._source.items.map((item) => { if (item.electronicLocator) return item let deliveryInfo const itemIsInRecap = isInRecap(item) let physRequestableCriteria - const hasRecapCustomerCode = item.recapCustomerCode?.[0] + const hasRecapCustomerCode = item.recapCustomerCode && item.recapCustomerCode[0] if (itemIsInRecap) { // recap items missing codes should default to true for phys and edd // requestable, unless it has a non-requestable holding location deliveryInfo = DeliveryLocationsResolver.getRecapDeliveryInfo(item) physRequestableCriteria = hasRecapCustomerCode - ? `${(deliveryInfo.deliveryLocation?.length) || 0} delivery locations.` + ? `${(deliveryInfo.deliveryLocation && + deliveryInfo.deliveryLocation.length) || 0} delivery locations.` : 'Missing customer code' } else if (!itemIsInRecap) { deliveryInfo = DeliveryLocationsResolver.getOnsiteDeliveryInfo(item) - physRequestableCriteria = `${(deliveryInfo.deliveryLocation?.length) || 0} delivery locations.` + physRequestableCriteria = `${(deliveryInfo.deliveryLocation && + deliveryInfo.deliveryLocation.length) || 0} delivery locations.` } - item.specRequestable = this.buildSpecRequestable(item, parentBibHasFindingAid) - item.physRequestable = !!deliveryInfo.deliveryLocation?.length - item.eddRequestable = !!deliveryInfo.eddRequestable && !item.specRequestable + item.eddRequestable = !!deliveryInfo.eddRequestable + item.physRequestable = !!(deliveryInfo.deliveryLocation && + deliveryInfo.deliveryLocation.length) + item.specRequestable = !!item.aeonUrl // items without barcodes should not be requestable const hasBarcode = (item.identifier || []).some((identifier) => /^(urn|bf):[bB]arcode:\w+/.test(identifier)) if (isItemNyplOwned(item) && !hasBarcode) { physRequestableCriteria = 'NYPL item missing barcode' item.physRequestable = false } - if (item.specRequestable && item.physRequestable) { - item.physRequestable = false - physRequestableCriteria = 'specRequestable overrides physRequestable location' - } logger.debug(`item ${item.uri}: `, { physRequestable: item.physRequestable, physRequestableCriteria }) item.requestable = [item.eddRequestable || item.physRequestable || item.specRequestable] return item @@ -45,13 +42,6 @@ class RequestabilityResolver { }) return elasticSearchResponse } - - static buildSpecRequestable (item, parentBibHasFindingAid) { - const holdingLocation = DeliveryLocationsResolver.extractLocationCode(item) - const nyplCoreLocation = DeliveryLocationsResolver.nyplCoreLocation(holdingLocation) - const isSpecialCollectionsOnlyAccessType = !!(nyplCoreLocation?.collectionAccessType === 'special') - return !!item.aeonUrl || parentBibHasFindingAid || isSpecialCollectionsOnlyAccessType - } } module.exports = RequestabilityResolver diff --git a/package-lock.json b/package-lock.json index d91e7e20..95ca6716 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-kms": "^3.533.0", "@elastic/elasticsearch": "~8.12.0", - "@nypl/nypl-core-objects": "3.0.3", + "@nypl/nypl-core-objects": "2.3.2", "@nypl/nypl-data-api-client": "^2.0.0", "@nypl/scsb-rest-client": "3.0.0", "dotenv": "^16.4.5", @@ -4922,44 +4922,87 @@ } }, "node_modules/@nypl/nypl-core-objects": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-3.0.3.tgz", - "integrity": "sha512-tnqqCRsaydRQACaBf5IFMiKVEMKwevWVsPrwnbOsMkGqmIrM023d4T+NILfeOEYD74P8R8StwYCThuEt7G+8rw==", + "version": "2.3.2", + "license": "MIT", "dependencies": { - "axios": "^1.6.8", "csv": "^5.3.2", "csv-stringify": "^5.6.0", - "just-flatten": "^1.0.0" + "just-flatten": "^1.0.0", + "sync-request": "^6.1.0" } }, - "node_modules/@nypl/nypl-core-objects/node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } + "node_modules/@nypl/nypl-core-objects/node_modules/caseless": { + "version": "0.12.0", + "license": "Apache-2.0" }, "node_modules/@nypl/nypl-core-objects/node_modules/csv-stringify": { "version": "5.6.5", "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" }, - "node_modules/@nypl/nypl-core-objects/node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "node_modules/@nypl/nypl-core-objects/node_modules/http-basic": { + "version": "8.1.3", + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" }, "engines": { - "node": ">= 6" + "node": ">=6.0.0" + } + }, + "node_modules/@nypl/nypl-core-objects/node_modules/http-response-object": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/@nypl/nypl-core-objects/node_modules/promise": { + "version": "8.3.0", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/@nypl/nypl-core-objects/node_modules/sync-request": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nypl/nypl-core-objects/node_modules/then-request": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" } }, + "node_modules/@nypl/nypl-core-objects/node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "license": "MIT" + }, "node_modules/@nypl/nypl-data-api-client": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@nypl/nypl-data-api-client/-/nypl-data-api-client-2.0.0.tgz", @@ -4979,6 +5022,46 @@ "nypl-data-api": "bin/nypl-data-api.js" } }, + "node_modules/@nypl/nypl-data-api-client/node_modules/@nypl/nypl-core-objects": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-3.0.0.tgz", + "integrity": "sha512-chFXev9uMpL0mU+y5BTvC2/4mOwhXNicTVfIqnasAqNWbOClEHhS/fIPod/q1TjdkD5jqbTlMVxsW/JjE3UjFg==", + "dependencies": { + "axios": "^1.6.8", + "csv": "^5.3.2", + "csv-stringify": "^5.6.0", + "just-flatten": "^1.0.0" + } + }, + "node_modules/@nypl/nypl-data-api-client/node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@nypl/nypl-data-api-client/node_modules/csv-stringify": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", + "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" + }, + "node_modules/@nypl/nypl-data-api-client/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "BSD-2-Clause", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@nypl/nypl-streams-client": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@nypl/nypl-streams-client/-/nypl-streams-client-2.0.0.tgz", @@ -8210,9 +8293,7 @@ }, "node_modules/form-data": { "version": "2.3.3", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -11236,17 +11317,6 @@ "node": ">=10" } }, - "node_modules/research-catalog-indexer/node_modules/@nypl/nypl-core-objects": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-2.3.2.tgz", - "integrity": "sha512-5IRW6y/kv6Oh82nnLaSDMjJj3rKSG+YhVum5LsY/2Heoq3Jve+p31Et9DvymgqaZNldSADRTR2GPwdfovOiCVg==", - "dependencies": { - "csv": "^5.3.2", - "csv-stringify": "^5.6.0", - "just-flatten": "^1.0.0", - "sync-request": "^6.1.0" - } - }, "node_modules/research-catalog-indexer/node_modules/@nypl/scsb-rest-client": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@nypl/scsb-rest-client/-/scsb-rest-client-2.0.0.tgz", @@ -11280,11 +11350,6 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "node_modules/research-catalog-indexer/node_modules/csv-stringify": { - "version": "5.6.5", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz", - "integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==" - }, "node_modules/research-catalog-indexer/node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", diff --git a/package.json b/package.json index 719ba346..e83f91bf 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies": { "@aws-sdk/client-kms": "^3.533.0", "@elastic/elasticsearch": "~8.12.0", - "@nypl/nypl-core-objects": "3.0.3", + "@nypl/nypl-core-objects": "2.3.2", "@nypl/nypl-data-api-client": "^2.0.0", "@nypl/scsb-rest-client": "3.0.0", "dotenv": "^16.4.5", @@ -65,4 +65,4 @@ "type": "git", "url": "https://github.com/NYPL/discovery-api.git" } -} \ No newline at end of file +} diff --git a/routes/resources.js b/routes/resources.js index 9da2309b..51983eef 100644 --- a/routes/resources.js +++ b/routes/resources.js @@ -100,6 +100,7 @@ module.exports = function (app) { * e.g. discovery/resources/b1234 */ app.get(`/api/v${VER}/discovery/resources/:uri.:ext?`, function (req, res) { + console.log('heeeere') const params = Object.assign({}, req.query, { uri: req.params.uri }) if (Number.isInteger(parseInt(req.query.items_size))) params.items_size = req.query.items_size diff --git a/test/available_delivery_location_types.test.js b/test/available_delivery_location_types.test.js index 89c1f349..00787530 100644 --- a/test/available_delivery_location_types.test.js +++ b/test/available_delivery_location_types.test.js @@ -1,7 +1,8 @@ const fixtures = require('./fixtures') -const AvailableDeliveryLocationTypes = require('../lib/available_delivery_location_types.js') describe('AvailableDeliveryLocationTypes', function () { + const AvailableDeliveryLocationTypes = require('../lib/available_delivery_location_types.js') + before(function () { // Reroute these (and only these) api paths to local fixtures: fixtures.enableDataApiFixtures({ diff --git a/test/fixtures/specRequestable/specRequestable-es-response.js b/test/fixtures/specRequestable-es-response.js similarity index 99% rename from test/fixtures/specRequestable/specRequestable-es-response.js rename to test/fixtures/specRequestable-es-response.js index f86ef6dc..e2133d07 100644 --- a/test/fixtures/specRequestable/specRequestable-es-response.js +++ b/test/fixtures/specRequestable-es-response.js @@ -174,7 +174,7 @@ module.exports = () => { holdingLocation: [ { label: 'Schomburg Center - Research & Reference', - id: 'loc:mao82' + id: 'loc:scff2' } ], status_packed: [ diff --git a/test/fixtures/specRequestable/findingAid-es-response.js b/test/fixtures/specRequestable/findingAid-es-response.js deleted file mode 100644 index 7de9c342..00000000 --- a/test/fixtures/specRequestable/findingAid-es-response.js +++ /dev/null @@ -1,152 +0,0 @@ -module.exports = () => { - return { - _shards: { - failed: 0, - successful: 1, - total: 1 - }, - took: 1, - hits: { - total: 1, - max_score: 1.3862944, - hits: [ - { - _id: 'b10980129', - _source: { - supplementaryContent: [{ - label: 'Finding aid', - url: 'spaghetti.com' - }], - items: [ - { - holdingLocation: [ - { - label: 'OFFSITE - Request in Advance', - id: 'loc:rc2ma' - } - ], - status_packed: [ - 'status:na||Not Available' - ], - owner: [ - { - id: 'orgs:1000', - label: 'Stephen A. Schwarzman Building' - } - ], - deliveryLocation: [ - { - id: 'loc:mala', - label: 'SASB - Allen Scholar Room' - } - ], - deliveryLocation_packed: [ - 'loc:mala||SASB - Allen Scholar Room' - ], - uri: 'i10283664', - accessMessage_packed: [ - 'accessMessage:2||ADV REQUEST' - ], - accessMessage: [ - { - id: 'accessMessage:2', - label: 'ADV REQUEST' - } - ], - status: [ - { - id: 'status:na', - label: 'Not available' - } - ], - owner_packed: [ - 'orgs:1000||Stephen A. Schwarzman Building' - ], - requestable: [ - false - ], - identifier: [ - 'urn:barcode:1000546836' - ], - holdingLocation_packed: [ - 'loc:rc2ma||OFFSITE - Request in Advance' - ], - shelfMark: [ - '*OFC 90-2649' - ], - suppressed: [ - false - ] - }, - { - holdingLocation: [ - { - label: 'OFFSITE - Request in Advance', - id: 'loc:rc2ma' - } - ], - status_packed: [ - 'status:a||Available' - ], - owner: [ - { - id: 'orgs:1000', - label: 'Stephen A. Schwarzman Building' - } - ], - deliveryLocation: [ - { - id: 'loc:mala', - label: 'SASB - Allen Scholar Room' - } - ], - deliveryLocation_packed: [ - 'loc:mala||SASB - Allen Scholar Room' - ], - uri: 'i102836649', - accessMessage_packed: [ - 'accessMessage:2||ADV REQUEST' - ], - accessMessage: [ - { - id: 'accessMessage:2', - label: 'ADV REQUEST' - } - ], - status: [ - { - id: 'status:a', - label: 'Available' - } - ], - owner_packed: [ - 'orgs:1000||Stephen A. Schwarzman Building' - ], - requestable: [ - false - ], - identifier: [ - 'urn:barcode:10005468369' - ], - holdingLocation_packed: [ - 'loc:rc2ma||OFFSITE - Request in Advance' - ], - shelfMark: [ - '*OFC 90-2649 2' - ], - suppressed: [ - false - ] - } - ] - - }, - _type: 'resource', - _index: 'resources-2017-06-13', - _score: 154.93451 - } - ] - }, - timed_out: false - } -} diff --git a/test/fixtures/specRequestable/phys-requestable-override.js b/test/fixtures/specRequestable/phys-requestable-override.js deleted file mode 100644 index 6c022618..00000000 --- a/test/fixtures/specRequestable/phys-requestable-override.js +++ /dev/null @@ -1,297 +0,0 @@ -module.exports = { - _shards: { - failed: 0, - successful: 1, - total: 1 - }, - took: 1, - hits: { - total: 1, - max_score: 1.3862944, - hits: [ - { - _id: 'b10980129', - _source: { - numItems: [ - 4 - ], - createdString: [ - '1989' - ], - issuance: [ - { - label: 'monograph/item', - id: 'urn:biblevel:m' - } - ], - supplementaryContent: [{ label: 'Finding aid', url: 'spaghetti.com' }], - creatorLiteral: [ - 'Maḥfūẓ, Najīb, 1911-2006.' - ], - creator_sort: [ - 'maḥfūẓ, najīb, 1911-2006.' - ], - level: 'debug', - items: [ - { - holdingLocation_packed: [ - 'loc:scff2||Schomburg Center - Research & Reference' - ], - suppressed: [ - false - ], - shelfMark: [ - 'Sc D 90-863' - ], - accessMessage_packed: [ - 'accessMessage:1||USE IN LIBRARY' - ], - uri: 'i10283665', - accessMessage: [ - { - label: 'USE IN LIBRARY', - id: 'accessMessage:1' - } - ], - catalogItemType: [ - { - id: 'catalogItemType:2', - label: 'book non-circ' - } - ], - deliveryLocation_packed: [ - 'loc:sc||Schomburg Center' - ], - owner: [ - { - label: 'Schomburg Center for Research in Black Culture, Jean Blackwell Hutson Research and Reference Division', - id: 'orgs:1114' - } - ], - deliveryLocation: [ - { - label: 'Schomburg Center', - id: 'loc:sc' - } - ], - identifier: [ - 'urn:barcode:32101071572406' - ], - requestable: [ - true - ], - owner_packed: [ - 'orgs:1114||Schomburg Center for Research in Black Culture, Jean Blackwell Hutson Research and Reference Division' - ], - status: [ - { - label: 'Available', - id: 'status:a' - } - ], - holdingLocation: [ - { - label: 'Schomburg Center - Research & Reference', - id: 'loc:scff2' - } - ], - status_packed: [ - 'status:a||Available' - ] - }, - { - holdingLocation_packed: [ - 'loc:scff2||Schomburg Center - Research & Reference' - ], - suppressed: [ - false - ], - shelfMark: [ - 'Sc D 90-863' - ], - accessMessage_packed: [ - 'accessMessage:1||USE IN LIBRARY' - ], - uri: 'i10283665777', - accessMessage: [ - { - label: 'USE IN LIBRARY', - id: 'accessMessage:1' - } - ], - catalogItemType: [ - { - id: 'catalogItemType:2', - label: 'book non-circ' - } - ], - deliveryLocation_packed: [ - 'loc:sc||Schomburg Center' - ], - owner: [ - { - label: 'Schomburg Center for Research in Black Culture, Jean Blackwell Hutson Research and Reference Division', - id: 'orgs:1114' - } - ], - deliveryLocation: [ - { - label: 'Schomburg Center', - id: 'loc:sc' - } - ], - identifier: [ - 'urn:barcode:32101071572406777' - ], - requestable: [ - true - ], - owner_packed: [ - 'orgs:1114||Schomburg Center for Research in Black Culture, Jean Blackwell Hutson Research and Reference Division' - ], - status: [ - { - label: 'Not Available', - id: 'status:na' - } - ], - holdingLocation: [ - { - label: 'Schomburg Center - Research & Reference', - id: 'loc:scff2' - } - ], - status_packed: [ - 'status:na||Not Available' - ] - } - ], - message: 'ResourceSerializer#serialize', - materialType_packed: [ - 'resourcetypes:txt||Text' - ], - suppressed: [ - 'false' - ], - placeOfPublication: [ - 'New York :' - ], - dateEndString: [ - '1984' - ], - title_sort: [ - 'the thief and the dogs' - ], - uris: [ - 'b11293188', - 'b11293188-i22566485', - 'b11293188-i22566489', - 'b11293188-i10283665', - 'b11293188-i10283664' - ], - language: [ - { - id: 'lang:eng', - label: 'English' - } - ], - dateString: [ - '1989' - ], - identifier: [ - 'urn:bnum:11293188', - 'urn:oclc:12248278', - 'urn:lcc:PJ7846.A46', - 'urn:lccCoarse:PJ7695.8-7976' - ], - publisher: [ - 'Doubleday,' - ], - type: [ - 'nypl:Item' - ], - createdYear: [ - 1989 - ], - contributor_sort: [ - 'badawī, muḥammad muṣṭafá.' - ], - materialType: [ - { - id: 'resourcetypes:txt', - label: 'Text' - } - ], - numAvailable: [ - 2 - ], - dimensions: [ - '22 cm.' - ], - carrierType_packed: [ - 'carriertypes:nc||volume' - ], - note: [ - 'Translation of: al-Liṣṣ wa-al-kilāb.' - ], - dateStartYear: [ - 1989 - ], - shelfMark: [ - '*OFC 90-2649' - ], - idOwi: [ - 'urn:owi:58201773' - ], - mediaType: [ - { - label: 'unmediated', - id: 'mediatypes:n' - } - ], - title: [ - 'The thief and the dogs', - 'The thief and the dogs /' - ], - titleAlt: [ - 'Liṣṣ wa-al-kilāb.' - ], - language_packed: [ - 'lang:eng||English' - ], - mediaType_packed: [ - 'mediatypes:n||unmediated' - ], - titleDisplay: [ - 'The thief and the dogs / Naguib Mahfouz ; translated by Trevor Le Gassick, M.M. Badawi ; revised by John Rodenbeck.' - ], - uri: 'b11293188', - extent: [ - '158 p. ;' - ], - carrierType: [ - { - id: 'carriertypes:nc', - label: 'volume' - } - ], - issuance_packed: [ - 'urn:biblevel:m||monograph/item' - ], - contributorLiteral: [ - 'Badawī, Muḥammad Muṣṭafá.', - 'Le Gassick, Trevor.', - 'Rodenbeck, John.' - ], - dateEndYear: [ - 1984 - ] - }, - _type: 'resource', - _index: 'resources-2017-06-13', - _score: 154.93451 - } - ] - }, - timed_out: false -} diff --git a/test/requestability_resolver.test.js b/test/requestability_resolver.test.js index da8de713..a720d7d8 100644 --- a/test/requestability_resolver.test.js +++ b/test/requestability_resolver.test.js @@ -1,35 +1,18 @@ const RequestabilityResolver = require('../lib/requestability_resolver') const elasticSearchResponse = require('./fixtures/elastic_search_response.js') -const specRequestableElasticSearchResponse = require('./fixtures/specRequestable/specRequestable-es-response.js') +const specRequestableElasticSearchResponse = require('./fixtures/specRequestable-es-response') const eddElasticSearchResponse = require('./fixtures/edd_elastic_search_response') -const findingAidElasticSearchResponse = require('./fixtures/specRequestable/findingAid-es-response.js') const noBarcodeResponse = require('./fixtures/no_barcode_es_response') const noRecapResponse = require('./fixtures/no_recap_response') -const physRequestableOverride = require('./fixtures/specRequestable/phys-requestable-override.js') -const DeliveryLocationsResolver = require('../lib/delivery-locations-resolver.js') describe('RequestabilityResolver', () => { describe('fixItemRequestability', function () { - let NyplResponse - before(() => { - NyplResponse = elasticSearchResponse.fakeElasticSearchResponseNyplItem() - }) + const NyplResponse = elasticSearchResponse.fakeElasticSearchResponseNyplItem() it('sets physRequestable false for items with no barcodes', () => { const noBarcode = noBarcodeResponse() const resp = RequestabilityResolver.fixItemRequestability(noBarcode) expect(resp.hits.hits[0]._source.items.every((item) => item.physRequestable === false)).to.equal(true) }) - it('specRequestable overrides physRequestable, when items have phys requestable holding location', () => { - const esResponseItems = physRequestableOverride.hits.hits[0]._source.items - const isPhysRequestable = (item) => !!item.deliveryLocation.length - const resp = RequestabilityResolver.fixItemRequestability(physRequestableOverride) - // verify that items are phys requestable based on location... - expect(esResponseItems - .map(DeliveryLocationsResolver.getOnsiteDeliveryInfo) - .every(isPhysRequestable)).to.equal(true) - // ...but overridden by specRequestability - expect(resp.hits.hits[0]._source.items.every((item) => !item.physRequestable && item.specRequestable)).to.equal(true) - }) it('will set requestable to false for an item not found in ReCAP', function () { const indexedButNotAvailableInSCSBURI = 'i22566485' @@ -189,25 +172,10 @@ describe('RequestabilityResolver', () => { expect(specRequestableItem.specRequestable).to.equal(true) }) - it('marks items as specRequestable when there is a special collectionAccessType designation', function () { + it('marks items as not specRequestable when there is no aeonURL present', function () { const response = RequestabilityResolver.fixItemRequestability(specRequestableElasticSearchResponse()) const items = response.hits.hits[0]._source.items - const specRequestableItem = items.find((item) => item.uri === 'i10283665777') - expect(specRequestableItem.specRequestable).to.equal(true) - }) - - it('marks items as specRequestable when there is a finding aid on the parent bib', function () { - const response = RequestabilityResolver.fixItemRequestability(findingAidElasticSearchResponse()) - - const items = response.hits.hits[0]._source.items - expect(items.every((item) => item.specRequestable)).to.equal(true) - }) - - it('leaves item as specRequestable false when there is no finding aid, aeon url, or special holding location', () => { - const response = RequestabilityResolver.fixItemRequestability(elasticSearchResponse.fakeElasticSearchResponseNyplItem()) - const items = response.hits.hits[0]._source.items - const specRequestableItem = items.find((item) => item.uri === 'i10283665') expect(specRequestableItem.specRequestable).to.equal(false) }) @@ -239,13 +207,8 @@ describe('RequestabilityResolver', () => { }) describe('Missing recapCustomerCode', function () { - let response - let resolved - before(() => { - response = noRecapResponse.fakeElasticSearchResponseNyplItem() - resolved = RequestabilityResolver.fixItemRequestability(response) - }) - + const response = noRecapResponse.fakeElasticSearchResponseNyplItem() + const resolved = RequestabilityResolver.fixItemRequestability(response) it('marks edd and physical requestability correctly', function () { const items = resolved.hits.hits[0]._source.items const firstItem = items.find((item) => {