From d26f54ca971d5b2e72864b2bf522cfd03d0a49f9 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Wed, 14 Jan 2026 15:36:06 -0500 Subject: [PATCH 01/40] wip --- config/production.env | 2 +- config/qa.env | 2 +- lib/available_delivery_location_types.js | 1 - lib/load_nypl_core.js | 2 ++ lib/models/Item.js | 1 - lib/models/Location.js | 6 +++++- lib/requestability-utils.js | 1 + lib/resources.js | 1 - 8 files changed, 10 insertions(+), 6 deletions(-) diff --git a/config/production.env b/config/production.env index f115ea4d..abf281a4 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.35 +NYPL_CORE_VERSION=v2.37 LOG_LEVEL=info FEATURES=on-site-edd diff --git a/config/qa.env b/config/qa.env index f7cfb2f4..90b8f7c3 100644 --- a/config/qa.env +++ b/config/qa.env @@ -11,7 +11,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.35 +NYPL_CORE_VERSION=v2.37 LOG_LEVEL=debug FEATURES=on-site-edd diff --git a/lib/available_delivery_location_types.js b/lib/available_delivery_location_types.js index 16cf7bd6..1f3835ec 100644 --- a/lib/available_delivery_location_types.js +++ b/lib/available_delivery_location_types.js @@ -8,7 +8,6 @@ class AvailableDeliveryLocationTypes { if (!patronID) return null const patronType = await this._getPatronTypeOf(patronID) - if (this._isUnfamiliarPatronType(patronType)) { return null } diff --git a/lib/load_nypl_core.js b/lib/load_nypl_core.js index 6957cf7f..39dadfa7 100644 --- a/lib/load_nypl_core.js +++ b/lib/load_nypl_core.js @@ -1,6 +1,7 @@ const _data = {} const nyplCoreObjects = require('@nypl/nypl-core-objects') const loadNyplCoreData = () => { + console.log('Loading nypl core data from ', process.env.NYPL_CORE_VERSION || 'master') const vocabularies = { collections: 'by-collection', sierraLocations: 'by-sierra-location', @@ -11,6 +12,7 @@ const loadNyplCoreData = () => { } return Promise.all(Object.keys(vocabularies).map(async (vocab) => { const nyplCoreValues = await nyplCoreObjects(vocabularies[vocab]) + if (vocab === 'sierraLocations') console.log(nyplCoreValues) _data[vocab] = nyplCoreValues })) } diff --git a/lib/models/Item.js b/lib/models/Item.js index 2a868f29..f1e990ec 100644 --- a/lib/models/Item.js +++ b/lib/models/Item.js @@ -114,7 +114,6 @@ Item.withDeliveryLocationsByBarcode = async function (item, scholarRoom) { const model = new Item(item) item.eddRequestable = !!model.eddRequestable const filteredDeliveryLocationsWithScholarRoom = locationUtils.filterLocations(model.deliveryLocation, scholarRoom) - item.deliveryLocation = locationUtils.formatLocations(filteredDeliveryLocationsWithScholarRoom) return item } diff --git a/lib/models/Location.js b/lib/models/Location.js index 49592f09..b62254bb 100644 --- a/lib/models/Location.js +++ b/lib/models/Location.js @@ -2,7 +2,10 @@ const nyplCore = require('../load_nypl_core') class Location { constructor ({ holdingLocation, recapCustomerCode, m2CustomerCode }) { + console.log('location model constructor holdinglocation', holdingLocation) this.nyplCoreLocation = nyplCore.sierraLocations()[holdingLocation] + console.log('location model constructor nyplcore location data', this.nyplCoreLocation) + console.log(Object.keys(nyplCore.sierraLocations())) this.m2CustomerCode = m2CustomerCode this.recapCustomerCode = recapCustomerCode this.requestable = this.nyplCoreLocation?.requestable @@ -32,8 +35,9 @@ class Location { if (this.nyplCoreLocation?.sierraDeliveryLocations?.length) { // It's mapped, but the sierraDeliveryLocation entities only have `code` and `label` // Do a second lookup to populate `deliveryLocationTypes` + console.log(this.nyplCoreLocation.sierraDeliveryLocations) return this.nyplCoreLocation.sierraDeliveryLocations.map((deliveryLocation) => { - deliveryLocation.deliveryLocationTypes = this.nyplCoreLocation.deliveryLocationTypes + // deliveryLocation.deliveryLocationTypes = this.nyplCoreLocation.deliveryLocationTypes return deliveryLocation }) } diff --git a/lib/requestability-utils.js b/lib/requestability-utils.js index 56cb867c..2b1f5550 100644 --- a/lib/requestability-utils.js +++ b/lib/requestability-utils.js @@ -87,6 +87,7 @@ class DeliveryLocationsResolver { // Filter out anything not matching the specified deliveryLocationType return locations.filter((location) => { const locationIsNotScholarRoom = !location.deliveryLocationTypes.includes('Scholar') + console.log({ location, locationIsNotScholarRoom, locationCodeEqualsScholarRoom: location.code === scholarRoom }) return locationIsNotScholarRoom || (location.code === scholarRoom) }) } diff --git a/lib/resources.js b/lib/resources.js index f342cd3b..699d7ec1 100644 --- a/lib/resources.js +++ b/lib/resources.js @@ -305,7 +305,6 @@ module.exports = function (app, _private = null) { // The resolved values of Promise.all are strictly ordered based on original array of promises const items = resp[0] const scholarRoom = resp[1] - // Use HTC API and nypl-core mappings to ammend ES response with deliveryLocations: return Promise.all(items.map(async (item) => Item.withDeliveryLocationsByBarcode(item, scholarRoom))) .catch((e) => { From f4defb62cbeccd6d7199da52882f84fa1d574e95 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Wed, 14 Jan 2026 15:48:10 -0500 Subject: [PATCH 02/40] fix delivery location type biz --- lib/load_nypl_core.js | 1 - lib/models/Location.js | 17 +---------------- package-lock.json | 1 + 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/load_nypl_core.js b/lib/load_nypl_core.js index 39dadfa7..9d8a8b3a 100644 --- a/lib/load_nypl_core.js +++ b/lib/load_nypl_core.js @@ -12,7 +12,6 @@ const loadNyplCoreData = () => { } return Promise.all(Object.keys(vocabularies).map(async (vocab) => { const nyplCoreValues = await nyplCoreObjects(vocabularies[vocab]) - if (vocab === 'sierraLocations') console.log(nyplCoreValues) _data[vocab] = nyplCoreValues })) } diff --git a/lib/models/Location.js b/lib/models/Location.js index b62254bb..6cbbc634 100644 --- a/lib/models/Location.js +++ b/lib/models/Location.js @@ -2,10 +2,7 @@ const nyplCore = require('../load_nypl_core') class Location { constructor ({ holdingLocation, recapCustomerCode, m2CustomerCode }) { - console.log('location model constructor holdinglocation', holdingLocation) this.nyplCoreLocation = nyplCore.sierraLocations()[holdingLocation] - console.log('location model constructor nyplcore location data', this.nyplCoreLocation) - console.log(Object.keys(nyplCore.sierraLocations())) this.m2CustomerCode = m2CustomerCode this.recapCustomerCode = recapCustomerCode this.requestable = this.nyplCoreLocation?.requestable @@ -27,19 +24,7 @@ class Location { case 'm2-customer-code': return this.deliveryLocationsByM2CustomerCode default: - return this.deliveryLocationByHoldingLocation - } - } - - get deliveryLocationByHoldingLocation () { - if (this.nyplCoreLocation?.sierraDeliveryLocations?.length) { - // It's mapped, but the sierraDeliveryLocation entities only have `code` and `label` - // Do a second lookup to populate `deliveryLocationTypes` - console.log(this.nyplCoreLocation.sierraDeliveryLocations) - return this.nyplCoreLocation.sierraDeliveryLocations.map((deliveryLocation) => { - // deliveryLocation.deliveryLocationTypes = this.nyplCoreLocation.deliveryLocationTypes - return deliveryLocation - }) + return this.nyplCoreLocation?.sierraDeliveryLocations } } diff --git a/package-lock.json b/package-lock.json index 57dba3b4..e142a8e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3712,6 +3712,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" From 4f4e0e350f78a95ead48378e9f1073f0bdceb7f6 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Wed, 14 Jan 2026 15:58:07 -0500 Subject: [PATCH 03/40] rm console.log --- lib/requestability-utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/requestability-utils.js b/lib/requestability-utils.js index 2b1f5550..56cb867c 100644 --- a/lib/requestability-utils.js +++ b/lib/requestability-utils.js @@ -87,7 +87,6 @@ class DeliveryLocationsResolver { // Filter out anything not matching the specified deliveryLocationType return locations.filter((location) => { const locationIsNotScholarRoom = !location.deliveryLocationTypes.includes('Scholar') - console.log({ location, locationIsNotScholarRoom, locationCodeEqualsScholarRoom: location.code === scholarRoom }) return locationIsNotScholarRoom || (location.code === scholarRoom) }) } From ec1ea45319145d0e4793f2f7820b1aad743d6f93 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Wed, 14 Jan 2026 15:58:51 -0500 Subject: [PATCH 04/40] rm console log --- lib/load_nypl_core.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/load_nypl_core.js b/lib/load_nypl_core.js index 9d8a8b3a..6957cf7f 100644 --- a/lib/load_nypl_core.js +++ b/lib/load_nypl_core.js @@ -1,7 +1,6 @@ const _data = {} const nyplCoreObjects = require('@nypl/nypl-core-objects') const loadNyplCoreData = () => { - console.log('Loading nypl core data from ', process.env.NYPL_CORE_VERSION || 'master') const vocabularies = { collections: 'by-collection', sierraLocations: 'by-sierra-location', From 7a3448059c3bcaa8803230f596708fbb20e4cd19 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 12 Feb 2026 10:28:24 -0500 Subject: [PATCH 05/40] update resources index --- config/production.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/production.env b/config/production.env index abf281a4..42396637 100644 --- a/config/production.env +++ b/config/production.env @@ -1,5 +1,5 @@ ENCRYPTED_ELASTICSEARCH_URI=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJYwgZMGCSqGSIb3DQEHBqCBhTCBggIBADB9BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDFWw8ECX9Pz81z0kvAIBEIBQGec9PCpwuvEgLH6imhqP6tx1fj8Vlf2ZipnUy06jzmpE262Qvk9LPAq7sIYPVkTCZctwilwcU9oC6yxasVoUlK87la77v03CeZsPIDwciFY= -ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHIwcAYJKoZIhvcNAQcGoGMwYQIBADBcBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGw+4ALa5eFYzC4SWwIBEIAvOVuHGgflzmWLE6GBauPAiSQLmL4xaSfBFED+zyGnQ+jV0KS64aN45ZFR0xgaZS0= +ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHcwdQYJKoZIhvcNAQcGoGgwZgIBADBhBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGWf4Ka9zGyw0zzPCAIBEIA0nAD58HXOOpXOUDY/xT5RYo9G7liIOPCVeq/W8EIEr1zerU4AnZr+A4y2kkLiBs99kfV4aw== ENCRYPTED_ELASTICSEARCH_API_KEY=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJ4wgZsGCSqGSIb3DQEHBqCBjTCBigIBADCBhAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyPOPaQCBbvKQhJoPQCARCAV2TlWlRh+xKnCegpprEQgfldZGcVW48RND0LVd/pQpVTJnRTtbCpP7damT7k8ziJVdWZ3jsfs5fw5YnKc/EIQ1M//DRUzOJL98ir5LTTxE7QhflKDtUY+Q== ENCRYPTED_SCSB_URL=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHwwegYJKoZIhvcNAQcGoG0wawIBADBmBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDKPFC8wFkVM5CyT6VQIBEIA5m4eLBkpChRA//ZNEWsRqIDGZmevb/thzI03a0NiAW6VfybSAYpFthh+bj/yAk1VEEBF6r1T4A2GP From 1474814fdc795853499d8e81b3023f8e12bb96e8 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 13 Feb 2026 09:53:05 -0500 Subject: [PATCH 06/40] update index --- config/production.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/production.env b/config/production.env index 42396637..be8cda54 100644 --- a/config/production.env +++ b/config/production.env @@ -1,5 +1,5 @@ ENCRYPTED_ELASTICSEARCH_URI=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJYwgZMGCSqGSIb3DQEHBqCBhTCBggIBADB9BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDFWw8ECX9Pz81z0kvAIBEIBQGec9PCpwuvEgLH6imhqP6tx1fj8Vlf2ZipnUy06jzmpE262Qvk9LPAq7sIYPVkTCZctwilwcU9oC6yxasVoUlK87la77v03CeZsPIDwciFY= -ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHcwdQYJKoZIhvcNAQcGoGgwZgIBADBhBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGWf4Ka9zGyw0zzPCAIBEIA0nAD58HXOOpXOUDY/xT5RYo9G7liIOPCVeq/W8EIEr1zerU4AnZr+A4y2kkLiBs99kfV4aw== +ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHcwdQYJKoZIhvcNAQcGoGgwZgIBADBhBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGOwQk67wE9nnptNhgIBEIA0olICpuUjppBGfOeFMWkmnElVd6qfJXPlgezZDu08t+rt4kQzWBuK7DALwhCeGNl45UpfGg== ENCRYPTED_ELASTICSEARCH_API_KEY=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJ4wgZsGCSqGSIb3DQEHBqCBjTCBigIBADCBhAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyPOPaQCBbvKQhJoPQCARCAV2TlWlRh+xKnCegpprEQgfldZGcVW48RND0LVd/pQpVTJnRTtbCpP7damT7k8ziJVdWZ3jsfs5fw5YnKc/EIQ1M//DRUzOJL98ir5LTTxE7QhflKDtUY+Q== ENCRYPTED_SCSB_URL=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHwwegYJKoZIhvcNAQcGoG0wawIBADBmBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDKPFC8wFkVM5CyT6VQIBEIA5m4eLBkpChRA//ZNEWsRqIDGZmevb/thzI03a0NiAW6VfybSAYpFthh+bj/yAk1VEEBF6r1T4A2GP From 891e41d9e637cd80ce4c89651b5ea4f7b08353dd Mon Sep 17 00:00:00 2001 From: Ian O'Connor Date: Wed, 11 Mar 2026 16:49:50 -0400 Subject: [PATCH 07/40] Support for _displayPacked fields (contributors and creators) --- lib/contributors.js | 6 +++--- lib/display-field-unpacker.js | 25 ++++++++++++++++++++++ lib/elasticsearch/elastic-query-builder.js | 2 +- lib/response_massager.js | 4 ++++ test/display-field-unpacker.test.js | 22 +++++++++++++++++++ test/elastic-query-builder.test.js | 2 +- test/fixtures/packed-display-response.json | 18 ++++++++++++++++ 7 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 lib/display-field-unpacker.js create mode 100644 test/display-field-unpacker.test.js create mode 100644 test/fixtures/packed-display-response.json diff --git a/lib/contributors.js b/lib/contributors.js index 0e7321de..4ccf874c 100644 --- a/lib/contributors.js +++ b/lib/contributors.js @@ -100,21 +100,21 @@ module.exports = function (app, _private = null) { } /** - * Builds an aggregation query that checks the resource index for counts on the contributorRoleLiteral field for a list of contributors. + * Builds an aggregation query that checks the resource index for counts on the browseableContributorRole_packed field for a list of contributors. */ const buildElasticRoleCountQuery = function (contributorList) { return { size: 0, query: { terms: { - contributorRoleLiteral: contributorList + browseableContributorRole_packed: contributorList } }, aggs: { contributor_role: { terms: { script: { - source: 'def results = []; for (val in doc["contributorRoleLiteral"]) { int pos = val.indexOf("||"); if (pos != -1) { String name = val.substring(0, pos); if (params.targets.contains(name)) { results.add(val); } } } return results;', + source: 'def results = []; for (val in doc["browseableContributorRole_packed"]) { int pos = val.indexOf("||"); if (pos != -1) { String name = val.substring(0, pos); if (params.targets.contains(name)) { results.add(val); } } } return results;', params: { targets: contributorList } diff --git a/lib/display-field-unpacker.js b/lib/display-field-unpacker.js new file mode 100644 index 00000000..5ae5b26e --- /dev/null +++ b/lib/display-field-unpacker.js @@ -0,0 +1,25 @@ +const parseValueAndLabel = (delimitedString) => { + if (!delimitedString.includes('||')) { + return { value: delimitedString, display: null } + } + const [value, display] = delimitedString.split('||') + return { value, display } +} + +module.exports = (elasticSearchResponse) => { + elasticSearchResponse.hits.hits.forEach((bib) => { + // Contributors and creators are packed like so || where + // can have prefix, title, and roles. We'd like to unpack them in a friendly format for the frontend + // to display the full label and use the isolated name for link-building + Object.entries(bib._source).forEach(([key, value]) => { + if (key.endsWith('_displayPacked')) { + const fieldName = key.replace('_displayPacked', '') + bib._source[fieldName + 'Display'] = value.map((packedValue) => parseValueAndLabel(packedValue)) + delete bib._source[key] + } + }) + + return bib + }) + return elasticSearchResponse +} diff --git a/lib/elasticsearch/elastic-query-builder.js b/lib/elasticsearch/elastic-query-builder.js index 70efa58f..0b4452bc 100644 --- a/lib/elasticsearch/elastic-query-builder.js +++ b/lib/elasticsearch/elastic-query-builder.js @@ -181,7 +181,7 @@ class ElasticQueryBuilder { * Concat contributor + role if role param is provided */ applyContributorRole () { - this.query.addMust(termMatch('contributorRoleLiteral', this.request.params.filters.contributorLiteral + '||' + this.request.params.role)) + this.query.addMust(termMatch('browseableContributorRole_packed', this.request.params.filters.contributorLiteral + '||' + this.request.params.role)) } /** diff --git a/lib/response_massager.js b/lib/response_massager.js index a1eb17ec..034580a5 100644 --- a/lib/response_massager.js +++ b/lib/response_massager.js @@ -1,6 +1,7 @@ const LocationLabelUpdater = require('./location_label_updater') const AvailabilityResolver = require('./availability_resolver.js') const parallelFieldsExtractor = require('./parallel-fields-extractor') +const displayFieldUnpacker = require('./display-field-unpacker') const { isAeonUrl, sortOnPropWithUndefinedLast } = require('../lib/util') const FulfillmentResolver = require('./fulfillment_resolver') const fixItemRequestability = require('./requestability_resolver') @@ -73,6 +74,9 @@ class ResponseMassager { // Rename parallel fields: response = parallelFieldsExtractor(response) + // Extract display values and labels from packed fields + response = displayFieldUnpacker(response) + // Update ES response with updated availability from SCSB: const updatedWithAvailability = (new AvailabilityResolver(response)) .responseWithUpdatedAvailability(options) diff --git a/test/display-field-unpacker.test.js b/test/display-field-unpacker.test.js new file mode 100644 index 00000000..3f768393 --- /dev/null +++ b/test/display-field-unpacker.test.js @@ -0,0 +1,22 @@ +const { expect } = require('chai') +const displayFieldsUnpacker = require('../lib/display-field-unpacker') +const packedDisplayBib = require('./fixtures/packed-display-response.json') + +describe('Display field unpacker', () => { + describe('When a bib has a packed display property', () => { + it('adds each of the items in that array as unpacked objects', () => { + const displayFieldsUnpacked = displayFieldsUnpacker(packedDisplayBib).hits.hits[0]._source + expect(Object.keys(displayFieldsUnpacked).length).to.equal(2) + expect(displayFieldsUnpacked).to.deep.equal({ + testDisplay: [ + { value: 'someValue', display: 'someDisplay' }, + { value: 'someValueB', display: 'someDisplayB' }, + { value: 'someValueC', display: null } + ], + testOtherDisplay: [ + { value: 'otherValue', display: 'otherDisplay' } + ] + }) + }) + }) +}) diff --git a/test/elastic-query-builder.test.js b/test/elastic-query-builder.test.js index 48c08440..062a9ec0 100644 --- a/test/elastic-query-builder.test.js +++ b/test/elastic-query-builder.test.js @@ -272,7 +272,7 @@ describe('ElasticQueryBuilder', () => { const inst = ElasticQueryBuilder.forApiRequest(request) expect(inst.query.toJson()).to.nested - .include({ 'bool.must[0].term.contributorRoleLiteral.value': 'Patinkin, Mandy||performer.' }) + .include({ 'bool.must[0].term.browseableContributorRole_packed.value': 'Patinkin, Mandy||performer.' }) }) }) diff --git a/test/fixtures/packed-display-response.json b/test/fixtures/packed-display-response.json new file mode 100644 index 00000000..3a304c50 --- /dev/null +++ b/test/fixtures/packed-display-response.json @@ -0,0 +1,18 @@ +{ + "hits": { + "hits": [ + { + "_source": { + "test_displayPacked": [ + "someValue||someDisplay", + "someValueB||someDisplayB", + "someValueC" + ], + "testOther_displayPacked": [ + "otherValue||otherDisplay" + ] + } + } + ] + } +} From 3dcbf335834f95f818cf3266b35cbc5cbff9faee Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 12 Mar 2026 10:20:01 -0400 Subject: [PATCH 08/40] refactor --- .github/workflows/test-and-deploy.yml | 133 ++++---------------------- 1 file changed, 20 insertions(+), 113 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index c9306ac6..a179ba4b 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -2,6 +2,11 @@ name: Unit Tests on: [push] +# Global environment variable based on the branch name +env: + ENV_TAG: ${{ github.ref_name }} + ECR_REPOSITORY: discovery-api + jobs: tests: runs-on: ubuntu-latest @@ -15,114 +20,15 @@ jobs: run: npm ci - name: Unit Tests run: npm test - integration-test-qa: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: tests - if: github.ref == 'refs/heads/qa' - steps: - - uses: actions/checkout@v4 - - name: Set Node version - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Start service - run: ENV=qa npm start & - - name: Run tests - run: npm run test-integration - deploy-qa: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa --service discovery-api-qa --force-new-deployment - deploy-qa2: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa2' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa2-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa2 --service discovery-api-qa2 --force-new-deployment - deploy-production: + deploy: permissions: id-token: write contents: read runs-on: ubuntu-latest needs: tests - if: github.ref == 'refs/heads/production' + # Only run if it's one of our three deployment branches + if: contains(fromJSON('["qa", "qa2", "production"]'), github.ref_name) steps: - name: Checkout repo uses: actions/checkout@v3 @@ -138,22 +44,23 @@ jobs: uses: aws-actions/amazon-ecr-login@v1 - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "production-previous" --image-manifest "$MANIFEST"; fi + MANIFEST=$(aws ecr batch-get-image --repository-name ${{ env.ECR_REPOSITORY }} --image-ids imageTag="${{ env.ENV_TAG }}-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') + PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name ${{ env.ECR_REPOSITORY }} --image-ids imageTag="${{ env.ENV_TAG }}-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') + if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then + aws ecr put-image --repository-name ${{ env.ECR_REPOSITORY }} --image-tag "${{ env.ENV_TAG }}-previous" --image-manifest "$MANIFEST" + fi + - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api IMAGE_TAG: ${{ github.sha }} run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:production-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:production-latest + docker build -t $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG . + docker push $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG + docker tag $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:${{ env.ENV_TAG }}-latest + docker push $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:${{ env.ENV_TAG }}-latest + - name: Force ECS Update run: | - aws ecs update-service --cluster discovery-api-production --service discovery-api-production --force-new-deployment + aws ecs update-service --cluster discovery-api-${{ env.ENV_TAG }} --service discovery-api-${{ env.ENV_TAG }} --force-new-deployment \ No newline at end of file From 643b8df9855904f87154e16f1793e2dde465973c Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 12 Mar 2026 14:16:29 -0400 Subject: [PATCH 09/40] fix filter --- lib/elasticsearch/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elasticsearch/config.js b/lib/elasticsearch/config.js index a7ae053d..bc9f9ec7 100644 --- a/lib/elasticsearch/config.js +++ b/lib/elasticsearch/config.js @@ -89,7 +89,7 @@ const FILTER_CONFIG = { mediaType: { operator: 'match', field: ['mediaType.id', 'mediaType.label'], repeatable: true }, carrierType: { operator: 'match', field: ['carrierType.id', 'carrierType.label'], repeatable: true }, publisher: { operator: 'match', field: ['publisherLiteral.raw'], repeatable: true }, - contributorLiteral: { operator: 'match', field: ['contributorLiteral.keywordLowercased', 'parallelContributor.raw', 'creatorLiteral.keywordLowercased', 'parallelCreatorLiteral.raw'], repeatable: true }, + contributorLiteral: { operator: 'match', field: ['contributorLiteral.keywordLowercased', 'parallelContributorLiteral.raw', 'creatorLiteral.keywordLowercased', 'parallelCreatorLiteral.raw'], repeatable: true }, creatorLiteral: { operator: 'match', field: ['creatorLiteral.raw', 'parallelCreatorLiteral.raw'], repeatable: true }, issuance: { operator: 'match', field: ['issuance.id', 'issuance.label'], repeatable: true }, createdYear: { operator: 'match', field: ['createdYear'], repeatable: true }, From d4b31bcbfdeb4321725a5ffd532aa08d71884274 Mon Sep 17 00:00:00 2001 From: Emma Mansell <73774046+7emansell@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:30:51 -0400 Subject: [PATCH 10/40] Bump core to 2.39 --- config/production.env | 2 +- config/qa.env | 2 +- config/test.env | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/production.env b/config/production.env index cd7e9955..6fe4c6b2 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.35 +NYPL_CORE_VERSION=v2.39 LOG_LEVEL=info FEATURES=on-site-edd diff --git a/config/qa.env b/config/qa.env index 627e25b8..05a98812 100644 --- a/config/qa.env +++ b/config/qa.env @@ -12,7 +12,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.35 +NYPL_CORE_VERSION=v2.39 LOG_LEVEL=debug FEATURES=on-site-edd diff --git a/config/test.env b/config/test.env index 3883094a..9267f131 100644 --- a/config/test.env +++ b/config/test.env @@ -10,7 +10,7 @@ NYPL_OAUTH_URL=http://oauth.example.com NYPL_OAUTH_ID=encrypted-nypl-oauth-id NYPL_OAUTH_SECRET=encrypted-nypl-oauth-id -NYPL_CORE_VERSION=v2.37 +NYPL_CORE_VERSION=v2.39 LOG_LEVEL=error FEATURES=on-site-edd From 220f3363ed7a111dcf32f2d7054a5ab251e71a13 Mon Sep 17 00:00:00 2001 From: Emma Mansell <73774046+7emansell@users.noreply.github.com> Date: Thu, 12 Mar 2026 16:31:06 -0400 Subject: [PATCH 11/40] Add collection access type to item holding location, plus test --- lib/jsonld_serializers.js | 18 ++++++++++++++++++ test/item-resource-serializer.test.js | 14 +++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/jsonld_serializers.js b/lib/jsonld_serializers.js index 31081f7e..300d42bd 100644 --- a/lib/jsonld_serializers.js +++ b/lib/jsonld_serializers.js @@ -374,10 +374,15 @@ class ItemResourceSerializer extends JsonLdItemSerializer { }) } + if (this.body.holdingLocation) { + stmts.holdingLocation = ItemResourceSerializer.getFormattedHoldingLocation(this.body.holdingLocation) + } + // Override default serialization of item.electronicLocator statements (full digital surrogates): if (this.body.electronicLocator) { stmts.electronicLocator = this.body.electronicLocator.map((link) => ResourceSerializer.formatElectronicResourceBlankNode(link, 'nypl:ElectronicLocation')) } + return stmts } @@ -386,6 +391,19 @@ class ItemResourceSerializer extends JsonLdItemSerializer { return (new ItemResourceSerializer(resp, options)).format() } + static getFormattedHoldingLocation (location) { + const loc = location[0] + const locationId = loc['@id'].split(':')[1] + // Add collection access type from corresponding Core location + const collectionAccessType = + nyplCore.sierraLocations()[locationId]?.collectionAccessType + return [{ + '@id': loc['@id'], + prefLabel: loc.prefLabel, + collectionAccessType + }] + } + // Given an item, returns item with an added `identifier` // of form 'urn:[sourceIdentifierPrefix]:[sourceIdentifier]' // e.g. diff --git a/test/item-resource-serializer.test.js b/test/item-resource-serializer.test.js index 57429231..3387c8c8 100644 --- a/test/item-resource-serializer.test.js +++ b/test/item-resource-serializer.test.js @@ -60,7 +60,19 @@ describe('ItemResourceSerializer', () => { expect(doc.idNyplSourceId['@value']).to.eq('9876543210') }) }) - + describe('getFormattedHoldingLocation', () => { + it('should return holding location with id, label, and collection access type', () => { + const locationEntity = ItemResourceSerializer.getFormattedHoldingLocation([ + { + '@id': 'loc:maff1', + prefLabel: 'Schwarzman Building - Dorot Jewish Division Reference Room 111' + } + ]) + expect(locationEntity[0].prefLabel).to.equal('Schwarzman Building - Dorot Jewish Division Reference Room 111') + expect(locationEntity[0]['@id']).to.equal('loc:maff1') + expect(locationEntity[0].collectionAccessType).to.equal('shelf') + }) + }) describe('addSourceIdentifier', () => { it('adds source identifier for NYPL', async () => { const item = { uri: 'i1234' } From c5d482d4d7cfdd94d5674d41a1c689a01a98b9c2 Mon Sep 17 00:00:00 2001 From: danamansana Date: Fri, 13 Mar 2026 13:08:25 -0400 Subject: [PATCH 12/40] Add shelfmark sorting for callnumber searches --- lib/resources.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/resources.js b/lib/resources.js index 462ae779..3c9cccf6 100644 --- a/lib/resources.js +++ b/lib/resources.js @@ -46,6 +46,10 @@ const SORT_FIELDS = { initialDirection: 'asc', field: 'creator_sort' }, + callnumber: { + initialDirection: 'asc', + field: 'shelfMark.keywordLowercased' + }, relevance: {} } From fa9747970affc20a437f815a6ecf7731650a9a68 Mon Sep 17 00:00:00 2001 From: Emma Mansell <73774046+7emansell@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:28:01 -0400 Subject: [PATCH 13/40] Update collection entities to include locationsPath, bump nypl-core-objects to 3.0.5 --- lib/jsonld_serializers.js | 4 +++- lib/vocabularies.js | 3 ++- package-lock.json | 40 ++++++++++++++++++++++++++------ package.json | 2 +- test/resource_serializer.test.js | 2 ++ test/vocabularies.test.js | 2 +- 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/lib/jsonld_serializers.js b/lib/jsonld_serializers.js index 300d42bd..a2d690b5 100644 --- a/lib/jsonld_serializers.js +++ b/lib/jsonld_serializers.js @@ -301,12 +301,14 @@ ResourceSerializer.getFormattedFormat = function (formatId) { ResourceSerializer.formatCollection = function (collectionId) { const prefLabel = nyplCore.collections()[`nyplCollection:${collectionId}`]?.label + const locationsPath = nyplCore.collections()[`nyplCollection:${collectionId}`]?.locationsPath const buildingLocationLabel = buildingLocations.find((loc) => loc.value === collectionId.slice(0, 2))?.label if (!prefLabel) return null return { '@id': collectionId, prefLabel, - buildingLocationLabel + buildingLocationLabel, + locationsPath } } diff --git a/lib/vocabularies.js b/lib/vocabularies.js index b7627e48..451f1e81 100644 --- a/lib/vocabularies.js +++ b/lib/vocabularies.js @@ -17,7 +17,8 @@ module.exports = function (app, _private = null) { (val) => ({ value: val.code, label: val.label, - holdingLocations: val.holdingLocations + holdingLocations: val.holdingLocations, + locationsPath: val.locationsPath }) ) return ({ formats, collections, languages: languages.values, buildingLocations }) diff --git a/package-lock.json b/package-lock.json index e142a8e2..12741fbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-kms": "^3.948.0", "@elastic/elasticsearch": "~8.12.0", - "@nypl/nypl-core-objects": "3.0.4", + "@nypl/nypl-core-objects": "3.0.5", "@nypl/nypl-data-api-client": "^2.0.0", "@nypl/scsb-rest-client": "3.0.0", "dotenv": "^16.4.5", @@ -3588,9 +3588,9 @@ } }, "node_modules/@nypl/nypl-core-objects": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-3.0.4.tgz", - "integrity": "sha512-xFo14urEMO2VUL2/YcjG3io1deU/UCKtYlI2ADCOTEwOcCpwzzvkvOvTI2E2CvgstkHplVejBjdvd4l6ak0inQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-3.0.5.tgz", + "integrity": "sha512-XXQ15ekqBgn/XX9eNmU40EE+OpIYSyTkq/TdAEhmTRiHWC+tohT8cfDNABz8lH+xnuqiwam6Fmvla3g5KI2DHQ==", "dependencies": { "axios": "^1.6.8", "csv": "^5.3.2", @@ -7245,9 +7245,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", @@ -10379,6 +10379,27 @@ "node": ">=10" } }, + "node_modules/research-catalog-indexer/node_modules/@nypl/nypl-core-objects": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@nypl/nypl-core-objects/-/nypl-core-objects-3.0.4.tgz", + "integrity": "sha512-xFo14urEMO2VUL2/YcjG3io1deU/UCKtYlI2ADCOTEwOcCpwzzvkvOvTI2E2CvgstkHplVejBjdvd4l6ak0inQ==", + "dependencies": { + "axios": "^1.6.8", + "csv": "^5.3.2", + "csv-stringify": "^5.6.0", + "just-flatten": "^1.0.0" + } + }, + "node_modules/research-catalog-indexer/node_modules/@nypl/nypl-core-objects/node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.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", @@ -10397,6 +10418,11 @@ "form-data": "^4.0.0" } }, + "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 3383b465..a1cec83f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies": { "@aws-sdk/client-kms": "^3.948.0", "@elastic/elasticsearch": "~8.12.0", - "@nypl/nypl-core-objects": "3.0.4", + "@nypl/nypl-core-objects": "3.0.5", "@nypl/nypl-data-api-client": "^2.0.0", "@nypl/scsb-rest-client": "3.0.0", "dotenv": "^16.4.5", diff --git a/test/resource_serializer.test.js b/test/resource_serializer.test.js index be0427e6..5924b72b 100644 --- a/test/resource_serializer.test.js +++ b/test/resource_serializer.test.js @@ -9,11 +9,13 @@ describe('Resource Serializer', () => { { '@id': 'mal', buildingLocationLabel: 'Stephen A. Schwarzman Building (SASB)', + locationsPath: 'locations/schwarzman/general-research-division', prefLabel: 'General Research Division' }, { '@id': 'bur', buildingLocationLabel: 'Stavros Niarchos Foundation Library (SNFL)', + locationsPath: 'locations/snfl/yoseloff-business', prefLabel: 'Yoseloff Business Center' } ]) diff --git a/test/vocabularies.test.js b/test/vocabularies.test.js index d3bdb48d..81c87a6e 100644 --- a/test/vocabularies.test.js +++ b/test/vocabularies.test.js @@ -57,6 +57,6 @@ describe('Vocabularies', function () { const results = await app.vocabularies({}, { baseUrl: app.baseUrl }) - expect(results.collections[0]).to.have.keys(['value', 'label', 'holdingLocations']) + expect(results.collections[0]).to.have.keys(['value', 'label', 'holdingLocations', 'locationsPath']) }) }) From 129f9a691f80b034fc75874bddc34a3efa0e6105 Mon Sep 17 00:00:00 2001 From: Emma Mansell <73774046+7emansell@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:29:59 -0400 Subject: [PATCH 14/40] One more test --- test/resource_serializer.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/resource_serializer.test.js b/test/resource_serializer.test.js index 5924b72b..133671ae 100644 --- a/test/resource_serializer.test.js +++ b/test/resource_serializer.test.js @@ -32,6 +32,7 @@ describe('Resource Serializer', () => { expect(collectionEntity.prefLabel).to.equal('Art & Architecture Collection') expect(collectionEntity['@id']).to.equal('mab') expect(collectionEntity.buildingLocationLabel).to.equal('Stephen A. Schwarzman Building (SASB)') + expect(collectionEntity.locationsPath).to.equal('locations/schwarzman/wallach-division/art-architecture-collection') }) }) describe('.formatItemFilterAggregations()', () => { From af3fdab47faf72787a53d8a1beb2f742b4751c56 Mon Sep 17 00:00:00 2001 From: Emma Mansell <73774046+7emansell@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:35:25 -0400 Subject: [PATCH 15/40] Make getFormattedHoldingLocation more defensive --- lib/jsonld_serializers.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/jsonld_serializers.js b/lib/jsonld_serializers.js index a2d690b5..b3b1ca36 100644 --- a/lib/jsonld_serializers.js +++ b/lib/jsonld_serializers.js @@ -394,14 +394,14 @@ class ItemResourceSerializer extends JsonLdItemSerializer { } static getFormattedHoldingLocation (location) { - const loc = location[0] - const locationId = loc['@id'].split(':')[1] - // Add collection access type from corresponding Core location - const collectionAccessType = - nyplCore.sierraLocations()[locationId]?.collectionAccessType + const loc = Array.isArray(location) ? location[0] : null + if (!loc) return [] + const locationId = loc['@id']?.split(':')[1] + const sierraLocations = nyplCore.sierraLocations() + const collectionAccessType = sierraLocations?.[locationId]?.collectionAccessType return [{ '@id': loc['@id'], - prefLabel: loc.prefLabel, + prefLabel: loc?.prefLabel, collectionAccessType }] } From b6dcd86bc4e396853067c3d7153b9c990a4a0e41 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 16 Mar 2026 15:29:04 -0400 Subject: [PATCH 16/40] the tests --- .github/workflows/test-and-deploy.yml | 133 +++--------------- .../delivery-locations-by-barcode.test.js | 41 ++++-- .../delivery-locations-constants.js | 10 +- 3 files changed, 55 insertions(+), 129 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index c9306ac6..a179ba4b 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -2,6 +2,11 @@ name: Unit Tests on: [push] +# Global environment variable based on the branch name +env: + ENV_TAG: ${{ github.ref_name }} + ECR_REPOSITORY: discovery-api + jobs: tests: runs-on: ubuntu-latest @@ -15,114 +20,15 @@ jobs: run: npm ci - name: Unit Tests run: npm test - integration-test-qa: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: tests - if: github.ref == 'refs/heads/qa' - steps: - - uses: actions/checkout@v4 - - name: Set Node version - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - - name: Install dependencies - run: npm ci - - name: Start service - run: ENV=qa npm start & - - name: Run tests - run: npm run test-integration - deploy-qa: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa --service discovery-api-qa --force-new-deployment - deploy-qa2: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa2' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa2-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa2 --service discovery-api-qa2 --force-new-deployment - deploy-production: + deploy: permissions: id-token: write contents: read runs-on: ubuntu-latest needs: tests - if: github.ref == 'refs/heads/production' + # Only run if it's one of our three deployment branches + if: contains(fromJSON('["qa", "qa2", "production"]'), github.ref_name) steps: - name: Checkout repo uses: actions/checkout@v3 @@ -138,22 +44,23 @@ jobs: uses: aws-actions/amazon-ecr-login@v1 - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "production-previous" --image-manifest "$MANIFEST"; fi + MANIFEST=$(aws ecr batch-get-image --repository-name ${{ env.ECR_REPOSITORY }} --image-ids imageTag="${{ env.ENV_TAG }}-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') + PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name ${{ env.ECR_REPOSITORY }} --image-ids imageTag="${{ env.ENV_TAG }}-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') + if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then + aws ecr put-image --repository-name ${{ env.ECR_REPOSITORY }} --image-tag "${{ env.ENV_TAG }}-previous" --image-manifest "$MANIFEST" + fi + - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api IMAGE_TAG: ${{ github.sha }} run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:production-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:production-latest + docker build -t $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG . + docker push $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG + docker tag $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:$IMAGE_TAG $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:${{ env.ENV_TAG }}-latest + docker push $ECR_REGISTRY/${{ env.ECR_REPOSITORY }}:${{ env.ENV_TAG }}-latest + - name: Force ECS Update run: | - aws ecs update-service --cluster discovery-api-production --service discovery-api-production --force-new-deployment + aws ecs update-service --cluster discovery-api-${{ env.ENV_TAG }} --service discovery-api-${{ env.ENV_TAG }} --force-new-deployment \ No newline at end of file diff --git a/test/integration/delivery-locations-by-barcode.test.js b/test/integration/delivery-locations-by-barcode.test.js index 2302b12b..9da42420 100644 --- a/test/integration/delivery-locations-by-barcode.test.js +++ b/test/integration/delivery-locations-by-barcode.test.js @@ -1,17 +1,25 @@ -require('dotenv').config('config/qa.env') -const axios = require('axios') +const { loadConfig } = require('../../lib/load-config') const { expectations, ptypes } = require('./delivery-locations-constants') +const { makeNyplDataApiClient } = require('../../lib/data-api-client') const checkLocationsForPtype = async (ptype) => { const problems = [] const match = [] - await Promise.all(Object.values(expectations).map(async (expectation) => { - const deliveryLocationsFromApi = await getDeliveryLocations(expectation.barcode, ptypes[ptype]) + + await Promise.all(Object.entries(expectations).map(async ([holdingLocation, expectation], i) => { + let deliveryLocationsFromApi let totalMatch = true const registerProblem = (problem) => { - problems.push({ barcode: expectation.barcode, deliveryLocationsFromApi, ...problem }) + problems.push({ holdingLocation, barcode: expectation.barcode, deliveryLocationsFromApi, ...problem }) totalMatch = false } + try { + deliveryLocationsFromApi = await getDeliveryLocations(expectation.barcode, ptypes[ptype]) + } catch (e) { + registerProblem({ lookUpFailed: true }) + return + } + const checkForValue = (expectedValue, action) => { const includedValueIncluded = deliveryLocationsFromApi.some((label) => label.includes(expectedValue)) const match = action === 'include' ? includedValueIncluded : !includedValueIncluded @@ -27,20 +35,31 @@ const checkLocationsForPtype = async (ptype) => { } const getDeliveryLocations = async (barcode, patronId) => { - const { data: { itemListElement: deliveryLocationsPerRecord } } = await axios.get(`http://localhost:8082/api/v0.1/request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) - // per record - return deliveryLocationsPerRecord[0] - .deliveryLocation.map(loc => loc.prefLabel.toLowerCase()) + try { + // const { data: { itemListElement: deliveryLocationsPerRecord } } = await axios.get(`https://${process.env === 'qa' ? 'qa-' : ''}platform.nypl.org/api/v0.1/request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) + const { itemListElement: itemData } = await makeNyplDataApiClient().get(`request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) + // per record + return itemData[0] + .deliveryLocation.map(loc => loc.prefLabel.toLowerCase()) + } catch (e) { + console.error(e) + } } const theThing = async () => { + await loadConfig() const results = await Promise.all(Object.keys(ptypes).map((checkLocationsForPtype))) - Object.keys(ptypes).forEach((ptype, i) => { + const resultsHaveProblems = Object.keys(ptypes).some((ptype, i) => { const resultsForPtype = results[i] if (resultsForPtype.problems.length) { console.error(`Error with ${ptype} ptype delivery results, `, resultsForPtype.problems) - } else console.log(`All delivery location checks for ${ptype} patron type successful`) + return true + } else { + console.log(`All delivery location checks for ${ptype} patron type successful`) + return false + } }) + if (resultsHaveProblems) throw new Error('Delivery location checks failed.') } theThing() diff --git a/test/integration/delivery-locations-constants.js b/test/integration/delivery-locations-constants.js index b46d812c..bf079333 100644 --- a/test/integration/delivery-locations-constants.js +++ b/test/integration/delivery-locations-constants.js @@ -38,11 +38,11 @@ const expectations = { scholar: { includes: [schomburg], excludes: [scholar, sasb, lpa] }, general: { includes: [schomburg], excludes: [scholar, sasb, lpa] } }, - // nyplM1: { - // barcode: null, - // scholar: { includes: [sasb], excludes: [scholar, lpa, schomburg] }, - // general: { includes: [sasb], excludes: [scholar, lpa, schomburg] } - // }, + nyplM1: { + barcode: '33433084847221', + scholar: { includes: [sasb], excludes: [scholar, lpa, schomburg] }, + general: { includes: [sasb], excludes: [scholar, lpa, schomburg] } + }, nyplM2: { barcode: '33333069027734', scholar: { includes: [sasb, scholar], excludes: [lpa, schomburg] }, From 3aa92cd32e70effed78a390442e75267a1e8f570 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 16 Mar 2026 15:32:56 -0400 Subject: [PATCH 17/40] add int test file --- .github/workflows/integration-tests.yml | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/integration-tests.yml diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 00000000..f9d5f765 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,30 @@ +name: integration-test + +on: + pull_request: + branches: + - production + +jobs: + integration-test: + permissions: + id-token: write + contents: read + runs-on: ubuntu-latest + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole + aws-region: us-east-1 + - uses: actions/checkout@v3 + - name: Set Node version + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + - name: npm install + run: npm i + - name: Run integration tests + env: + ENV: 'qa' + run: node test/integration/delivery-locations-by-barcode.test.js From b62d8467b3c72929707e0bf000de2a98d0341096 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Mon, 16 Mar 2026 15:42:38 -0400 Subject: [PATCH 18/40] rm commented code --- test/integration/delivery-locations-by-barcode.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/delivery-locations-by-barcode.test.js b/test/integration/delivery-locations-by-barcode.test.js index 9da42420..23245370 100644 --- a/test/integration/delivery-locations-by-barcode.test.js +++ b/test/integration/delivery-locations-by-barcode.test.js @@ -36,7 +36,6 @@ const checkLocationsForPtype = async (ptype) => { const getDeliveryLocations = async (barcode, patronId) => { try { - // const { data: { itemListElement: deliveryLocationsPerRecord } } = await axios.get(`https://${process.env === 'qa' ? 'qa-' : ''}platform.nypl.org/api/v0.1/request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) const { itemListElement: itemData } = await makeNyplDataApiClient().get(`request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) // per record return itemData[0] From 8f07bfca68f5a14e16f2f3be644b1e1aa1b3e39c Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Wed, 18 Mar 2026 10:44:56 -0400 Subject: [PATCH 19/40] test From f53c870f957abed778a3db124e95f9b9b3b6ef66 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 19 Mar 2026 12:49:24 -0400 Subject: [PATCH 20/40] expect scholar delivery for m1 item/scholar --- test/integration/delivery-locations-constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/delivery-locations-constants.js b/test/integration/delivery-locations-constants.js index bf079333..6b70cfd7 100644 --- a/test/integration/delivery-locations-constants.js +++ b/test/integration/delivery-locations-constants.js @@ -40,7 +40,7 @@ const expectations = { }, nyplM1: { barcode: '33433084847221', - scholar: { includes: [sasb], excludes: [scholar, lpa, schomburg] }, + scholar: { includes: [sasb, scholar], excludes: [lpa, schomburg] }, general: { includes: [sasb], excludes: [scholar, lpa, schomburg] } }, nyplM2: { From 2ede4c46046034c4ece30b53d9e2f994b89d09e3 Mon Sep 17 00:00:00 2001 From: danamansana Date: Wed, 1 Apr 2026 11:57:30 -0400 Subject: [PATCH 21/40] Allow alphanumeric characters in itemUri --- routes/resources.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/resources.js b/routes/resources.js index 9b7e394f..171393ab 100644 --- a/routes/resources.js +++ b/routes/resources.js @@ -90,7 +90,7 @@ module.exports = function (app) { * * e.g. discovery/resources/b1234-i9876 */ - app.get(`/api/v${VER}/discovery/resources/:uri-:itemUri([a-z]?i[0-9]+)`, function (req, res, next) { + app.get(`/api/v${VER}/discovery/resources/:uri-:itemUri([a-z]?i[a-z0-9]+)`, function (req, res, next) { const params = { uri: req.params.uri, itemUri: req.params.itemUri } return app.resources.findByUri(params, { baseUrl: app.baseUrl }, req) From 795c6438718c571a0385e97ea1743d6d0c44f5d1 Mon Sep 17 00:00:00 2001 From: Ian O'Connor Date: Thu, 2 Apr 2026 13:50:59 -0400 Subject: [PATCH 22/40] Ignore case in search for roles in contributor browse --- lib/contributors.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/contributors.js b/lib/contributors.js index 4ccf874c..90cdeb15 100644 --- a/lib/contributors.js +++ b/lib/contributors.js @@ -106,8 +106,20 @@ const buildElasticRoleCountQuery = function (contributorList) { return { size: 0, query: { - terms: { - browseableContributorRole_packed: contributorList + bool: { + should: [ + { + terms: { + 'creatorLiteral.keywordLowercased': contributorList + } + }, + { + terms: { + 'contributorLiteral.keywordLowercased': contributorList + } + } + ], + minimum_should_match: 1 } }, aggs: { From fc42be2d0012651fbb60c3670b8697d63ce39e4a Mon Sep 17 00:00:00 2001 From: Ian O'Connor Date: Wed, 8 Apr 2026 16:09:13 -0400 Subject: [PATCH 23/40] Update resources index to resources-prod-2026-04-08 --- config/production.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/production.env b/config/production.env index 6811e45f..43da8308 100644 --- a/config/production.env +++ b/config/production.env @@ -1,5 +1,5 @@ ENCRYPTED_ELASTICSEARCH_URI=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJYwgZMGCSqGSIb3DQEHBqCBhTCBggIBADB9BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDFWw8ECX9Pz81z0kvAIBEIBQGec9PCpwuvEgLH6imhqP6tx1fj8Vlf2ZipnUy06jzmpE262Qvk9LPAq7sIYPVkTCZctwilwcU9oC6yxasVoUlK87la77v03CeZsPIDwciFY= -ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHcwdQYJKoZIhvcNAQcGoGgwZgIBADBhBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGOwQk67wE9nnptNhgIBEIA0olICpuUjppBGfOeFMWkmnElVd6qfJXPlgezZDu08t+rt4kQzWBuK7DALwhCeGNl45UpfGg== +ENCRYPTED_RESOURCES_INDEX=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHcwdQYJKoZIhvcNAQcGoGgwZgIBADBhBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDAk2mOM+zTrPU2rOagIBEIA0E3OuraTI5i3rqAiNrAj4RKV/c6DqotQ5nqSP8TzKk0qZYlHz+1jua11ZtKHIJ9LhszaE4Q== ENCRYPTED_ELASTICSEARCH_API_KEY=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAJ4wgZsGCSqGSIb3DQEHBqCBjTCBigIBADCBhAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAyPOPaQCBbvKQhJoPQCARCAV2TlWlRh+xKnCegpprEQgfldZGcVW48RND0LVd/pQpVTJnRTtbCpP7damT7k8ziJVdWZ3jsfs5fw5YnKc/EIQ1M//DRUzOJL98ir5LTTxE7QhflKDtUY+Q== ENCRYPTED_SCSB_URL=AQECAHh7ea2tyZ6phZgT4B9BDKwguhlFtRC6hgt+7HbmeFsrsgAAAHwwegYJKoZIhvcNAQcGoG0wawIBADBmBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDKPFC8wFkVM5CyT6VQIBEIA5m4eLBkpChRA//ZNEWsRqIDGZmevb/thzI03a0NiAW6VfybSAYpFthh+bj/yAk1VEEBF6r1T4A2GP From 45ebe0260e2247e3b364208b85f045d58b06e48f Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:27:40 -0400 Subject: [PATCH 24/40] use wait for stable ecs --- .github/workflows/integration-tests.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f9d5f765..e4485da8 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,9 +1,14 @@ -name: integration-test +name: Run Smoke Tests on: - pull_request: - branches: - - production + workflow_run: + workflows: ["Deploy to QA"] + types: + - completed + +permissions: + id-token: write + contents: read jobs: integration-test: @@ -17,6 +22,12 @@ jobs: with: role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole aws-region: us-east-1 + - name: Wait for stable service + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + timeout_minutes: 10 + command: aws ecs wait services-stable --cluster collections-api-qa --services collections-api-qa - uses: actions/checkout@v3 - name: Set Node version uses: actions/setup-node@v3 From 5e872a7bb67e629def08f6755a2529a43c3a5005 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:28:56 -0400 Subject: [PATCH 25/40] replace service name --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e4485da8..bbe65dd5 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -27,7 +27,7 @@ jobs: with: max_attempts: 3 timeout_minutes: 10 - command: aws ecs wait services-stable --cluster collections-api-qa --services collections-api-qa + command: aws ecs wait services-stable --cluster discovery-api-qa --services discovery-api-qa - uses: actions/checkout@v3 - name: Set Node version uses: actions/setup-node@v3 From 9cdb57046cffd2bcaede734cd5a4478948746643 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:32:05 -0400 Subject: [PATCH 26/40] generalize --- .github/workflows/integration-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index bbe65dd5..75205de5 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -2,7 +2,7 @@ name: Run Smoke Tests on: workflow_run: - workflows: ["Deploy to QA"] + workflows: ["Unit tests / deploy"] types: - completed @@ -27,7 +27,7 @@ jobs: with: max_attempts: 3 timeout_minutes: 10 - command: aws ecs wait services-stable --cluster discovery-api-qa --services discovery-api-qa + command: aws ecs wait services-stable --cluster discovery-api-${{github.ref_name}} --services discovery-api-${{github.ref_name}} - uses: actions/checkout@v3 - name: Set Node version uses: actions/setup-node@v3 From 0fb6eb54802a4f80276989310cf4a5d454b7d2b8 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:37:09 -0400 Subject: [PATCH 27/40] extract tests --- .../{test-and-deploy.yml => deploy.yml} | 17 ++++------------- .github/workflows/integration-tests.yml | 2 +- .github/workflows/test.yml | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 14 deletions(-) rename .github/workflows/{test-and-deploy.yml => deploy.yml} (87%) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/deploy.yml similarity index 87% rename from .github/workflows/test-and-deploy.yml rename to .github/workflows/deploy.yml index a179ba4b..b955de12 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Unit Tests +name: Deploy on: [push] @@ -8,18 +8,9 @@ env: ECR_REPOSITORY: discovery-api jobs: - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set Node version - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - - name: npm install - run: npm ci - - name: Unit Tests - run: npm test + run_tests: + name: Run Tests + uses: ./.github/workflows/tests.yml deploy: permissions: diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 75205de5..49dd97e0 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -2,7 +2,7 @@ name: Run Smoke Tests on: workflow_run: - workflows: ["Unit tests / deploy"] + workflows: ["Deploy"] types: - completed diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..35a09108 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,17 @@ +name: Unit Tests + +on: [push] + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set Node version + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + - name: npm install + run: npm ci + - name: Unit Tests + run: npm test \ No newline at end of file From 8fd3841ab10f8af123b429b197d7e74cda11df78 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:39:55 -0400 Subject: [PATCH 28/40] restrict perms --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35a09108..13418e43 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,9 @@ on: [push] jobs: tests: + permissions: + id-token: write + contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 7bfc73ae440496aeecdbe35efe772ca5ca017634 Mon Sep 17 00:00:00 2001 From: Vera Kahn <86011349+charmingduchess@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:40:42 -0400 Subject: [PATCH 29/40] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35a09108..74d1e6ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,9 @@ name: Unit Tests on: [push] +permissions: + contents: read + jobs: tests: runs-on: ubuntu-latest From bedab5944d9d6ccb9a4ba01246e4022199dbd738 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:41:58 -0400 Subject: [PATCH 30/40] fix broken test reference --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b955de12..a0176ea6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,7 +8,7 @@ env: ECR_REPOSITORY: discovery-api jobs: - run_tests: + tests: name: Run Tests uses: ./.github/workflows/tests.yml From 35d6214f7dfd119a2669e8d75d7fc1067e462c32 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:45:41 -0400 Subject: [PATCH 31/40] fix test ref --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a0176ea6..6c09bee9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,7 +10,7 @@ env: jobs: tests: name: Run Tests - uses: ./.github/workflows/tests.yml + uses: ./.github/workflows/test.yml deploy: permissions: From 4d50db131cc20b205b357569e3c623ce4e203f68 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:49:13 -0400 Subject: [PATCH 32/40] add workflow call --- .github/workflows/deploy.yml | 9 ++++++--- .github/workflows/test.yml | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6c09bee9..a89f2d98 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,11 @@ name: Deploy -on: [push] +on: + push: + branches: + - qa + - production + - qa2 # Global environment variable based on the branch name env: @@ -18,8 +23,6 @@ jobs: contents: read runs-on: ubuntu-latest needs: tests - # Only run if it's one of our three deployment branches - if: contains(fromJSON('["qa", "qa2", "production"]'), github.ref_name) steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9b7a678e..786584b5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,8 @@ name: Unit Tests -on: [push] +on: + workflow_call: + push: permissions: contents: read From 584011ddfe35b9cf0f11b5c5c686bf2c78f69f7a Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 16:54:23 -0400 Subject: [PATCH 33/40] checkout repo --- .github/workflows/integration-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 49dd97e0..fdb1b50c 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -17,6 +17,8 @@ jobs: contents: read runs-on: ubuntu-latest steps: + - name: Checkout repo + uses: actions/checkout@v3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: From b46b4fde0352b90f1f0ae1d3129fd1b382cde4bb Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 17:19:48 -0400 Subject: [PATCH 34/40] head branch --- .github/workflows/integration-tests.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index fdb1b50c..e8ca15a4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -3,13 +3,16 @@ name: Run Smoke Tests on: workflow_run: workflows: ["Deploy"] - types: - - completed + types: [completed] + branches: [production, qa] permissions: id-token: write contents: read +env: + ENV: ${{ github.event.workflow_run.head_branch}} + jobs: integration-test: permissions: @@ -17,8 +20,6 @@ jobs: contents: read runs-on: ubuntu-latest steps: - - name: Checkout repo - uses: actions/checkout@v3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: From 3b23b3eca382127fbd71ef08b3e56218ce8e64ae Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Thu, 9 Apr 2026 17:23:51 -0400 Subject: [PATCH 35/40] spaghetti --- .github/workflows/integration-tests.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e8ca15a4..eb0bc040 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -11,7 +11,7 @@ permissions: contents: read env: - ENV: ${{ github.event.workflow_run.head_branch}} + ENV: ${{ github.event.workflow_run.head_branch }} jobs: integration-test: @@ -30,15 +30,15 @@ jobs: with: max_attempts: 3 timeout_minutes: 10 - command: aws ecs wait services-stable --cluster discovery-api-${{github.ref_name}} --services discovery-api-${{github.ref_name}} + command: aws ecs wait services-stable --cluster discovery-api-${{env.ENV}} --services discovery-api-${{env.ENV}} - uses: actions/checkout@v3 - name: Set Node version uses: actions/setup-node@v3 with: - node-version-file: '.nvmrc' + node-version-file: ".nvmrc" - name: npm install run: npm i - name: Run integration tests - env: - ENV: 'qa' + env: + ENV: "qa" run: node test/integration/delivery-locations-by-barcode.test.js From 3316e8fc5a579ca00b608211502f86b74bf009b5 Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 10 Apr 2026 09:26:19 -0400 Subject: [PATCH 36/40] trigger rollback with integration-test failure --- .github/workflows/integration-tests.yml | 2 +- .github/workflows/rollback-qa2.yml | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index eb0bc040..d0634cb7 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -4,7 +4,7 @@ on: workflow_run: workflows: ["Deploy"] types: [completed] - branches: [production, qa] + branches: [production, qa, qa2] permissions: id-token: write diff --git a/.github/workflows/rollback-qa2.yml b/.github/workflows/rollback-qa2.yml index 925d8b4f..6d54f636 100644 --- a/.github/workflows/rollback-qa2.yml +++ b/.github/workflows/rollback-qa2.yml @@ -1,7 +1,12 @@ name: Rollback qa2 -on: - workflow_dispatch: +on: + workflow_run: + workflows: ["Run Smoke Test"] + types: [completed] + +env: + ENV: ${{ github.event.workflow_run.head_branch }} jobs: # Rollback job in case of failure (Revert qa to the previous task definition) @@ -11,6 +16,7 @@ jobs: contents: read name: Rollback to Previous Version runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' && env.ENV == 'qa2'}} steps: - name: Checkout code uses: actions/checkout@v4 From a19d88f36031f445a56944afc0a71cfdfdbf70ef Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 10 Apr 2026 09:27:10 -0400 Subject: [PATCH 37/40] ensure int test failure --- .../delivery-locations-by-barcode.test.js | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/test/integration/delivery-locations-by-barcode.test.js b/test/integration/delivery-locations-by-barcode.test.js index 23245370..2a8fa699 100644 --- a/test/integration/delivery-locations-by-barcode.test.js +++ b/test/integration/delivery-locations-by-barcode.test.js @@ -46,19 +46,20 @@ const getDeliveryLocations = async (barcode, patronId) => { } const theThing = async () => { - await loadConfig() - const results = await Promise.all(Object.keys(ptypes).map((checkLocationsForPtype))) - const resultsHaveProblems = Object.keys(ptypes).some((ptype, i) => { - const resultsForPtype = results[i] - if (resultsForPtype.problems.length) { - console.error(`Error with ${ptype} ptype delivery results, `, resultsForPtype.problems) - return true - } else { - console.log(`All delivery location checks for ${ptype} patron type successful`) - return false - } - }) - if (resultsHaveProblems) throw new Error('Delivery location checks failed.') + throw new Error('spaghetti') + // await loadConfig() + // const results = await Promise.all(Object.keys(ptypes).map((checkLocationsForPtype))) + // const resultsHaveProblems = Object.keys(ptypes).some((ptype, i) => { + // const resultsForPtype = results[i] + // if (resultsForPtype.problems.length) { + // console.error(`Error with ${ptype} ptype delivery results, `, resultsForPtype.problems) + // return true + // } else { + // console.log(`All delivery location checks for ${ptype} patron type successful`) + // return false + // } + // }) + // if (resultsHaveProblems) throw new Error('Delivery location checks failed.') } theThing() From 4ba6d0d8718668ac9f638da4f03d97db74ab5f9b Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 10 Apr 2026 09:28:44 -0400 Subject: [PATCH 38/40] failure to pass linting --- .../delivery-locations-by-barcode.test.js | 114 +++++++++--------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/test/integration/delivery-locations-by-barcode.test.js b/test/integration/delivery-locations-by-barcode.test.js index 2a8fa699..c50f1d0b 100644 --- a/test/integration/delivery-locations-by-barcode.test.js +++ b/test/integration/delivery-locations-by-barcode.test.js @@ -1,65 +1,69 @@ -const { loadConfig } = require('../../lib/load-config') -const { expectations, ptypes } = require('./delivery-locations-constants') -const { makeNyplDataApiClient } = require('../../lib/data-api-client') +// const { loadConfig } = require('../../lib/load-config') +// const { expectations, ptypes } = require('./delivery-locations-constants') +// const { makeNyplDataApiClient } = require('../../lib/data-api-client') -const checkLocationsForPtype = async (ptype) => { - const problems = [] - const match = [] +// const checkLocationsForPtype = async (ptype) => { +// const problems = [] +// const match = [] - await Promise.all(Object.entries(expectations).map(async ([holdingLocation, expectation], i) => { - let deliveryLocationsFromApi - let totalMatch = true - const registerProblem = (problem) => { - problems.push({ holdingLocation, barcode: expectation.barcode, deliveryLocationsFromApi, ...problem }) - totalMatch = false - } - try { - deliveryLocationsFromApi = await getDeliveryLocations(expectation.barcode, ptypes[ptype]) - } catch (e) { - registerProblem({ lookUpFailed: true }) - return - } +// await Promise.all(Object.entries(expectations).map(async ([holdingLocation, expectation], i) => { +// let deliveryLocationsFromApi +// let totalMatch = true +// const registerProblem = (problem) => { +// problems.push({ holdingLocation, barcode: expectation.barcode, deliveryLocationsFromApi, ...problem }) +// totalMatch = false +// } +// try { +// deliveryLocationsFromApi = await getDeliveryLocations(expectation.barcode, ptypes[ptype]) +// } catch (e) { +// registerProblem({ lookUpFailed: true }) +// return +// } - const checkForValue = (expectedValue, action) => { - const includedValueIncluded = deliveryLocationsFromApi.some((label) => label.includes(expectedValue)) - const match = action === 'include' ? includedValueIncluded : !includedValueIncluded - if (!match) { - registerProblem({ [`expectedTo${action}`]: expectedValue }) - } - } - expectation[ptype].includes.forEach((expectedValue) => checkForValue(expectedValue, 'include')) - expectation[ptype].excludes.forEach((expectedValue) => checkForValue(expectedValue, 'exclude')) - if (totalMatch) match.push({ barcode: expectation.barcode, deliveryLocationsFromApi, expectedToInclude: expectation[ptype].includes, expectedToExclude: expectation[ptype].excludes }) - })) - return { match, problems } -} +// const checkForValue = (expectedValue, action) => { +// const includedValueIncluded = deliveryLocationsFromApi.some((label) => label.includes(expectedValue)) +// const match = action === 'include' ? includedValueIncluded : !includedValueIncluded +// if (!match) { +// registerProblem({ [`expectedTo${action}`]: expectedValue }) +// } +// } +// expectation[ptype].includes.forEach((expectedValue) => checkForValue(expectedValue, 'include')) +// expectation[ptype].excludes.forEach((expectedValue) => checkForValue(expectedValue, 'exclude')) +// if (totalMatch) match.push({ barcode: expectation.barcode, deliveryLocationsFromApi, expectedToInclude: expectation[ptype].includes, expectedToExclude: expectation[ptype].excludes }) +// })) +// return { match, problems } +// } -const getDeliveryLocations = async (barcode, patronId) => { - try { - const { itemListElement: itemData } = await makeNyplDataApiClient().get(`request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) - // per record - return itemData[0] - .deliveryLocation.map(loc => loc.prefLabel.toLowerCase()) - } catch (e) { - console.error(e) - } -} +// const getDeliveryLocations = async (barcode, patronId) => { +// try { +// const { itemListElement: itemData } = await makeNyplDataApiClient().get(`request/deliveryLocationsByBarcode?barcodes[]=${barcode}&patronId=${patronId}`) +// // per record +// return itemData[0] +// .deliveryLocation.map(loc => loc.prefLabel.toLowerCase()) +// } catch (e) { +// console.error(e) +// } +// } + +// const theThing = async () => { +// throw new Error('spaghetti') +// await loadConfig() +// const results = await Promise.all(Object.keys(ptypes).map((checkLocationsForPtype))) +// const resultsHaveProblems = Object.keys(ptypes).some((ptype, i) => { +// const resultsForPtype = results[i] +// if (resultsForPtype.problems.length) { +// console.error(`Error with ${ptype} ptype delivery results, `, resultsForPtype.problems) +// return true +// } else { +// console.log(`All delivery location checks for ${ptype} patron type successful`) +// return false +// } +// }) +// if (resultsHaveProblems) throw new Error('Delivery location checks failed.') +// } const theThing = async () => { throw new Error('spaghetti') - // await loadConfig() - // const results = await Promise.all(Object.keys(ptypes).map((checkLocationsForPtype))) - // const resultsHaveProblems = Object.keys(ptypes).some((ptype, i) => { - // const resultsForPtype = results[i] - // if (resultsForPtype.problems.length) { - // console.error(`Error with ${ptype} ptype delivery results, `, resultsForPtype.problems) - // return true - // } else { - // console.log(`All delivery location checks for ${ptype} patron type successful`) - // return false - // } - // }) - // if (resultsHaveProblems) throw new Error('Delivery location checks failed.') } theThing() From 69d493e991f7d52be039e55a10ea9aa6620f579e Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 10 Apr 2026 09:38:51 -0400 Subject: [PATCH 39/40] add yml to remove in qa2 merge conflict --- .github/workflows/test-and-deploy.yml | 139 ++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 .github/workflows/test-and-deploy.yml diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml new file mode 100644 index 00000000..415ac41b --- /dev/null +++ b/.github/workflows/test-and-deploy.yml @@ -0,0 +1,139 @@ +name: Unit Tests + +on: [push] + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set Node version + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + - name: npm install + run: npm ci + - name: Unit Tests + run: npm test + deploy-qa: + permissions: + id-token: write + contents: read + runs-on: ubuntu-latest + needs: + - tests + if: github.ref == 'refs/heads/qa' + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole + aws-region: us-east-1 + - name: Log in to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + - name: Back up previous image for rollback + env: + ECR_REPOSITORY: discovery-api + run: | + MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') + PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') + if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa-previous" --image-manifest "$MANIFEST"; fi + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: discovery-api + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest + docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest + - name: Force ECS Update + run: | + aws ecs update-service --cluster discovery-api-qa --service discovery-api-qa --force-new-deployment + deploy-qa2: + permissions: + id-token: write + contents: read + runs-on: ubuntu-latest + needs: + - tests + if: github.ref == 'refs/heads/qa2' + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole + aws-region: us-east-1 + + - name: Log in to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + - name: Back up previous image for rollback + env: + ECR_REPOSITORY: discovery-api + run: | + MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') + PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') + if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa2-previous" --image-manifest "$MANIFEST"; fi + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: discovery-api + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest + docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest + + - name: Force ECS Update + run: | + aws ecs update-service --cluster discovery-api-qa2 --service discovery-api-qa2 --force-new-deployment + deploy-production: + permissions: + id-token: write + contents: read + runs-on: ubuntu-latest + needs: tests + if: github.ref == 'refs/heads/production' + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole + aws-region: us-east-1 + + - name: Log in to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Back up previous image for rollback + env: + ECR_REPOSITORY: discovery-api + run: | + MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') + PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') + if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "production-previous" --image-manifest "$MANIFEST"; fi + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: discovery-api + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:production-latest + docker push $ECR_REGISTRY/$ECR_REPOSITORY:production-latest + - name: Force ECS Update + run: | + aws ecs update-service --cluster discovery-api-production --service discovery-api-production --force-new-deployment From a8df84b23d00fb59003ed50c2ff69d49d42e93cf Mon Sep 17 00:00:00 2001 From: Vera Kahn Date: Fri, 10 Apr 2026 09:39:23 -0400 Subject: [PATCH 40/40] rm test and deploy --- .github/workflows/test-and-deploy.yml | 139 -------------------------- 1 file changed, 139 deletions(-) delete mode 100644 .github/workflows/test-and-deploy.yml diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml deleted file mode 100644 index 415ac41b..00000000 --- a/.github/workflows/test-and-deploy.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: Unit Tests - -on: [push] - -jobs: - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set Node version - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - - name: npm install - run: npm ci - - name: Unit Tests - run: npm test - deploy-qa: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa-latest - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa --service discovery-api-qa --force-new-deployment - deploy-qa2: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: - - tests - if: github.ref == 'refs/heads/qa2' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="qa2-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "qa2-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:qa2-latest - - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-qa2 --service discovery-api-qa2 --force-new-deployment - deploy-production: - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - needs: tests - if: github.ref == 'refs/heads/production' - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - role-to-assume: arn:aws:iam::946183545209:role/GithubActionsDeployerRole - aws-region: us-east-1 - - - name: Log in to ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Back up previous image for rollback - env: - ECR_REPOSITORY: discovery-api - run: | - MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-latest" --output json | jq --raw-output --join-output '.images[0].imageManifest') - PREVIOUS_MANIFEST=$(aws ecr batch-get-image --repository-name $ECR_REPOSITORY --image-ids imageTag="production-previous" --output json | jq --raw-output --join-output '.images[0].imageManifest') - if [ "$MANIFEST" != "$PREVIOUS_MANIFEST" ]; then aws ecr put-image --repository-name $ECR_REPOSITORY --image-tag "production-previous" --image-manifest "$MANIFEST"; fi - - name: Build, tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: discovery-api - IMAGE_TAG: ${{ github.sha }} - run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:production-latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:production-latest - - name: Force ECS Update - run: | - aws ecs update-service --cluster discovery-api-production --service discovery-api-production --force-new-deployment