Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/scala/dpla/api/v2/registry/SearchRegistryBehavior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ final case class RegisterRandom(

trait SearchRegistryBehavior {

private val QueryParseErrorMessage = "The q parameter contains invalid search syntax."

def spawnSearchActor(context: ActorContext[SearchRegistryCommand]):
ActorRef[SearchCommand]

Expand Down Expand Up @@ -183,6 +185,10 @@ trait SearchRegistryBehavior {
searchResponse = Some(NotFoundFailure)
possibleSessionResolution

case SearchQueryParseFailure =>
searchResponse = Some(ValidationFailure(QueryParseErrorMessage))
possibleSessionResolution

case SearchFailure =>
searchResponse = Some(InternalFailure)
possibleSessionResolution
Expand Down Expand Up @@ -298,6 +304,10 @@ trait SearchRegistryBehavior {
fetchResponse = Some(NotFoundFailure)
possibleSessionResolution

case SearchQueryParseFailure =>
fetchResponse = Some(InternalFailure)
possibleSessionResolution

case SearchFailure =>
fetchResponse = Some(InternalFailure)
possibleSessionResolution
Expand Down Expand Up @@ -383,6 +393,10 @@ trait SearchRegistryBehavior {
randomResponse = Some(ValidationFailure(message))
possibleSessionResolution

case SearchQueryParseFailure =>
randomResponse = Some(ValidationFailure(QueryParseErrorMessage))
possibleSessionResolution

case SearchFailure =>
randomResponse = Some(InternalFailure)
possibleSessionResolution
Expand Down
14 changes: 10 additions & 4 deletions src/main/scala/dpla/api/v2/search/ElasticSearchClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,11 @@ object ElasticSearchClient {
nextPhase ! SearchQueryResponse(params, body, replyTo)
Behaviors.stopped

case ElasticSearchHttpError(_) =>
replyTo ! SearchFailure
case ElasticSearchHttpError(statusCode) =>
if (statusCode.intValue == 400)
replyTo ! SearchQueryParseFailure
else
replyTo ! SearchFailure
Behaviors.stopped

case ElasticSearchResponseFailure =>
Expand Down Expand Up @@ -385,8 +388,11 @@ object ElasticSearchClient {
nextPhase ! RandomQueryResponse(params, body, replyTo)
Behaviors.stopped

case ElasticSearchHttpError(_) =>
replyTo ! SearchFailure
case ElasticSearchHttpError(statusCode) =>
if (statusCode.intValue == 400)
replyTo ! SearchQueryParseFailure
else
replyTo ! SearchFailure
Behaviors.stopped

case ElasticSearchResponseFailure =>
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/dpla/api/v2/search/SearchProtocol.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ object SearchProtocol {
final case object FetchNotFound extends SearchResponse
final case object SearchNotFound extends SearchResponse
final case object SearchFailure extends SearchResponse
final case object SearchQueryParseFailure extends SearchResponse

/**
* Internal command protocol.
Expand Down
24 changes: 23 additions & 1 deletion src/test/scala/dpla/api/v2/endToEnd/InvalidParamsTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest
import dpla.api.Routes
import dpla.api.helpers.ActorHelper
import dpla.api.helpers.Utils.fakeApiKey
import dpla.api.v2.registry.{MockSmrRegistry, SmrRegistryCommand}
import dpla.api.v2.registry.{MockItemRegistry, MockSmrRegistry, SmrRegistryCommand}
import dpla.api.v2.search.{MockEsClientQueryParseError, MockItemSearch}
import dpla.api.v2.smr.MockSmrRequestHandler
import dpla.api.v2.smr.SmrProtocol.SmrCommand
import org.scalatest.matchers.should.Matchers
Expand All @@ -35,6 +36,17 @@ class InvalidParamsTest extends AnyWordSpec with Matchers
new Routes(itemRegistry, pssRegistry, apiKeyRegistry,
smrRegistryS3Success).applicationRoutes

val esQueryParseErrorClient =
testKit.spawn(MockEsClientQueryParseError())
val itemSearchWithParseError =
MockItemSearch(testKit, Some(esQueryParseErrorClient))
val itemRegistryWithParseError =
MockItemRegistry(testKit, authenticator, itemAnalyticsClient,
Some(itemSearchWithParseError))
val routesWithParseError: Route =
new Routes(itemRegistryWithParseError, pssRegistry, apiKeyRegistry,
smrRegistryS3Success).applicationRoutes

"malformed api_key" should {
"return Forbidden for a key that is too short" in {
val request = Get("/v2/items?api_key=tooshort")
Expand Down Expand Up @@ -93,4 +105,14 @@ class InvalidParamsTest extends AnyWordSpec with Matchers
}
}
}

"Elasticsearch query parse error" should {
"return BadRequest for /v2/items" in {
val request = Get(s"/v2/items?api_key=$fakeApiKey&q=invalid%29-")
request ~> Route.seal(routesWithParseError) ~> check {
status shouldEqual StatusCodes.BadRequest
contentType should === (ContentTypes.`application/json`)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dpla.api.v2.search

import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import dpla.api.v2.search.SearchProtocol.{
IntermediateSearchResult,
RandomQuery,
SearchQuery,
SearchQueryParseFailure
}

object MockEsClientQueryParseError {

def apply(): Behavior[IntermediateSearchResult] = {
Behaviors.receiveMessage[IntermediateSearchResult] {

case SearchQuery(_, _, replyTo) =>
replyTo ! SearchQueryParseFailure
Behaviors.same

case RandomQuery(_, _, replyTo) =>
replyTo ! SearchQueryParseFailure
Behaviors.same

case _ =>
Behaviors.unhandled
}
}
}
Loading