Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return empty results when searching for a nonexistent collection #185

Merged
merged 12 commits into from
Feb 23, 2022
17 changes: 16 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,20 @@
"consistent"
],
"import/extensions": "off"
}
},
"overrides": [
{
"files": [
"test-*.js"
],
"rules": {
"max-len": [
"error",
{
"ignoreStrings": true
}
]
}
}
]
}
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Fixed

- open-ended datetime intervals using either empty string or '..' now work
- Open-ended datetime intervals using either empty string or '..' now work
- Correct content types are now returned
- Searching for a nonexistent collection returns empty results

### Changed

Expand Down
35 changes: 35 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"js-yaml": "^3.13.1",
"memorystream": "^0.3.1",
"morgan": "^1.10.0",
"p-filter": "^2.0.0",
"pump": "^3.0.0",
"serverless-http": "^2.7.0",
"through2": "^3.0.1"
Expand Down
7 changes: 7 additions & 0 deletions src/lambdas/api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ app.get('/', async (req, res, next) => {

app.get('/api', async (_req, res, next) => {
try {
res.type('application/vnd.oai.openapi')
const spec = await readYaml(path.join(__dirname, 'api.yaml'))
res.json(spec)
} catch (error) {
Expand All @@ -77,6 +78,7 @@ app.get('/conformance', async (_req, res, next) => {

app.get('/search', async (req, res, next) => {
try {
res.type('application/geo+json')
res.json(await api.searchItems(null, req.query, satlib.es, req.endpoint, 'GET'))
} catch (error) {
next(error)
Expand All @@ -85,6 +87,7 @@ app.get('/search', async (req, res, next) => {

app.post('/search', async (req, res, next) => {
try {
res.type('application/geo+json')
res.json(await api.searchItems(null, req.body, satlib.es, req.endpoint, 'POST'))
} catch (error) {
next(error)
Expand Down Expand Up @@ -112,6 +115,7 @@ app.get('/collections/:collectionId', async (req, res, next) => {

app.get('/collections/:collectionId/items', async (req, res, next) => {
try {
res.type('application/geo+json')
res.json(await api.searchItems(
req.params.collectionId,
req.query,
Expand Down Expand Up @@ -140,6 +144,7 @@ app.post('/collections/:collectionId/items', async (req, res, next) => {

app.get('/collections/:collectionId/items/:itemId', async (req, res, next) => {
try {
res.type('application/geo+json')
res.json(await api.getItem(
req.params.collectionId,
req.params.itemId,
Expand Down Expand Up @@ -172,6 +177,8 @@ app.use(
/** @type {ErrorRequestHandler} */ ((err, _req, res, _next) => {
res.status(err.status || 500)

res.type('application/json')

switch (err.status) {
case 404:
res.json({ error: 'Not Found' })
Expand Down
29 changes: 27 additions & 2 deletions src/lib/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const gjv = require('geojson-validation')
const extent = require('@mapbox/extent')
const pFilter = require('p-filter')
const { isIndexNotFoundError, indexExists } = require('./es')
const logger = console

// max number of collections to retrieve
Expand Down Expand Up @@ -377,14 +379,18 @@ const searchItems = async function (collectionId, queryParameters, backend, endp
const ids = extractIds(queryParameters)
const collections = extractCollectionIds(queryParameters)

const collectionsThatExist = Array.isArray(collections)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if we not have to make the calls to check if the indicies exist first before calling the search. I was thinking the fix would be to add allow_no_indices=true to the call to client.search in es.js.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4fd74d3

? await pFilter(collections, indexExists)
: undefined

const parameters = {
datetime,
intersects,
query,
sortby,
fields,
ids,
collections
collections: collectionsThatExist
}

// Keep only existing parameters
Expand All @@ -401,7 +407,26 @@ const searchItems = async function (collectionId, queryParameters, backend, endp
newEndpoint = `${endpoint}/collections/${collectionId}/items`
}
logger.debug(`Search parameters: ${JSON.stringify(searchParameters)}`)
const results = await backend.search(searchParameters, page, limit)

let results
try {
results = await backend.search(searchParameters, page, limit)
} catch (error) {
if (isIndexNotFoundError(error)) {
results = {
context: {
matched: 0,
returned: 0,
page,
limit
},
results: []
}
} else {
throw error
}
}

const { 'results': itemsResults, 'context': itemsMeta } = results
const pageLinks = buildPageLinks(itemsMeta, searchParameters, newEndpoint, httpMethod)
const items = addItemLinks(itemsResults, endpoint)
Expand Down
21 changes: 21 additions & 0 deletions src/lib/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const logger = console //require('./logger')
const COLLECTIONS_INDEX = process.env.COLLECTIONS_INDEX || 'collections'
const ITEMS_INDEX = process.env.ITEMS_INDEX || 'items'

const isIndexNotFoundError = (e) => (
e instanceof Error
&& e.name === 'ResponseError'
&& e.message.includes('index_not_found_exception'))

/*
This module is used for connecting to an Elasticsearch instance, writing records,
searching records, and managing the indexes. It looks for the ES_HOST environment
Expand Down Expand Up @@ -351,9 +356,25 @@ async function search(parameters, page = 1, limit = 10) {
return response
}

/**
* Test if an ES index exists
*
* @param {string} index
* @returns {Promise<boolean>}
*/
const indexExists = async (index) => {
const client = await esClient.client()

const result = await client.indices.exists({ index })

return result.body
}

module.exports = {
getCollection,
getCollections,
indexExists,
isIndexNotFoundError,
search,
editPartialItem,
constructSearchParams,
Expand Down
8 changes: 8 additions & 0 deletions tests/system/test-api-get-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const test = require('ava')
const { apiClient } = require('../helpers/api-client')

test('GET /api has a content type of "application/json', async (t) => {
const response = await apiClient.get('api', { resolveBodyOnly: false })

t.is(response.headers['content-type'], 'application/vnd.oai.openapi; charset=utf-8')
})
Loading