Skip to content

Commit 6721996

Browse files
authored
Use ObjectId in TracingStore where applicable (#8674)
Using our ObjectId class + validation in the tracingstore for annotationId and userId fields, to match the data model on the wk side. For some reason putting the sbt setting that enables using ObjectId in routes files in commonSettings would not work, I had to repeat it for all three projects 🤷 ### Steps to test: - Trace some, should still work - Annotation user state should still work - Re-test availableBySwitching workflow for annotations (not sure why there was String there previously instead of ObjectId despite this living on the wk side) ------ - [x] Removed dev-only changes like prints and application.conf edits - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [x] Needs datastore update after deployment
1 parent 3d93c5c commit 6721996

File tree

45 files changed

+462
-403
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+462
-403
lines changed

app/controllers/AuthenticationController.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ class AuthenticationController @Inject()(
215215
} yield result
216216

217217
def accessibleBySwitching(datasetId: Option[ObjectId],
218-
annotationId: Option[String],
218+
annotationId: Option[ObjectId],
219219
workflowHash: Option[String]): Action[AnyContent] = sil.SecuredAction.async {
220220
implicit request =>
221221
for {

app/controllers/WKRemoteTracingStoreController.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
8282
tracingStoreService.validateAccess(name, key) { _ =>
8383
val report = request.body
8484
for {
85-
annotationId <- ObjectId.fromString(report.annotationId)
86-
annotation <- annotationDAO.findOne(annotationId)
85+
annotation <- annotationDAO.findOne(report.annotationId)
8786
_ <- ensureAnnotationNotFinished(annotation)
8887
_ <- annotationDAO.updateModified(annotation._id, Instant.now)
8988
_ = report.statistics.map(statistics => annotationService.updateStatistics(annotation._id, statistics))

app/models/annotation/AnnotationLayerPrecedence.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ case class RedundantTracingProperties(
2929
zoomLevel: Double,
3030
userBoundingBoxes: Seq[NamedBoundingBoxProto],
3131
editPositionAdditionalCoordinates: Seq[AdditionalCoordinateProto],
32-
userStateBoundingBoxVisibilities: Map[String, Seq[Id32WithBool]] // UserId → Seq(bboxId, bboxIsVisible)
32+
userStateBoundingBoxVisibilities: Map[ObjectId, Seq[Id32WithBool]] // UserId → Seq(bboxId, bboxIsVisible)
3333
)
3434

3535
trait AnnotationLayerPrecedence extends FoxImplicits {
@@ -79,7 +79,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
7979
userStates: Seq[SkeletonUserStateProto],
8080
oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[SkeletonUserStateProto] = {
8181
val adaptedExistingUserStates = userStates.map { userState =>
82-
val userId = userState.userId
82+
val userId = ObjectId(userState.userId)
8383
oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match {
8484
case None => userState
8585
case Some(precedenceBboxVisibilities) =>
@@ -88,9 +88,9 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
8888
}
8989
// We also have to create new user states for the users the old precedence layer has, but the new precedence layer is missing.
9090
val newUserPrecedenceProperties = oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.filter(tuple =>
91-
!userStates.exists(_.userId == tuple._1))
91+
!userStates.exists(_.userId == tuple._1.toString))
9292
val newUserStates = newUserPrecedenceProperties.map {
93-
case (userId: String, boundingBoxVisibilities: Seq[Id32WithBool]) =>
93+
case (userId: ObjectId, boundingBoxVisibilities: Seq[Id32WithBool]) =>
9494
SkeletonTracingDefaults
9595
.emptyUserState(userId)
9696
.copy(
@@ -104,7 +104,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
104104
userStates: Seq[VolumeUserStateProto],
105105
oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[VolumeUserStateProto] = {
106106
val adaptedExistingUserStates = userStates.map { userState =>
107-
val userId = userState.userId
107+
val userId = ObjectId(userState.userId)
108108
oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match {
109109
case None => userState
110110
case Some(precedenceBboxVisibilities) =>
@@ -113,9 +113,9 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
113113
}
114114
// We also have to create new user states for the users the old precedence layer has, but the new precedence layer is missing.
115115
val newUserPrecedenceProperties = oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.filter(tuple =>
116-
!userStates.exists(_.userId == tuple._1))
116+
!userStates.exists(_.userId == tuple._1.toString))
117117
val newUserStates = newUserPrecedenceProperties.map {
118-
case (userId: String, boundingBoxVisibilities: Seq[Id32WithBool]) =>
118+
case (userId: ObjectId, boundingBoxVisibilities: Seq[Id32WithBool]) =>
119119
VolumeTracingDefaults
120120
.emptyUserState(userId)
121121
.copy(
@@ -194,7 +194,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
194194
s.userBoundingBoxes ++ s.userBoundingBox.map(
195195
com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)),
196196
s.editPositionAdditionalCoordinates,
197-
s.userStates.map(userState => (userState.userId, userState.boundingBoxVisibilities)).toMap
197+
s.userStates.map(userState => (ObjectId(userState.userId), userState.boundingBoxVisibilities)).toMap
198198
)
199199
case Right(v) =>
200200
RedundantTracingProperties(
@@ -204,7 +204,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
204204
v.userBoundingBoxes ++ v.userBoundingBox.map(
205205
com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)),
206206
v.editPositionAdditionalCoordinates,
207-
v.userStates.map(userState => (userState.userId, userState.boundingBoxVisibilities)).toMap
207+
v.userStates.map(userState => (ObjectId(userState.userId), userState.boundingBoxVisibilities)).toMap
208208
)
209209
}
210210
}

app/models/annotation/AnnotationMerger.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class AnnotationMerger @Inject()(datasetDAO: DatasetDAO, tracingStoreService: Tr
7474
for {
7575
dataset <- datasetDAO.findOne(datasetId)
7676
tracingStoreClient: WKRemoteTracingStoreClient <- tracingStoreService.clientFor(dataset)
77-
mergedAnnotationProto <- tracingStoreClient.mergeAnnotationsByIds(annotations.map(_.id),
77+
mergedAnnotationProto <- tracingStoreClient.mergeAnnotationsByIds(annotations.map(_._id),
7878
annotations.map(_._user),
7979
newAnnotationId,
8080
toTemporaryStore,

app/models/annotation/AnnotationService.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ class AnnotationService @Inject()(
801801
dataStore <- dataStoreDAO.findOneByName(dataset._dataStore.trim) ?~> "datastore.notFound"
802802
tracingStore <- tracingStoreDAO.findFirst
803803
annotationSource = AnnotationSource(
804-
id = annotation.id,
804+
id = annotation._id,
805805
annotationLayers = annotation.annotationLayers,
806806
datasetDirectoryName = dataset.directoryName,
807807
organizationId = organization._id,

app/models/annotation/WKRemoteTracingStoreClient.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ class WKRemoteTracingStoreClient(
172172
.postEmpty()
173173
}
174174

175-
def mergeAnnotationsByIds(annotationIds: List[String],
175+
def mergeAnnotationsByIds(annotationIds: List[ObjectId],
176176
ownerIds: List[ObjectId],
177177
newAnnotationId: ObjectId,
178178
toTemporaryStore: Boolean,
@@ -183,8 +183,8 @@ class WKRemoteTracingStoreClient(
183183
.addQueryString("toTemporaryStore" -> toTemporaryStore.toString)
184184
.addQueryString("newAnnotationId" -> newAnnotationId.toString)
185185
.addQueryString("requestingUserId" -> requestingUserId.toString)
186-
.postJsonWithProtoResponse[MergedFromIdsRequest, AnnotationProto](
187-
MergedFromIdsRequest(annotationIds, ownerIds.map(_.toString)))(AnnotationProto)
186+
.postJsonWithProtoResponse[MergedFromIdsRequest, AnnotationProto](MergedFromIdsRequest(annotationIds, ownerIds))(
187+
AnnotationProto)
188188
}
189189

190190
def mergeSkeletonTracingsByContents(newTracingId: String, tracings: SkeletonTracings): Fox[Unit] = {

app/security/AccessibleBySwitchingService.scala

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class AccessibleBySwitchingService @Inject()(
3030

3131
def getOrganizationToSwitchTo(user: User,
3232
datasetId: Option[ObjectId],
33-
annotationId: Option[String],
33+
annotationId: Option[ObjectId],
3434
workflowHash: Option[String])(implicit ctx: DBAccessContext): Fox[Organization] =
3535
for {
3636
isSuperUser <- multiUserDAO.findOne(user._multiUser).map(_.isSuperUser)
@@ -42,7 +42,7 @@ class AccessibleBySwitchingService @Inject()(
4242
} yield selectedOrganization
4343

4444
private def accessibleBySwitchingForSuperUser(datasetIdOpt: Option[ObjectId],
45-
annotationIdOpt: Option[String],
45+
annotationIdOpt: Option[ObjectId],
4646
workflowHashOpt: Option[String]): Fox[Organization] = {
4747
implicit val ctx: DBAccessContext = GlobalAccessContext
4848
(datasetIdOpt, annotationIdOpt, workflowHashOpt) match {
@@ -53,8 +53,7 @@ class AccessibleBySwitchingService @Inject()(
5353
} yield organization
5454
case (None, Some(annotationId), None) =>
5555
for {
56-
annotationObjectId <- ObjectId.fromString(annotationId)
57-
annotation <- annotationDAO.findOne(annotationObjectId) // Note: this does not work for compound annotations.
56+
annotation <- annotationDAO.findOne(annotationId) // Note: this does not work for compound annotations.
5857
user <- userDAO.findOne(annotation._user)
5958
organization <- organizationDAO.findOne(user._organization)
6059
} yield organization
@@ -69,7 +68,7 @@ class AccessibleBySwitchingService @Inject()(
6968

7069
private def accessibleBySwitchingForMultiUser(multiUserId: ObjectId,
7170
datasetIdOpt: Option[ObjectId],
72-
annotationIdOpt: Option[String],
71+
annotationIdOpt: Option[ObjectId],
7372
workflowHashOpt: Option[String]): Fox[Organization] =
7473
for {
7574
identities <- userDAO.findAllByMultiUser(multiUserId)
@@ -80,7 +79,7 @@ class AccessibleBySwitchingService @Inject()(
8079

8180
private def canAccessDatasetOrAnnotationOrWorkflow(user: User,
8281
datasetIdOpt: Option[ObjectId],
83-
annotationIdOpt: Option[String],
82+
annotationIdOpt: Option[ObjectId],
8483
workflowHashOpt: Option[String]): Fox[Boolean] = {
8584
val ctx = AuthorizedAccessContext(user)
8685
(datasetIdOpt, annotationIdOpt, workflowHashOpt) match {
@@ -99,12 +98,11 @@ class AccessibleBySwitchingService @Inject()(
9998
foundFox.shiftBox.map(_.isDefined)
10099
}
101100

102-
private def canAccessAnnotation(user: User, ctx: DBAccessContext, annotationId: String): Fox[Boolean] = {
101+
private def canAccessAnnotation(user: User, ctx: DBAccessContext, annotationId: ObjectId): Fox[Boolean] = {
103102
val foundFox = for {
104-
annotationIdParsed <- ObjectId.fromString(annotationId)
105-
annotation <- annotationDAO.findOne(annotationIdParsed)(GlobalAccessContext)
103+
annotation <- annotationDAO.findOne(annotationId)(GlobalAccessContext)
106104
_ <- Fox.fromBool(annotation.state != Cancelled)
107-
restrictions <- annotationProvider.restrictionsFor(AnnotationIdentifier(annotation.typ, annotationIdParsed))(ctx)
105+
restrictions <- annotationProvider.restrictionsFor(AnnotationIdentifier(annotation.typ, annotationId))(ctx)
108106
_ <- restrictions.allowAccess(user)
109107
} yield ()
110108
foundFox.shiftBox.map(_.isDefined)

build.sbt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,10 @@ Compile / console / scalacOptions -= "-Xlint:unused"
3434
scapegoatIgnoredFiles := Seq(".*/Tables.scala", ".*/Routes.scala", ".*/.*mail.*template\\.scala")
3535
scapegoatDisabledInspections := Seq("FinalModifierOnCaseClass", "UnusedMethodParameter", "UnsafeTraversableMethods")
3636

37-
// Allow path binding for ObjectId
38-
routesImport += "com.scalableminds.util.objectid.ObjectId"
39-
4037
lazy val commonSettings = Seq(
4138
resolvers ++= DependencyResolvers.dependencyResolvers,
4239
Compile / doc / sources := Seq.empty,
43-
Compile / packageDoc / publishArtifact := false
40+
Compile / packageDoc / publishArtifact := false,
4441
)
4542

4643
lazy val protocolBufferSettings = Seq(
@@ -92,6 +89,7 @@ lazy val webknossosDatastore = (project in file("webknossos-datastore"))
9289
}
9390
((libs +++ subs +++ targets) ** "*.jar").classpath
9491
},
92+
routesImport += "com.scalableminds.util.objectid.ObjectId",
9593
copyMessagesFilesSetting
9694
)
9795

@@ -102,11 +100,12 @@ lazy val webknossosTracingstore = (project in file("webknossos-tracingstore"))
102100
.settings(
103101
name := "webknossos-tracingstore",
104102
commonSettings,
103+
routesImport += "com.scalableminds.util.objectid.ObjectId",
105104
generateReverseRouter := false,
106105
BuildInfoSettings.webknossosTracingstoreBuildInfoSettings,
107106
libraryDependencies ++= Dependencies.webknossosTracingstoreDependencies,
108107
dependencyOverrides ++= Dependencies.dependencyOverrides,
109-
copyMessagesFilesSetting
108+
copyMessagesFilesSetting,
110109
)
111110

112111
lazy val webknossos = (project in file("."))
@@ -116,6 +115,7 @@ lazy val webknossos = (project in file("."))
116115
.settings(
117116
name := "webknossos",
118117
commonSettings,
118+
routesImport += "com.scalableminds.util.objectid.ObjectId",
119119
generateReverseRouter := false,
120120
AssetCompilation.settings,
121121
BuildInfoSettings.webknossosBuildInfoSettings,

conf/webknossos.latest.routes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ GET /auth/token
2828
DELETE /auth/token controllers.AuthenticationController.deleteToken()
2929
GET /auth/switch controllers.AuthenticationController.switchMultiUser(to: String)
3030
POST /auth/switchOrganization/:organizationId controllers.AuthenticationController.switchOrganization(organizationId: String)
31-
GET /auth/accessibleBySwitching controllers.AuthenticationController.accessibleBySwitching(datasetId: Option[ObjectId], annotationId: Option[String], workflowHash: Option[String])
31+
GET /auth/accessibleBySwitching controllers.AuthenticationController.accessibleBySwitching(datasetId: Option[ObjectId], annotationId: Option[ObjectId], workflowHash: Option[String])
3232
POST /auth/sendInvites controllers.AuthenticationController.sendInvites()
3333
POST /auth/startResetPassword controllers.AuthenticationController.handleStartResetPassword()
3434
POST /auth/changePassword controllers.AuthenticationController.changePassword()

frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ describe("Annotation API (E2E)", () => {
199199
[UpdateActions.updateCameraAnnotation([2, 3, 4], null, [1, 2, 3], 2)],
200200
],
201201
123456789,
202+
null,
203+
true,
202204
),
203205
0,
204206
);
@@ -246,6 +248,8 @@ describe("Annotation API (E2E)", () => {
246248
createSaveQueueFromUpdateActions(
247249
[createTreesUpdateActions, [updateTreeGroupsUpdateAction]],
248250
123456789,
251+
null,
252+
true,
249253
),
250254
0,
251255
);
@@ -284,7 +288,12 @@ describe("Annotation API (E2E)", () => {
284288

285289
const updateTreeAction = UpdateActions.updateTree(trees.getOrThrow(1), tracingId);
286290
const [saveQueue] = addVersionNumbers(
287-
createSaveQueueFromUpdateActions([createTreesUpdateActions, [updateTreeAction]], 123456789),
291+
createSaveQueueFromUpdateActions(
292+
[createTreesUpdateActions, [updateTreeAction]],
293+
123456789,
294+
null,
295+
true,
296+
),
288297
0,
289298
);
290299

@@ -301,6 +310,8 @@ describe("Annotation API (E2E)", () => {
301310
createSaveQueueFromUpdateActions(
302311
[[UpdateActions.updateMetadataOfAnnotation(newDescription)]],
303312
123456789,
313+
null,
314+
true,
304315
),
305316
0,
306317
);

frontend/javascripts/test/e2e-setup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const tokenUserFInOrgaX =
3434

3535
let currentUserAuthToken = tokenUserA;
3636

37+
const idUserA = "570b9f4d2a7c0e4d008da6ef";
38+
3739
function setUserAuthToken(token: string) {
3840
currentUserAuthToken = token;
3941
}
@@ -163,4 +165,5 @@ export {
163165
tokenUserFInOrgaX,
164166
tokenUserFInOrgaY,
165167
setUserAuthToken,
168+
idUserA,
166169
};

frontend/javascripts/test/helpers/saveHelpers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { TracingStats } from "viewer/model/accessors/annotation_accessor";
22
import type { UpdateActionWithoutIsolationRequirement } from "viewer/model/sagas/update_actions";
33
import type { SaveQueueEntry } from "viewer/store";
4+
import { idUserA } from "test/e2e-setup";
45
import dummyUser from "test/fixtures/dummy_user";
56

67
export function createSaveQueueFromUpdateActions(
78
updateActions: UpdateActionWithoutIsolationRequirement[][],
89
timestamp: number,
910
stats: TracingStats | null = null,
11+
useE2eAuthorId: boolean = false,
1012
): SaveQueueEntry[] {
1113
return updateActions.map((ua) => ({
1214
version: -1,
@@ -15,7 +17,7 @@ export function createSaveQueueFromUpdateActions(
1517
actions: ua,
1618
info: "[]",
1719
transactionGroupCount: 1,
18-
authorId: dummyUser.id,
20+
authorId: useE2eAuthorId ? idUserA : dummyUser.id,
1921
transactionGroupIndex: 0,
2022
transactionId: "dummyRequestId",
2123
}));

0 commit comments

Comments
 (0)