Skip to content

Commit

Permalink
Added unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hohonuuli committed Jul 15, 2024
1 parent 055168a commit 715c821
Show file tree
Hide file tree
Showing 19 changed files with 315 additions and 111 deletions.
3 changes: 3 additions & 0 deletions oni/src/main/scala/org/mbari/oni/AppConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import scala.util.Try
import org.mbari.oni.jpa.EntityManagerFactories
import jakarta.persistence.EntityManagerFactory

/**
* Parse configuration info from reference.conf and application.conf
*/
object AppConfig:

val Config = ConfigFactory.load()
Expand Down
34 changes: 23 additions & 11 deletions oni/src/main/scala/org/mbari/oni/Endpoints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@
package org.mbari.oni

import jakarta.persistence.EntityManagerFactory
import org.mbari.oni.endpoints.{AuthorizationEndpoints, ConceptEndpoints, ConceptNameEndpoints, HealthEndpoints, HistoryEndpoints, LinkEndpoints, PhylogenyEndpoints, PrefNodeEndpoints, ReferenceEndpoints, UserAccountEndpoints}
import org.mbari.oni.endpoints.{
AuthorizationEndpoints,
ConceptEndpoints,
ConceptNameEndpoints,
HealthEndpoints,
HistoryEndpoints,
LinkEndpoints,
PhylogenyEndpoints,
PrefNodeEndpoints,
ReferenceEndpoints,
UserAccountEndpoints
}
import org.mbari.oni.etc.jwt.JwtService
import org.mbari.oni.jdbc.FastPhylogenyService
import sttp.tapir.server.ServerEndpoint
Expand All @@ -27,23 +38,24 @@ object Endpoints:
val config = AppConfig.DefaultJwtConfig
JwtService(config.issuer, config.apiKey, config.signingSecret)

given ExecutionContext = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(AppConfig.NumberOfThreads))
given ExecutionContext =
ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(AppConfig.NumberOfThreads))

val entityMangerFactory: EntityManagerFactory = AppConfig.DefaultEntityManagerFactory

val phylogenyEndpoints: PhylogenyEndpoints = PhylogenyEndpoints(entityMangerFactory)

val authorizationEndpoints: AuthorizationEndpoints = AuthorizationEndpoints(entityMangerFactory)
val conceptEndpoints: ConceptEndpoints = ConceptEndpoints(entityMangerFactory)
val conceptNameEndpoints: ConceptNameEndpoints = ConceptNameEndpoints(entityMangerFactory)
val healthEndpoints: HealthEndpoints = HealthEndpoints()
val historyEndpoints: HistoryEndpoints = HistoryEndpoints(entityMangerFactory, phylogenyEndpoints.service)
val linkEndpoints: LinkEndpoints = LinkEndpoints(entityMangerFactory)
val authorizationEndpoints: AuthorizationEndpoints = AuthorizationEndpoints(entityMangerFactory)
val conceptEndpoints: ConceptEndpoints = ConceptEndpoints(entityMangerFactory)
val conceptNameEndpoints: ConceptNameEndpoints = ConceptNameEndpoints(entityMangerFactory)
val healthEndpoints: HealthEndpoints = HealthEndpoints()
val historyEndpoints: HistoryEndpoints = HistoryEndpoints(entityMangerFactory, phylogenyEndpoints.service)
val linkEndpoints: LinkEndpoints = LinkEndpoints(entityMangerFactory)
val linkRealizationEndpoints: LinkRealizationEndpoints = LinkRealizationEndpoints(entityMangerFactory)
val linkTemplateEndpoints: LinkTemplateEndpoints = LinkTemplateEndpoints(entityMangerFactory)
val prefNodeEndpoints: PrefNodeEndpoints = PrefNodeEndpoints(entityMangerFactory)
val referenceEndpoints: ReferenceEndpoints = ReferenceEndpoints(entityMangerFactory)
val userAccountEndpoints: UserAccountEndpoints = UserAccountEndpoints(entityMangerFactory)
val prefNodeEndpoints: PrefNodeEndpoints = PrefNodeEndpoints(entityMangerFactory)
val referenceEndpoints: ReferenceEndpoints = ReferenceEndpoints(entityMangerFactory)
val userAccountEndpoints: UserAccountEndpoints = UserAccountEndpoints(entityMangerFactory)

