Skip to content
Open
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
51 changes: 40 additions & 11 deletions src/main/scala/com/newlibertie/pollster/DataAdapter.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.newlibertie.pollster

import java.sql.{Connection, DriverManager, ResultSet}
import java.sql.{Connection, DriverManager, ResultSet, SQLException, SQLTimeoutException}

import com.newlibertie.pollster.errorenum.{ApplicationError, DatabaseError}
import com.newlibertie.pollster.impl.Poll
import com.typesafe.scalalogging.LazyLogging
import net.liftweb.json.DefaultFormats
Expand Down Expand Up @@ -30,6 +31,15 @@ object DataAdapter extends LazyLogging {
}
}

private def executeUpdateQuery(sql: String): Any = {
try getConnection.createStatement().executeUpdate(sql)
catch {
case _: SQLException => DatabaseError.Access
case _: SQLTimeoutException => DatabaseError.Timeout
case _: Throwable => ApplicationError.ExceptionError
}
}

def createPoll(poll: Poll) = {
val query =
s"""
Expand All @@ -54,11 +64,10 @@ object DataAdapter extends LazyLogging {
|)
""".stripMargin
logger.info(query)
val statement = getConnection.createStatement
val numRows = statement.executeUpdate(query)
val numRows = executeUpdateQuery(query)
if (numRows != 1) {
logger.error("failed to insert " + query)
-1
DatabaseError.ConstraintViolation
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice

else {
logger.info("poll.p.id: " + poll.p.id)
Expand All @@ -76,11 +85,15 @@ object DataAdapter extends LazyLogging {
| WHERE id = '$id'
""".stripMargin
logger.info(query)
val statement = getConnection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)
val rs: ResultSet = statement.executeQuery(query)
val rs: ResultSet =
getConnection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY).executeQuery(query)

// Comvert to Option form

if (!rs.first()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

comment on flags being used - what is the intention

logger.error("failed to retrieve using: " + query)
-1
// TODO throw DatabaseError.RecordNotFound
throw new Error("dkfldsfd");
}
else {
val md = rs.getMetaData
Expand All @@ -92,7 +105,15 @@ object DataAdapter extends LazyLogging {
mutableMap
}
}
def updatePoll(poll:Poll): Any = {

/**
* Update the poll. if success then return nothing else throw exception
*
* @param poll
*/
def updatePoll(poll:Poll):Unit = {


val query =
s"""
|UPDATE nldb.polls
Expand All @@ -106,9 +127,17 @@ object DataAdapter extends LazyLogging {
|WHERE id = '${poll.p.id.get}'
""".stripMargin
logger.info(query)
getConnection.createStatement().executeUpdate(query)
val x = getConnection.createStatement().executeUpdate(query)
//x match {
// case Some()
// case None => Throw new ErrorEnum(XXX)
//}
}
def deletePoll(id: String): Int = {
getConnection.createStatement().executeUpdate("DELETE FROM polls WHERE id = '" + id + "'")
def deletePoll(id: String):Unit = {
val x = executeUpdateQuery(s"DELETE FROM polls WHERE id = '$id'")
//x match {
// case Some()
// case None => Throw
//}
}
}
16 changes: 11 additions & 5 deletions src/main/scala/com/newlibertie/pollster/api/v1/PollApi.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.newlibertie.pollster.api.v1

import java.sql.{DatabaseMetaData, SQLException, SQLTimeoutException}

import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.model.headers.`Content-Type`
import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpResponse, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import com.newlibertie.pollster.errorenum.{ApplicationError, ApplicationErrorEnum, BaseErrorEnum, DatabaseError, DatabaseErrorEnum}
import com.newlibertie.pollster.impl.Poll
import com.typesafe.scalalogging.LazyLogging

Expand All @@ -23,9 +26,10 @@ object PollApi extends LazyLogging {
get {
parameters("id") { id: String =>
Poll.read(id) match {
case -1 => complete(StatusCodes.NotFound)
case -2 => complete(StatusCodes.BadRequest)
case p: Poll => complete(HttpResponse(entity = p.toJsonString))
case DatabaseError.RecordNotFound => complete(StatusCodes.NotFound)
case ApplicationError.ExceptionError => complete(StatusCodes.InternalServerError)
case _ => complete(StatusCodes.InternalServerError)
}
}
} ~
Expand Down Expand Up @@ -70,12 +74,14 @@ object PollApi extends LazyLogging {
delete {
parameters("id") { id: String =>
Poll.read(id) match {
case -1 => complete(StatusCodes.NotFound)
case -2 => complete(StatusCodes.BadRequest)
case DatabaseError.RecordNotFound => complete(StatusCodes.NotFound)
case _:DatabaseErrorEnum#AEVal => complete(StatusCodes.BadRequest)
case _:BaseErrorEnum#AEVal => complete(StatusCodes.InternalServerError)
case p: Poll => if (p.canDelete()) {
p.deletePoll() match {
case 1 => complete(StatusCodes.OK)
case _ => complete(StatusCodes.NotFound)
case 0 => complete(StatusCodes.NotFound)
case _ => complete(StatusCodes.InternalServerError)
}
} else complete(StatusCodes.NotAcceptable)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.newlibertie.pollster.errorenum

/**
* Class to define errors related to application logic
* @param errEnumName The unique name of the derived class
* @param initial the id of the first enum variable in the class with name $errEnumName
* @param capacity the number of unique ids for the enum variables in the class with name $errEnumName
*/
class ApplicationErrorEnum (errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)

/**
* companion object to define error variables for application errors
*/
object ApplicationError extends ApplicationErrorEnum("ApplicationError", 0, 1000){
val Unknown = AEVal(0, "Unknown")
val AssertionError = AEVal(1, "AssertionError")
val ExceptionError = AEVal(2, "ExceptionError")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.newlibertie.pollster.errorenum

import scala.collection.mutable

/***
* A class at the root of all error enum derivations
* responsible for validating uniqueness of the name of the errEnumName and non-overlapping id ranges
* registers the EnumClass
* @param errEnumName The unique name of the derived class
* @param initial the id of the first enum variable in the class with name $errEnumName
* @param capacity the number of unique ids for the enum variables in the class with name $errEnumName
*/

// TODO : Is there a benefit in having class also extend from or implement Exception or Throwable

class BaseErrorEnum(val errEnumName: String, val initial:Int, val capacity:Int) extends Enumeration { // with Throwable {
require(BaseErrorEnum.register(errEnumName, initial, capacity), "Failed to register: "+errEnumName)
type AppErr = AEVal
import scala.language.implicitConversions
implicit def valueAppErrVal(x: Value): AEVal = x.asInstanceOf[AEVal]

case class AEVal(i: Int, name: String) extends super.Val(initial+i, name){
protected def nextId: Int = BaseErrorEnum.super.Value.nextId
}

protected object AEVal{
protected def generateName(i: Int): String = {s"$errEnumName.Error#$i"}
protected def generateName(s: String): String = {s"$errEnumName.$s"}
def apply(i:Int):AEVal ={
val name = AEVal.generateName(i)
require(BaseErrorEnum.super.values.find(_.toString == name) == None, "Duplicate AEVal.name: " + name)
AEVal(i, name)
}
def apply(name: String):AEVal = {
val name2 = generateName(name)
require(BaseErrorEnum.super.values.find(_.toString == name2) == None, "Duplicate AEVal.name: " + name2)
AEVal(nextId-initial, name2)
}
def apply():AEVal = {
AEVal(nextId-initial)
}
}
}

/**
* Helping Object providing validations and register each EnumClass derived from BaseErrorEnum
*/
private object BaseErrorEnum {
class ErrEnumRange(val initial: Int, val capacity: Int)
private val registry: mutable.Map[String, ErrEnumRange] = new mutable.HashMap
def register(errEnumName: String, initial:Int, capacity:Int):Boolean = {
if (registry.contains(errEnumName) ||
(registry.valuesIterator.exists(er => er.initial < (initial + capacity) &&
initial < (er.initial + er.capacity)))){
false
}
else
{
registry(errEnumName) = new ErrEnumRange(initial, capacity)
true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.newlibertie.pollster.errorenum

/**
* Class to define errors related to database
* @param errEnumName The unique name of the derived class
* @param initial the id of the first enum variable in the class with name $errEnumName
* @param capacity the number of unique ids for the enum variables in the class with name $errEnumName
*/
class DatabaseErrorEnum(errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)

/**
* companion object to define error variables for database errors
*/
object DatabaseError extends DatabaseErrorEnum("DatabaseError", 1000, 1000){
val RecordNotFound = AEVal(0, "RecordNotFound")
val ConstraintViolation = AEVal(1, "ConstraintViolation")
val RecordIntegrityError = AEVal(2, "RecordIntegrityError")
val ConnectIdentifierResolution = AEVal(3, "ConnectIdentifierResolution")
val InternalCodeError = AEVal(4, "InternalCodeError")
val DataTypeMisMatch = AEVal(5, "DataTypeMisMatch")
val Access = AEVal(6, "Access")
val Timeout = AEVal(7, "Timeout")
}
8 changes: 5 additions & 3 deletions src/main/scala/com/newlibertie/pollster/impl/Poll.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.math.BigInteger
import java.util.Date

import com.newlibertie.pollster.DataAdapter
import com.newlibertie.pollster.errorenum.{ApplicationError, BaseErrorEnum, DatabaseError, DatabaseErrorEnum}
import com.typesafe.scalalogging.LazyLogging
import net.liftweb.json._

Expand Down Expand Up @@ -55,9 +56,10 @@ object Poll extends LazyLogging {
catch {
case ex: Exception =>
logger.error(s"Error constructing poll Object from id=${id}: ${ex.getMessage}")
-2
ApplicationError.ExceptionError
}
case _ => -1
case DatabaseError.RecordNotFound => DatabaseError.RecordNotFound
case _ => ApplicationError.Unknown
}
}
}
Expand All @@ -79,7 +81,7 @@ class Poll(val p:PollParameters, val cp:CryptographicParameters = new Cryptograp
def canOpen():Boolean = {
p.opening_ts after new Date()
}
def deletePoll(): Int = {
def deletePoll(): Any = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Type should not be made Any

DataAdapter.deletePoll(p.id.get)
}
def toJsonString: String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.newlibertie.pollster.api.v1
import akka.http.scaladsl.model.{ContentTypes, StatusCodes}
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.testkit.ScalatestRouteTest
//import com.newlibertie.pollster.DataAdapter.logger
import com.newlibertie.pollster.impl.Poll
import org.scalatest._
import com.typesafe.scalalogging.LazyLogging
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.newlibertie.pollster.errorenum

import org.scalatest._
import com.typesafe.scalalogging.LazyLogging
class ApplicationErrorEnumTest extends WordSpec with Matchers with LazyLogging {

class TestApplicationErrorEnum (errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)
object TestApplicationErrorEnum extends TestApplicationErrorEnum("TestApplicationError", 0, 10){
val Unknown = AEVal(0, "Unknown")
}

"the enum creation" should {
"detect duplicate enum name" in {
class TestApplicationErrorEnum2 (errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)
object TestApplicationErrorEnum2 extends TestApplicationErrorEnum2("TestApplicationError", 0, 10){
val Unknown = AEVal(0, "Unknown")
}
var stat = -1
try {
var err:AnyRef = TestApplicationErrorEnum.Unknown
err = TestApplicationErrorEnum2.Unknown
}
catch {
case ex: Exception =>
logger.info(ex.getMessage)
stat = if (ex.getMessage().equalsIgnoreCase("requirement failed: Failed to register: TestApplicationError")) 0 else -1
}
stat shouldEqual 0
}
"detect overlapping id range" in {
var stat = -1
class TestApplicationErrorEnum3 (errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)
object TestApplicationErrorEnum3 extends TestApplicationErrorEnum3("TestApplicationError3", 0, 10){
val Unknown = AEVal(0, "Unknown")
}
try {
var err:AnyRef = TestApplicationErrorEnum.Unknown
err = TestApplicationErrorEnum3.Unknown
}
catch {
case ex: Exception =>
logger.info(ex.getMessage)
stat = if (ex.getMessage().equalsIgnoreCase("requirement failed: Failed to register: TestApplicationError3")) 0 else -1
case t: Throwable =>
logger.info(t.getLocalizedMessage)
stat = -1
}
stat shouldEqual 0
}
"detect duplicate id" in {
var stat = -1
class TestApplicationErrorEnum4 (errEnumName: String, initial:Int, capacity:Int) extends BaseErrorEnum(errEnumName, initial, capacity)
try {
object TestApplicationErrorEnum4 extends TestApplicationErrorEnum4("TestApplicationError4", 10, 10){
val Unknown = AEVal(0, "Unknown")
val Unknown2 = AEVal(0, "Unknown2")
}
TestApplicationErrorEnum4.Unknown
}
catch {
case t: Throwable =>
logger.info(t.getMessage)
stat = if (t.getMessage.startsWith("assertion failed: Duplicate id: ")) 0 else -1
}
stat shouldEqual 0
}
}
}