Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 3 additions & 2 deletions app/WebknossosModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import com.scalableminds.webknossos.datastore.storage.DataVaultService
import controllers.{Application, InitialDataService}
import files.WkTempFileService
import mail.MailchimpTicker
import models.analytics.AnalyticsSessionService
import models.annotation.{AnnotationMutexService, AnnotationStore, AnnotationDataSourceTemporaryStore}
import models.analytics.{AnalyticsService, AnalyticsSessionService}
import models.annotation.{AnnotationDataSourceTemporaryStore, AnnotationMutexService, AnnotationStore}
import models.dataset.{DatasetService, ThumbnailCachingService}
import models.job.{JobService, WorkerLivenessService}
import models.organization.FreeCreditTransactionService
Expand Down Expand Up @@ -43,5 +43,6 @@ class WebknossosModule extends AbstractModule {
bind(classOf[AnnotationDataSourceTemporaryStore]).asEagerSingleton()
bind(classOf[CertificateValidationService]).asEagerSingleton()
bind(classOf[FreeCreditTransactionService]).asEagerSingleton()
bind(classOf[AnalyticsService]).asEagerSingleton()
}
}
24 changes: 5 additions & 19 deletions app/controllers/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,29 @@ import play.api.mvc.{Action, AnyContent, Result}
import play.silhouette.api.Silhouette
import security.{CertificateValidationService, WkEnv}
import utils.sql.{SimpleSQLDAO, SqlClient}
import utils.{StoreModules, WkConf}
import utils.{BuildInfoService, StoreModules, WkConf}

import javax.inject.Inject
import scala.concurrent.ExecutionContext

class Application @Inject()(actorSystem: ActorSystem,
userService: UserService,
releaseInformationDAO: ReleaseInformationDAO,
buildInfoService: BuildInfoService,
organizationDAO: OrganizationDAO,
conf: WkConf,
defaultMails: DefaultMails,
storeModules: StoreModules,
sil: Silhouette[WkEnv],
certificateValidationService: CertificateValidationService)(implicit ec: ExecutionContext)
extends Controller
with ApiVersioning {
extends Controller {

private lazy val Mailer =
actorSystem.actorSelection("/user/mailActor")

// Note: This route is used by external applications, keep stable
def buildInfo: Action[AnyContent] = sil.UserAwareAction.async {
for {
schemaVersion <- releaseInformationDAO.getSchemaVersion.futureBox
} yield {
addRemoteOriginHeaders(
Ok(
Json.obj(
"webknossos" -> Json.toJson(
webknossos.BuildInfo.toMap.view.mapValues(_.toString).filterKeys(_ != "certificatePublicKey").toMap),
"schemaVersion" -> schemaVersion.toOption,
"httpApiVersioning" -> apiVersioningInfo,
"localDataStoreEnabled" -> storeModules.localDataStoreEnabled,
"localTracingStoreEnabled" -> storeModules.localTracingStoreEnabled
))
)
}
buildInfoJson <- buildInfoService.buildInfoJson
} yield addRemoteOriginHeaders(Ok(buildInfoJson))
}

// This only changes on server restart, so we can cache the full result.
Expand Down
12 changes: 12 additions & 0 deletions app/models/analytics/AnalyticsEvent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,15 @@ case class FrontendAnalyticsEvent(user: User, eventType: String, eventProperties
override def eventProperties(analyticsLookUpService: AnalyticsLookUpService): Fox[JsObject] =
Fox.successful(eventProperties ++ Json.obj("is_frontend_event" -> true))
}

case class WebknossosHeartbeatAnalyticsEvent(user: User, buildInfoJson: JsObject)(implicit ec: ExecutionContext)
extends AnalyticsEvent {
def eventType: String = "webknossos_heartbeat"
def eventProperties(analyticsLookUpService: AnalyticsLookUpService): Fox[JsObject] =
Fox.successful(
Json.obj(
"build_info" -> buildInfoJson,
)
)

}
24 changes: 22 additions & 2 deletions app/models/analytics/AnalyticsService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ import com.scalableminds.webknossos.datastore.rpc.RPC
import com.typesafe.scalalogging.LazyLogging
import models.user.{MultiUserDAO, UserDAO}
import com.scalableminds.util.tools.Box.tryo
import com.scalableminds.webknossos.datastore.helpers.IntervalScheduler
import controllers.ReleaseInformationDAO
import org.apache.pekko.actor.ActorSystem
import play.api.http.Status.UNAUTHORIZED
import play.api.inject.ApplicationLifecycle
import play.api.libs.json._
import utils.WkConf
import utils.{BuildInfoService, WkConf}

import javax.inject.Inject
import scala.concurrent.ExecutionContext
Expand All @@ -20,8 +24,13 @@ class AnalyticsService @Inject()(rpc: RPC,
wkConf: WkConf,
analyticsLookUpService: AnalyticsLookUpService,
analyticsSessionService: AnalyticsSessionService,
analyticsDAO: AnalyticsDAO)(implicit ec: ExecutionContext)
analyticsDAO: AnalyticsDAO,
userDAO: UserDAO,
buildInfoService: BuildInfoService,
val actorSystem: ActorSystem,
val lifecycle: ApplicationLifecycle)(implicit val ec: ExecutionContext)
extends LazyLogging
with IntervalScheduler
with FoxImplicits {

private lazy val conf = wkConf.BackendAnalytics
Expand Down Expand Up @@ -75,6 +84,17 @@ class AnalyticsService @Inject()(rpc: RPC,
}
Fox.successful(())
}

