0

Background:

I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).

If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.

One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.

Question:

Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:

def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
  parameters.foreach(p =>
    // Tell the handler to handle this specific parameter
    case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
      someResultFunction(p)
  )
}

Is this possible in Acolyte? If so, please provide an example.

Cities
  • 163
  • 1
  • 11
  • Please avoid to start a question by your own conclusion ("The inability to dynamically ..."), moreover in a quite negative way. That's far from the best way to ask. You state how you intend to solve the situation, without explaining what's the situation and need (XY question). – cchantep Oct 09 '18 at 20:07
  • I updated the question that qualifies the statement "The inability to dynamically...". However, I'm not sure what you mean by "You state how you intend to solve the situation, without explaining what's the situation and need". I stated "Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses." (the situation). I assumed that the need (keeping code DRY) was self-evident, but I will edit the question to include that as well. – Cities Oct 09 '18 at 20:12
  • You don't state what is the use case, but how you think it should be solved ([XY question](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)). – cchantep Oct 09 '18 at 20:39
  • Can you explain how "I want to be able to encapsulate the logic for matching queries and generating their responses" does not qualify as a use case? If this is an XY question, I may need some help with this aspect. Perhaps I was too general? My use case is this: I am using Acolyte to mock SQL responses that vary across `accountId` and one other parameter; the only thing that changes for each mocked `QueryExecution` and response is the `accountId` and this other parameter; I want to write the logic for matching this query and generating this response once. – Cities Oct 09 '18 at 20:53
  • That's a basic pattern matching use case, as once the value for the `accountId` is extracted for the various corresponding execution, it's possible to (dynamically) return the result according the actual value. If you have a look at the documentation, there are various examples. I think you should first read some Scala tutorial about pattern matching. – cchantep Oct 09 '18 at 21:20
  • Thanks for the tip. I added an answer that utilizes pattern matching. – Cities Oct 11 '18 at 14:30

1 Answers1

0

It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.

See the code below for an example:

import java.sql.DriverManager

import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec

class AcolyteTest extends FunSpec {
  describe("Using pattern matching to extract a query parameter") {
    it("should extract the parameter and make it usable for dynamic result returning") {
      val query = "SELECT someresult FROM someDB WHERE id = ?"

      val rows = RowLists.rowList1(classOf[String] -> "someresult")

      val handlerName = "testOneHandler"
      val handler = AcolyteDSL.handleQuery {
        case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
          rows.append(id.toString)
      }

      Driver.register(handlerName, handler)
      val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
      val preparedStatement = connection.prepareStatement(query)

      preparedStatement.setString(1, "hello world")
      val resultSet = preparedStatement.executeQuery()    
      resultSet.next()    
      assertResult(resultSet.getString(1))("hello world")

    }

    it("should support a slightly more complex example") {
      val firstResult = "The first result"
      val secondResult = "The second result"

      val query = "SELECT someresult FROM someDB WHERE id = ?"

      val rows = RowLists.rowList1(classOf[String] -> "someresult")

      val results: Map[String, RowList1.Impl[String]] = Map(
        "one" -> rows.append(firstResult),
        "two" -> rows.append(secondResult)
      )

      def getResult(parameter: String): QueryResult = {
        results.get(parameter) match {
          case Some(row) => row.asResult()
          case _ => acolyte.jdbc.QueryResult.Nil
        }
      }

      val handlerName = "testTwoHandler"
      val handler = AcolyteDSL.handleQuery {
        case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
          getResult(id.toString)
      }

      Driver.register(handlerName, handler)
      val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
      val preparedStatement = connection.prepareStatement(query)

      preparedStatement.setString(1, "one")
      val resultSetOne = preparedStatement.executeQuery()
      resultSetOne.next()
      assertResult(resultSetOne.getString(1))(firstResult)

      preparedStatement.setString(1, "two")
      val resultSetTwo = preparedStatement.executeQuery()
      resultSetTwo.next()
      assertResult(resultSetTwo.getString(1))(secondResult)
    }
  }
}
Cities
  • 163
  • 1
  • 11