Skip to content

Commit

Permalink
check point
Browse files Browse the repository at this point in the history
  • Loading branch information
hohonuuli committed Oct 16, 2024
1 parent f040342 commit af9dee1
Show file tree
Hide file tree
Showing 38 changed files with 722 additions and 251 deletions.
3 changes: 3 additions & 0 deletions annosaurus/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ database {
url = ${?DATABASE_URL}
user = "sa"
user = ${?DATABASE_USER}

query.view = ${?DATABASE_QUERY_VIEW}
query.view = "annotations"
# name = "Derby"
# name = ${?DATABASE_NAME}
# https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#configuration-optional-dialects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ object AppConfig {
user = Config.getString("database.user"),
password = Config.getString("database.password"),
driver = Config.getString("database.driver"),
queryView = Config.getString("database.query.view")
)
}

Expand All @@ -87,7 +88,8 @@ case class DatabaseConfig(
url: String,
user: String,
password: String,
driver: String
driver: String,
queryView: String
):
def newConnection(): java.sql.Connection =
Class.forName(driver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ object Endpoints {
val imageReferenceController = new ImageReferenceController(daoFactory)
val indexController = new IndexController(daoFactory)
val observationController = new ObservationController(daoFactory)
val queryController = new QueryController(
AppConfig.DefaultDatabaseConfig,
AppConfig.DefaultDatabaseConfig.queryView
)

// --------------------------------
val analysisRepository = new AnalysisRepository(daoFactory.entityManagerFactory)
Expand All @@ -67,6 +71,7 @@ object Endpoints {
val imageReferenceEndpoints = new ImageReferenceEndpoints(imageReferenceController)
val indexEndpoints = new IndexEndpoints(indexController)
val observationEndpoints = new ObservationEndpoints(observationController, jdbcRepository)
val queryEndpoints = new QueryEndpoints(queryController)

// --------------------------------
// For VertX, we need to separate the non-blocking endpoints from the blocking ones
Expand All @@ -86,7 +91,8 @@ object Endpoints {
imageEndpoints.allImpl,
imageReferenceEndpoints.allImpl,
indexEndpoints.allImpl,
observationEndpoints.allImpl
observationEndpoints.allImpl,
queryEndpoints.allImpl
).flatten

val apiEndpoints = nonBlockingEndpoints ++ blockingEndpoints
Expand Down
3 changes: 2 additions & 1 deletion annosaurus/src/main/scala/org/mbari/annosaurus/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ object Main:
val port = sys.env.get("HTTP_PORT").flatMap(_.toIntOption).getOrElse(8080)
log.atInfo.log(s"Starting ${AppConfig.Name} v${AppConfig.Version} on port $port")

val vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(AppConfig.NumberOfVertxWorkers))
val vertx =
Vertx.vertx(new VertxOptions().setWorkerPoolSize(AppConfig.NumberOfVertxWorkers))
// val vertx = Vertx.vertx()
val server = vertx.createHttpServer()
val router = Router.router(vertx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ class AnnotationController(

def create(annotation: Annotation)(using ec: ExecutionContext): Future[Seq[Annotation]] =
val entity = annotation.toEntity
val dao = daoFactory.newImagedMomentDAO()
val dao = daoFactory.newImagedMomentDAO()
val future = dao.runTransaction(d => {
// d.create(entity)
// Annotation.fromImagedMoment(entity, true)
Expand All @@ -259,7 +259,6 @@ class AnnotationController(
future.onComplete(_ => dao.close())
future


/** Bulk create annotations
* @param annotations
* THe annotations to create
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2017 Monterey Bay Aquarium Research Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.mbari.annosaurus.controllers

import org.mbari.annosaurus.DatabaseConfig
import org.mbari.annosaurus.domain.QueryRequest
import org.mbari.annosaurus.repository.query.{
Constraint,
Constraints,
JDBC,
QueryResults,
QueryService
}

class QueryController(databaseConfig: DatabaseConfig, viewName: String) {

private lazy val queryService = new QueryService(databaseConfig, viewName)

def count(constraints: Constraints): Either[Throwable, Int] =
queryService.count(constraints)

def query(queryRequest: QueryRequest): Either[Throwable, QueryResults] =
queryService.query(queryRequest.querySelects, queryRequest.constraints)

def listColumns(): Either[Throwable, Seq[JDBC.Metadata]] =
queryService.jdbc.listColumnsMetadata(viewName)

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ package org.mbari.annosaurus.domain

import java.util.UUID

/**
* Update multiple observations at once
* @param observationUuids The UUIDs of the observations to update
* @param concept The new concept
* @param observer The new observer
* @param activity The new activity
* @param group The new group
*/
/** Update multiple observations at once
* @param observationUuids
* The UUIDs of the observations to update
* @param concept
* The new concept
* @param observer
* The new observer
* @param activity
* The new activity
* @param group
* The new group
*/
case class ObservationsUpdate(
observationUuids: Seq[UUID],
concept: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2017 Monterey Bay Aquarium Research Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.mbari.annosaurus.domain

import org.mbari.annosaurus.repository.query.Constraints

case class QueryRequest(
querySelects: Seq[String],
constraints: Constraints
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.mbari.annosaurus.etc.circe.CirceCodecs
import org.mbari.annosaurus.etc.circe.CirceCodecs.{*, given}
import org.mbari.annosaurus.etc.jwt.JwtService
import org.mbari.annosaurus.etc.jdk.Logging.given
import org.mbari.annosaurus.repository.query.{Constraint, Constraints}

import scala.concurrent.ExecutionContext
import scala.concurrent.Future
Expand Down Expand Up @@ -118,6 +119,19 @@ trait Endpoints:
implicit lazy val sURL: Schema[URL] = Schema.string
implicit lazy val sInstant: Schema[Instant] = Schema.string
implicit lazy val sBulkAnnotationSc: Schema[BulkAnnotationSC] = Schema.derived[BulkAnnotationSC]
implicit lazy val sConstraintDate: Schema[Constraint.Date] = Schema.derived[Constraint.Date]
implicit lazy val sConstraintInString: Schema[Constraint.In[String]] =
Schema.derived[Constraint.In[String]]
implicit lazy val sConstraintLike: Schema[Constraint.Like] = Schema.derived[Constraint.Like]
implicit lazy val sConstraintMax: Schema[Constraint.Max] = Schema.derived[Constraint.Max]
implicit lazy val sConstraintMin: Schema[Constraint.Min] = Schema.derived[Constraint.Min]
implicit lazy val sConstraintMinMax: Schema[Constraint.MinMax] =
Schema.derived[Constraint.MinMax]
implicit lazy val sConstraintIsNull: Schema[Constraint.IsNull] =
Schema.derived[Constraint.IsNull]
implicit lazy val sConstraint: Schema[Constraint] = Schema.string
implicit lazy val sConstraints: Schema[Constraints] = Schema.derived[Constraints]
implicit lazy val sQueryRequest: Schema[QueryRequest] = Schema.derived[QueryRequest]
// given Schema[Option[URL]] = Schema.string
// implicit lazy val sOptCAD: Schema[Option[CachedAncillaryDatumSC]] = Schema.derived[Option[CachedAncillaryDatumSC]]
// implicit lazy val sOptDouble: Schema[Option[Double]] = Schema.derived[Option[Double]]
Expand All @@ -139,6 +153,15 @@ trait Endpoints:
log.atError.withCause(exception).log("Error")
Success(Left(ServerError(exception.getMessage)))

def handleEitherAsync[T](
f: => Either[Throwable, T]
)(using ec: ExecutionContext): Future[Either[ErrorMsg, T]] =
Future {
f match
case Right(value) => Right(value)
case Left(e) => Left(ServerError(e.getMessage))
}

def handleOption[T](f: Future[Option[T]])(using
ec: ExecutionContext
): Future[Either[ErrorMsg, T]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,9 @@ class FastAnnotationEndpoints(jdbcRepository: JdbcRepository)(using
.in(paging)
.out(jsonBody[Seq[UUID]])
.name("findImageMomentUuidsByConcept")
.description("Find the UUIDS of image moments by concept. Only include image moments with images. Sorted by recorded timestamp.")
.description(
"Find the UUIDS of image moments by concept. Only include image moments with images. Sorted by recorded timestamp."
)
.tag(tag)

val findImagedMomentUuidsByConceptImpl: ServerEndpoint[Any, Future] =
Expand Down Expand Up @@ -363,7 +365,9 @@ class FastAnnotationEndpoints(jdbcRepository: JdbcRepository)(using
.in(paging)
.out(jsonBody[Seq[UUID]])
.name("findImagedMomentUuidsByToConcept")
.description("Find image moment UUIDs by to concept. Only include image moments with images. Sorted by recorded timestamp.")
.description(
"Find image moment UUIDs by to concept. Only include image moments with images. Sorted by recorded timestamp."
)
.tag(tag)

val findImagedMomentUuidsByToConceptImpl: ServerEndpoint[Any, Future] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ class ImagedMomentEndpoints(controller: ImagedMomentController)(using
.in(jsonBody[MoveImagedMoments])
.out(jsonBody[Count])
.name("bulkMove")
.description("Bulk move imaged moments to a new video reference. JSON request can be camelCase or snake_case")
.description(
"Bulk move imaged moments to a new video reference. JSON request can be camelCase or snake_case"
)
.tag(tag)

val bulkMoveImpl: ServerEndpoint[Any, Future] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@
package org.mbari.annosaurus.endpoints

import org.mbari.annosaurus.controllers.ObservationController
import org.mbari.annosaurus.domain.{ConceptCount, Count, CountForVideoReferenceSC, ErrorMsg, ObservationSC, ObservationUpdateSC, ObservationsUpdate, RenameConcept, RenameCountSC}
import org.mbari.annosaurus.domain.{
ConceptCount,
Count,
CountForVideoReferenceSC,
ErrorMsg,
ObservationSC,
ObservationUpdateSC,
ObservationsUpdate,
RenameConcept,
RenameCountSC
}
import org.mbari.annosaurus.etc.jwt.JwtService
import org.mbari.annosaurus.etc.tapir.TapirCodecs.given
import sttp.tapir.*
Expand Down Expand Up @@ -320,7 +330,11 @@ class ObservationEndpoints(controller: ObservationController, jdbcRepository: Jd
secureEndpoint
.put
.in(base / "bulk")
.in(jsonBody[ObservationsUpdate].description("Describes the parameters and uuids of the observations to update. Can be camelCase or snake_case."))
.in(
jsonBody[ObservationsUpdate].description(
"Describes the parameters and uuids of the observations to update. Can be camelCase or snake_case."
)
)
.out(jsonBody[Count].description("The number of observations updated"))
.name("updateManyObservations")
.description(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2017 Monterey Bay Aquarium Research Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.mbari.annosaurus.endpoints

import CustomTapirJsonCirce.*
import org.mbari.annosaurus.controllers.QueryController
import org.mbari.annosaurus.domain.{ErrorMsg, QueryRequest}
import org.mbari.annosaurus.etc.circe.CirceCodecs.{*, given}
import org.mbari.annosaurus.etc.tapir.TapirCodecs.given
import org.mbari.annosaurus.repository.query.{JDBC, QueryResults}
import sttp.model.StatusCode
import sttp.tapir.*
import sttp.tapir.generic.auto.*
import sttp.tapir.server.ServerEndpoint
import sttp.tapir.server.ServerEndpoint.Full

import scala.util.Success
import scala.concurrent.{ExecutionContext, Future}

class QueryEndpoints(queryController: QueryController)(using executionContext: ExecutionContext)
extends Endpoints {

private val base = "query"
private val tag = "Query"

val listColumns: Endpoint[Unit, Unit, ErrorMsg, Seq[JDBC.Metadata], Any] = openEndpoint
.get
.in(base / "columns")
.out(jsonBody[Seq[JDBC.Metadata]])
.name("listQueryColumns")
.description("List columns in the query view")
.tag(tag)

val listColumnsImpl: Full[Unit, Unit, Unit, ErrorMsg, Seq[JDBC.Metadata], Any, Future] =
listColumns.serverLogic(_ => handleEitherAsync(queryController.listColumns()))

val runQuery: Endpoint[Unit, QueryRequest, ErrorMsg, String, Any] =
openEndpoint
.post
.in(base / "run")
.in(jsonBody[QueryRequest])
.out(stringBody)
.out(header("Content-Type", "text/tab-separated-values"))
.name("runQuery")
.description("Run a query")
.tag(tag)

val runQueryImpl =
runQuery.serverLogic(request =>
handleEitherAsync(
queryController.query(request).map(QueryResults.toTsv).map(_.mkString("\n"))
)
)

override def all: List[Endpoint[_, _, _, _, _]] = List(
listColumns,
runQuery
)

override def allImpl: List[ServerEndpoint[Any, Future]] = List(
listColumnsImpl,
runQueryImpl
)
}
Loading

0 comments on commit af9dee1

Please sign in to comment.