val endpoints: List[ServerEndpoint[Any, Future]] = List(
authorizationEndpoints,
Expand Down
3 changes: 3 additions & 0 deletions oni/src/main/scala/org/mbari/oni/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import io.vertx.core.VertxOptions
import scala.concurrent.Await
import scala.concurrent.duration.Duration

/**
* Launches Oni
*/
object Main:

def main(args: Array[String]): Unit =
Expand Down
3 changes: 3 additions & 0 deletions oni/src/main/scala/org/mbari/oni/OniException.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

package org.mbari.oni

/**
* Defines custom exceptions used by Oni
*/
sealed trait OniException extends Throwable

trait NotFoundException extends OniException
Expand Down
6 changes: 6 additions & 0 deletions oni/src/main/scala/org/mbari/oni/domain/AuthorizationSC.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

package org.mbari.oni.domain

/**
* Authorization information as snake_case
*
* @param token_type Typically it will be `Beaerer`
* @param access_token For Oni this will be a JWT
*/
final case class AuthorizationSC(token_type: String, access_token: String)

object AuthorizationSC:
Expand Down
30 changes: 30 additions & 0 deletions oni/src/main/scala/org/mbari/oni/domain/Concept.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import org.mbari.oni.jpa.entities.ConceptEntity
import org.mbari.oni.etc.jdk.Numbers.*
import scala.jdk.CollectionConverters.*

/**
* A node in the knoweldgebase tree
*
* @param name The primary, or accepted, name of the concept
* @param rank The pyhologenetic rank
* @param alternativeNames Synonyms, common names, or former names
* @param children Child concepts of this concept
* @param aphiaId The WoRMS AphiaID
* @param id The database id
*/
case class Concept(
name: String,
rank: Option[String] = None,
Expand All @@ -19,18 +29,38 @@ case class Concept(
aphiaId: Option[Long] = None,
id: Option[Long] = None
):

/**
* Checks is this concept uses the provided name
*
* @param n The name to check
* @return true if this concept's name or alternative names contain this
* name. false if it does not.
*/
def containsName(n: String): Boolean = name.equals(n) ||
alternativeNames.contains(n)

/**
* All names applicable to this concept. THe first name will
* be the primary name.
*/
lazy val names: Seq[String] = name +: alternativeNames

/**
* A sorted list all names applicable to this concept and
* all of it's descendants.
*/
lazy val descendantNames: Seq[String] = descendants
.flatMap(_.names)
.toSeq
.sorted

/**
* A list of all of this concepts descendants
*/
lazy val descendants: Set[Concept] = children.toSet.flatMap(_.descendants) + this


lazy val flatten: Seq[Concept] = Seq(this) ++ children.flatMap(_.flatten)

object Concept:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.mbari.oni.jpa.entities.ConceptEntity
import org.mbari.oni.etc.jdk.Numbers.*

/**
* Detailed information about a concept
* @author
* Brian Schlining
* @since 2016-11-17T15:54:00
Expand Down
3 changes: 3 additions & 0 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/CloseableLock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ package org.mbari.oni.etc.jdk

import java.util.concurrent.locks.ReentrantLock

/**
* An auto-closeable lock. Use in a try-with-resources block to ensure the lock is released.
*/
class CloseableLock extends ReentrantLock with AutoCloseable {

def lockAndGet: CloseableLock = {
Expand Down
7 changes: 7 additions & 0 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/Colors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import java.awt.Color

class Colors:

/**
* Converts any string to a hex color. The color is generated from the hash code of the string.
* @param s
* The string to convert
* @return
* The hex color
*/
def stringToHexColor(s: String): String =
val c = intToRGBA(s.hashCode)
f"#${c.getRed}%02X${c.getGreen}%02X${c.getBlue}%02X" // ignore alpha. Sharktopoda won't parse colors with alpha
Expand Down
3 changes: 2 additions & 1 deletion oni/src/main/scala/org/mbari/oni/etc/jdk/Files.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import java.nio.file.Path
import scala.util.Using

import java.nio.file.Files as JFiles
import java.io.BufferedInputStream

object Files:

Expand All @@ -25,7 +26,7 @@ object Files:
def concatenate(files: Seq[Path], output: Path): Unit =
Using(BufferedOutputStream(JFiles.newOutputStream(output))) { out =>
files.foreach { file =>
Using(JFiles.newInputStream(file)) { in =>
Using(BufferedInputStream(JFiles.newInputStream(file))) { in =>
val buffer = new Array[Byte](1024)
var bytesRead = in.read(buffer)
while bytesRead != -1 do
Expand Down
10 changes: 10 additions & 0 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/Instants.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ object Instants:
val CompactTimeFormatterNs: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSSSSX").withZone(utcZone)

/**
* Parse a string into an Instant. The string can be in any of the following formats:
* - yyyyMMdd'T'HHmmssX
* - yyyyMMdd'T'HHmmss.SSSX
* - yyyyMMdd'T'HHmmss.SSSSSSX
* - yyyy-MM-dd'T'HH:mm:ssX
*
* @param s
* @return
*/
def parseIso8601(s: String): Either[Throwable, Instant] =
val tried = Try(Instant.from(CompactTimeFormatter.parse(s))) orElse
Try(Instant.from(TimeFormatter.parse(s))) orElse
Expand Down
37 changes: 5 additions & 32 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/JdbcTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ object JdbcTypes:
* the JdbcRepository and associatited SQL classes.
*/
extension (obj: Object)
def asDouble: Option[Double] = doubleConverter(obj)
def asFloat: Option[Float] = floatConverter(obj)
def asDouble: Option[Double] = Numbers.doubleConverter(obj)
def asFloat: Option[Float] = Numbers.floatConverter(obj)
def asInstant: Option[Instant] = instantConverter(obj)
def asInt: Option[Int] = intConverter(obj)
def asLong: Option[Long] = longConverter(obj)
def asInt: Option[Int] = Numbers.intConverter(obj)
def asLong: Option[Long] = Numbers.longConverter(obj)
def asString: Option[String] = stringConverter(obj)
def asUrl: Option[URL] = urlConverter(obj)
def asUUID: Option[UUID] = uuidConverter(obj)
Expand All @@ -42,41 +42,14 @@ object JdbcTypes:
case null => None
case u: UUID => Some(u)
case s: String => Try(UUID.fromString(s)).toOption // TODO this could swallow errors
case _ => None

def stringConverter(obj: Object): Option[String] =
obj match
case null => None
case s: String => Some(s)
case _ => Some(obj.toString)

def doubleConverter(obj: Object): Option[Double] =
obj match
case null => None
case n: Number => Some(n.doubleValue())
case s: String => Try(s.toDouble).toOption
case _ => None

def floatConverter(obj: Object): Option[Float] =
obj match
case null => None
case n: Number => Some(n.floatValue())
case s: String => Try(s.toFloat).toOption
case _ => None

def longConverter(obj: Object): Option[Long] =
obj match
case null => None
case n: Number => Some(n.longValue())
case s: String => Try(s.toLong).toOption
case _ => None

def intConverter(obj: Object): Option[Int] =
obj match
case null => None
case n: Number => Some(n.intValue())
case s: String => Try(s.toInt).toOption
case _ => None

def urlConverter(obj: Object): Option[URL] =
obj match
case null => None
Expand Down
9 changes: 5 additions & 4 deletions oni/src/main/scala/org/mbari/oni/etc/jdk/Loggers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ import java.util.function.Supplier

/**
* Add fluent logging to System.Logger. Usage:
* {{{
* import org.fathomnet.support.etc.jdk.Logging.{given, *}
* given log: Logger = Sytem.getLogger("my.logger")
*
* ```scala
* import org.fathomnet.support.etc.jdk.Loggers.{given, *}
* given log: Logger = System.getLogger("my.logger")
*
* log.atInfo.log("Hello World")
* log.atInfo.withCause(new RuntimeException("Oops")).log("Hello World")
*
* 3.tapLog.atInfo.log(i => s"Hello World $i")
* }}}
* ```
* * @author Brian Schlining
*/
object Loggers:
Expand Down
73 changes: 10 additions & 63 deletions oni/src/main/scala/org/mbari/oni/jdbc/sql.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,73 +12,20 @@ import java.util.UUID
import scala.util.Try
import java.net.URL
import java.net.URI
import org.mbari.oni.etc.jdk.Numbers
import org.mbari.oni.etc.jdk.JdbcTypes

/**
* This is a collection of explicit conversions to convert from java.sql.ResultSet to various types. This is used in the
* JdbcRepository and associatited SQL classes.
*/
extension (obj: Object)
def asDouble: Option[Double] = doubleConverter(obj)
def asFloat: Option[Float] = floatConverter(obj)
def asInstant: Option[Instant] = instantConverter(obj)
def asInt: Option[Int] = intConverter(obj)
def asLong: Option[Long] = longConverter(obj)
def asString: Option[String] = stringConverter(obj)
def asUrl: Option[URL] = urlConverter(obj)
def asUUID: Option[UUID] = uuidConverter(obj)
def asDouble: Option[Double] = Numbers.doubleConverter(obj)
def asFloat: Option[Float] = Numbers.floatConverter(obj)
def asInstant: Option[Instant] = JdbcTypes.instantConverter(obj)
def asInt: Option[Int] = Numbers.intConverter(obj)
def asLong: Option[Long] = Numbers.longConverter(obj)
def asString: Option[String] = JdbcTypes.stringConverter(obj)
def asUrl: Option[URL] = JdbcTypes.urlConverter(obj)
def asUUID: Option[UUID] = JdbcTypes.uuidConverter(obj)

def instantConverter(obj: Object): Option[Instant] =
obj match
case null => None
case i: Instant => Some(i)
case ts: java.sql.Timestamp => Some(ts.toInstant)
case m: microsoft.sql.DateTimeOffset => Some(m.getOffsetDateTime().toInstant())
case _ => None // TODO handle postgres

def uuidConverter(obj: Object): Option[UUID] =
obj match
case null => None
case u: UUID => Some(u)
case s: String => Try(UUID.fromString(s)).toOption // TODO this could swallow errors

def stringConverter(obj: Object): Option[String] =
obj match
case null => None
case s: String => Some(s)
case _ => Some(obj.toString)

def doubleConverter(obj: Object): Option[Double] =
obj match
case null => None
case n: Number => Some(n.doubleValue())
case s: String => Try(s.toDouble).toOption
case _ => None

def floatConverter(obj: Object): Option[Float] =
obj match
case null => None
case n: Number => Some(n.floatValue())
case s: String => Try(s.toFloat).toOption
case _ => None

def longConverter(obj: Object): Option[Long] =
obj match
case null => None
case n: Number => Some(n.longValue())
case s: String => Try(s.toLong).toOption
case _ => None

def intConverter(obj: Object): Option[Int] =
obj match
case null => None
case n: Number => Some(n.intValue())
case s: String => Try(s.toInt).toOption
case _ => None

def urlConverter(obj: Object): Option[URL] =
obj match
case null => None
case u: URL => Some(u)
case uri: URI => Try(uri.toURL()).toOption
case s: String => Try(URI.create(s).toURL()).toOption
case _ => None
Loading

0 comments on commit 715c821

Please sign in to comment.