override protected def tickerInterval: FiniteDuration = 12 seconds

override protected def tick(): Fox[_] =
for {
oldestUser <- userDAO.findOldestActive
buildInfoJson <- Fox.fromFuture(buildInfoService.buildInfoJson)
event = WebknossosHeartbeatAnalyticsEvent(oldestUser, buildInfoJson)
_ = track(event)
} yield ()

}

class AnalyticsLookUpService @Inject()(userDAO: UserDAO, multiUserDAO: MultiUserDAO, wkConf: WkConf)
Expand Down
7 changes: 7 additions & 0 deletions app/models/user/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ class UserDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext)
def findIdsByMultiUserId(multiUserId: ObjectId): Fox[Seq[ObjectId]] =
run(q"SELECT _id FROM $existingCollectionName WHERE _multiUser = $multiUserId".as[ObjectId])

def findOldestActive: Fox[User] =
for {
r <- run(
q"SELECT $columns FROM $existingCollectionName WHERE NOT isDeactivated ORDER BY created LIMIT 1".as[UsersRow])
parsed <- parseFirst(r, "oldestActive")
} yield parsed

def buildSelectionPredicates(isEditableOpt: Option[Boolean],
isTeamManagerOrAdminOpt: Option[Boolean],
isAdminOpt: Option[Boolean],
Expand Down
25 changes: 25 additions & 0 deletions app/utils/BuildInfoService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package utils

import com.scalableminds.util.mvc.ApiVersioning
import controllers.ReleaseInformationDAO
import play.api.libs.json.{JsObject, Json}

import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}

class BuildInfoService @Inject()(releaseInformationDAO: ReleaseInformationDAO, storeModules: StoreModules)
extends ApiVersioning {

def buildInfoJson(implicit ec: ExecutionContext): Future[JsObject] =
for {
schemaVersion <- releaseInformationDAO.getSchemaVersion.futureBox
} yield
Json.obj(
"webknossos" -> Json.toJson(
webknossos.BuildInfo.toMap.view.mapValues(_.toString).filterKeys(_ != "certificatePublicKey").toMap),
"schemaVersion" -> schemaVersion.toOption,
"httpApiVersioning" -> apiVersioningInfo,
"localDataStoreEnabled" -> storeModules.localDataStoreEnabled,
"localTracingStoreEnabled" -> storeModules.localTracingStoreEnabled
)
}
3 changes: 1 addition & 2 deletions project/BuildInfoSettings.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sbt.Keys.{name, sbtVersion, scalaVersion}
import sbtbuildinfo.BuildInfoOption
import sbtbuildinfo.BuildInfoPlugin.autoImport.*

import scala.language.postfixOps
Expand Down Expand Up @@ -34,7 +35,6 @@ object BuildInfoSettings {
"ciBuild" -> ciBuild,
"ciTag" -> ciTag,
"version" -> webknossosVersion,
"datastoreApiVersion" -> "2.0",
"certificatePublicKey" -> certificatePublicKey
),
buildInfoPackage := "webknossos",
Expand All @@ -51,7 +51,6 @@ object BuildInfoSettings {
"ciBuild" -> ciBuild,
"ciTag" -> ciTag,
"version" -> webknossosVersion,
"datastoreApiVersion" -> "2.0"
),
buildInfoPackage := "webknossosDatastore",
buildInfoOptions := Seq(BuildInfoOption.ToJson)
Expand Down
Loading