Skip to content

Commit bec8480

Browse files
committed
Move byId routes to new controller
1 parent b9d05f3 commit bec8480

File tree

6 files changed

+207
-22
lines changed

6 files changed

+207
-22
lines changed

frontend/javascripts/admin/rest_api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,7 @@ export async function getHistogramForLayer(
14141414
): Promise<APIHistogramData> {
14151415
return doWithToken((token) =>
14161416
Request.receiveJSON(
1417-
`${datastoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/histogram?token=${token}`,
1417+
`${datastoreUrl}/data/wkDatasets/683d9643f600008d0e26a0a3/layers/${layerName}/histogram?token=${token}`,
14181418
{ showErrorToast: false },
14191419
),
14201420
);

frontend/javascripts/viewer/model/bucket_data_handling/wkstore_adapter.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export async function requestWithFallback(
103103
const tracingStoreHost = state.annotation.tracingStore.url;
104104

105105
const getDataStoreUrl = (optLayerName?: string) =>
106-
`${dataStoreHost}/data/datasets/${organization}/${datasetDirectoryName}/layers/${
106+
`${dataStoreHost}/data/wkDatasets/683d9643f600008d0e26a0a3/layers/${
107107
optLayerName || layerInfo.name
108108
}`;
109109

@@ -227,8 +227,6 @@ export async function requestFromStore(
227227
if (maybeAnnotationId != null) {
228228
params.append("annotationId", maybeAnnotationId);
229229
}
230-
// REMOVE ME
231-
dataUrl = "http://localhost:9000/data/wkDatasets/683d9643f600008d0e26a0a3/layers/layer424242"
232230
const { buffer: responseBuffer, headers } =
233231
await Request.sendJSONReceiveArraybufferWithHeaders(`${dataUrl}/data?${params}`, {
234232
data: bucketInfo,

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/BinaryDataController.scala

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.google.inject.Inject
44
import com.scalableminds.util.accesscontext.TokenContext
55
import com.scalableminds.util.geometry.Vec3Int
66
import com.scalableminds.util.image.{Color, JPEGWriter}
7-
import com.scalableminds.util.objectid.ObjectId
87
import com.scalableminds.util.time.Instant
98
import com.scalableminds.util.tools.Fox
109
import com.scalableminds.webknossos.datastore.DataStoreConfig
@@ -39,8 +38,7 @@ class BinaryDataController @Inject()(
3938
mappingService: MappingService,
4039
slackNotificationService: DSSlackNotificationService,
4140
adHocMeshServiceHolder: AdHocMeshServiceHolder,
42-
findDataService: FindDataService,
43-
datasetCache: DatasetCache
41+
findDataService: FindDataService
4442
)(implicit ec: ExecutionContext, bodyParsers: PlayBodyParsers)
4543
extends Controller
4644
with MissingBucketHeaders {
@@ -309,19 +307,6 @@ class BinaryDataController @Inject()(
309307
}
310308
}
311309

312-
def requestViaWebknossosById(datasetId: String, dataLayerName: String): Action[List[WebknossosDataRequest]] =
313-
Action.async(validateJson[List[WebknossosDataRequest]]) { implicit request =>
314-
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
315-
// TODO Log time
316-
for {
317-
datasetId <- ObjectId.fromString(datasetId)
318-
dataSource <- datasetCache.getById(datasetId)
319-
dataLayer <- dataSource.getDataLayer(dataLayerName).toFox ?~> "Data layer not found" ~> NOT_FOUND
320-
(data, indices) <- requestData(dataSource.id, dataLayer, request.body)
321-
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
322-
}
323-
}
324-
325310
private def requestData(
326311
dataSourceId: DataSourceId,
327312
dataLayer: DataLayer,
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package com.scalableminds.webknossos.datastore.controllers
2+
3+
import com.google.inject.Inject
4+
import com.scalableminds.util.accesscontext.TokenContext
5+
import com.scalableminds.util.geometry.Vec3Int
6+
import com.scalableminds.util.image.{Color, JPEGWriter}
7+
import com.scalableminds.util.objectid.ObjectId
8+
import com.scalableminds.util.tools.Fox
9+
import com.scalableminds.webknossos.datastore.DataStoreConfig
10+
import com.scalableminds.webknossos.datastore.helpers.MissingBucketHeaders
11+
import com.scalableminds.webknossos.datastore.image.{ImageCreator, ImageCreatorParameters}
12+
import com.scalableminds.webknossos.datastore.models.DataRequestCollection._
13+
import com.scalableminds.webknossos.datastore.models.{
14+
DataRequest,
15+
RawCuboidRequest,
16+
VoxelPosition,
17+
WebknossosDataRequest
18+
}
19+
import com.scalableminds.webknossos.datastore.models.requests.{DataServiceDataRequest, DataServiceRequestSettings}
20+
import com.scalableminds.webknossos.datastore.models.datasource.{Category, DataLayer, DataSourceId}
21+
import com.scalableminds.webknossos.datastore.services.{
22+
BinaryDataService,
23+
BinaryDataServiceHolder,
24+
DataSourceRepository,
25+
DataStoreAccessTokenService,
26+
DatasetCache,
27+
FindDataService,
28+
MappingService,
29+
UserAccessRequest
30+
}
31+
import com.scalableminds.webknossos.datastore.services.mesh.AdHocMeshServiceHolder
32+
import com.scalableminds.webknossos.datastore.slacknotification.DSSlackNotificationService
33+
import play.api.i18n.Messages
34+
import play.api.libs.json.Json
35+
import play.api.mvc.{Action, AnyContent, PlayBodyParsers, RawBuffer}
36+
37+
import java.io.ByteArrayOutputStream
38+
import scala.concurrent.ExecutionContext
39+
40+
/**
41+
* This is equivalent to the BinaryDataController for Datasets by DatasetId
42+
*/
43+
class WKDatasetController @Inject()(
44+
accessTokenService: DataStoreAccessTokenService,
45+
binaryDataServiceHolder: BinaryDataServiceHolder,
46+
findDataService: FindDataService,
47+
datasetCache: DatasetCache
48+
)(implicit ec: ExecutionContext, bodyParsers: PlayBodyParsers)
49+
extends Controller
50+
with MissingBucketHeaders {
51+
52+
val binaryDataService: BinaryDataService = binaryDataServiceHolder.binaryDataService
53+
54+
def requestViaWebknossos(datasetId: String, dataLayerName: String): Action[List[WebknossosDataRequest]] =
55+
Action.async(validateJson[List[WebknossosDataRequest]]) { implicit request =>
56+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
57+
// TODO Log time
58+
for {
59+
datasetId <- ObjectId.fromString(datasetId)
60+
dataSource <- datasetCache.getById(datasetId)
61+
dataLayer <- dataSource.getDataLayer(dataLayerName).toFox ?~> "Data layer not found" ~> NOT_FOUND
62+
(data, indices) <- requestData(dataSource.id, dataLayer, request.body)
63+
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
64+
}
65+
}
66+
67+
def requestRawCuboid(datasetId: String,
68+
dataLayerName: String,
69+
x: Int,
70+
y: Int,
71+
z: Int,
72+
width: Int,
73+
height: Int,
74+
depth: Int,
75+
mag: String,
76+
halfByte: Boolean,
77+
mappingName: Option[String]): Action[AnyContent] = Action.async { implicit request =>
78+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
79+
for {
80+
datasetId <- ObjectId.fromString(datasetId)
81+
(dataSource, dataLayer) <- datasetCache.getWithLayer(datasetId, dataLayerName) ~> NOT_FOUND
82+
magParsed <- Vec3Int.fromMagLiteral(mag).toFox ?~> "malformedMag"
83+
dataRequest = DataRequest(
84+
VoxelPosition(x, y, z, magParsed),
85+
width,
86+
height,
87+
depth,
88+
DataServiceRequestSettings(halfByte = halfByte, appliedAgglomerate = mappingName)
89+
)
90+
(data, indices) <- requestData(dataSource.id, dataLayer, dataRequest)
91+
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
92+
}
93+
}
94+
95+
def requestRawCuboidPost(datasetId: String, dataLayerName: String): Action[RawCuboidRequest] =
96+
Action.async(validateJson[RawCuboidRequest]) { implicit request =>
97+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
98+
for {
99+
datasetId <- ObjectId.fromString(datasetId)
100+
(dataSource, dataLayer) <- datasetCache.getWithLayer(datasetId, dataLayerName) ~> NOT_FOUND
101+
(data, indices) <- requestData(dataSource.id, dataLayer, request.body)
102+
} yield Ok(data).withHeaders(createMissingBucketsHeaders(indices): _*)
103+
}
104+
}
105+
106+
def thumbnailJpeg(datasetId: String,
107+
dataLayerName: String,
108+
x: Int,
109+
y: Int,
110+
z: Int,
111+
width: Int,
112+
height: Int,
113+
mag: String,
114+
mappingName: Option[String],
115+
intensityMin: Option[Double],
116+
intensityMax: Option[Double],
117+
color: Option[String],
118+
invertColor: Option[Boolean]): Action[RawBuffer] = Action.async(parse.raw) { implicit request =>
119+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
120+
for {
121+
datasetIdValidated <- ObjectId.fromString(datasetId)
122+
(dataSource, dataLayer) <- datasetCache.getWithLayer(datasetIdValidated, dataLayerName) ?~> Messages(
123+
"dataSource.notFound") ~> NOT_FOUND
124+
magParsed <- Vec3Int.fromMagLiteral(mag).toFox ?~> "malformedMag"
125+
dataRequest = DataRequest(
126+
VoxelPosition(x, y, z, magParsed),
127+
width,
128+
height,
129+
depth = 1,
130+
DataServiceRequestSettings(appliedAgglomerate = mappingName)
131+
)
132+
(data, _) <- requestData(dataSource.id, dataLayer, dataRequest)
133+
intensityRange: Option[(Double, Double)] = intensityMin.flatMap(min => intensityMax.map(max => (min, max)))
134+
layerColor = color.flatMap(Color.fromHTML)
135+
params = ImageCreatorParameters(
136+
dataLayer.elementClass,
137+
useHalfBytes = false,
138+
slideWidth = width,
139+
slideHeight = height,
140+
imagesPerRow = 1,
141+
blackAndWhite = false,
142+
intensityRange = intensityRange,
143+
isSegmentation = dataLayer.category == Category.segmentation,
144+
color = layerColor,
145+
invertColor = invertColor
146+
)
147+
dataWithFallback = if (data.length == 0)
148+
new Array[Byte](width * height * dataLayer.bytesPerElement)
149+
else data
150+
spriteSheet <- ImageCreator.spriteSheetFor(dataWithFallback, params).toFox ?~> "image.create.failed"
151+
firstSheet <- spriteSheet.pages.headOption.toFox ?~> "image.page.failed"
152+
outputStream = new ByteArrayOutputStream()
153+
_ = new JPEGWriter().writeToOutputStream(firstSheet.image)(outputStream)
154+
} yield Ok(outputStream.toByteArray).as(jpegMimeType)
155+
}
156+
}
157+
158+
def findData(datasetId: String, dataLayerName: String): Action[AnyContent] =
159+
Action.async { implicit request =>
160+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
161+
for {
162+
datasetIdValidated <- ObjectId.fromString(datasetId)
163+
(dataSource, dataLayer) <- datasetCache.getWithLayer(datasetIdValidated, dataLayerName) ~> NOT_FOUND
164+
positionAndMagOpt <- findDataService.findPositionWithData(dataSource.id, dataLayer)
165+
} yield Ok(Json.obj("position" -> positionAndMagOpt.map(_._1), "mag" -> positionAndMagOpt.map(_._2)))
166+
}
167+
}
168+
169+
def histogram(datasetId: String, dataLayerName: String): Action[AnyContent] =
170+
Action.async { implicit request =>
171+
accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readDataset(datasetId)) {
172+
for {
173+
datasetIdValidated <- ObjectId.fromString(datasetId)
174+
(dataSource, dataLayer) <- datasetCache.getWithLayer(datasetIdValidated, dataLayerName) ?~> Messages(
175+
"dataSource.notFound") ~> NOT_FOUND ?~> Messages("histogram.layerMissing", dataLayerName)
176+
listOfHistograms <- findDataService.createHistogram(dataSource.id, dataLayer) ?~> Messages("histogram.failed",
177+
dataLayerName)
178+
} yield Ok(Json.toJson(listOfHistograms))
179+
}
180+
}
181+
182+
private def requestData(
183+
dataSourceId: DataSourceId,
184+
dataLayer: DataLayer,
185+
dataRequests: DataRequestCollection
186+
)(implicit tc: TokenContext): Fox[(Array[Byte], List[Int])] = {
187+
val requests =
188+
dataRequests.map(r => DataServiceDataRequest(Some(dataSourceId), dataLayer, r.cuboid(dataLayer), r.settings))
189+
binaryDataService.handleDataRequests(requests)
190+
}
191+
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DatasetCache.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.scalableminds.webknossos.datastore.services
33
import com.google.inject.name.Named
44
import com.scalableminds.util.objectid.ObjectId
55
import com.scalableminds.util.tools.{Fox, FoxImplicits}
6-
import com.scalableminds.webknossos.datastore.models.datasource.DataSource
6+
import com.scalableminds.webknossos.datastore.models.datasource.{DataLayer, DataSource}
77
import com.scalableminds.webknossos.datastore.storage.TemporaryStore
88
import org.apache.pekko.actor.ActorSystem
99

@@ -26,6 +26,12 @@ class DatasetCache @Inject()(remoteWebknossosClient: DSRemoteWebknossosClient,
2626
} yield dataSource
2727
}
2828

29+
def getWithLayer(id: ObjectId, dataLayerName: String): Fox[(DataSource, DataLayer)] =
30+
for {
31+
dataSource <- getById(id)
32+
dataLayer <- dataSource.getDataLayer(dataLayerName).toFox ?~> "Data layer not found"
33+
} yield (dataSource, dataLayer)
34+
2935
def updateById(datasetId: String, dataSource: DataSource): Unit =
3036
cache.insert(ObjectId(datasetId), dataSource)
3137

webknossos-datastore/conf/com.scalableminds.webknossos.datastore.routes

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ GET /datasets/:organizationId/:datasetDirectoryName/layers/:dataLayerN
1212
GET /datasets/:organizationId/:datasetDirectoryName/layers/:dataLayerName/findData @com.scalableminds.webknossos.datastore.controllers.BinaryDataController.findData(organizationId: String, datasetDirectoryName: String, dataLayerName: String)
1313
GET /datasets/:organizationId/:datasetDirectoryName/layers/:dataLayerName/histogram @com.scalableminds.webknossos.datastore.controllers.BinaryDataController.histogram(organizationId: String, datasetDirectoryName: String, dataLayerName: String)
1414

15-
POST /wkDatasets/:datasetId/layers/:dataLayerName/data @com.scalableminds.webknossos.datastore.controllers.BinaryDataController.requestViaWebknossosById(datasetId: String, dataLayerName: String)
15+
POST /wkDatasets/:datasetId/layers/:dataLayerName/data @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.requestViaWebknossos(datasetId: String, dataLayerName: String)
16+
POST /wkDatasets/:datasetId/layers/:dataLayerName/readData @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.requestRawCuboidPost(datasetId: String, dataLayerName: String)
17+
GET /wkDatasets/:datasetId/layers/:dataLayerName/data @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.requestRawCuboid(datasetId: String, dataLayerName: String, x: Int, y: Int, z: Int, width: Int, height: Int, depth: Int, mag: String, halfByte: Boolean ?= false, mappingName: Option[String])
18+
GET /wkDatasets/:datasetId/layers/:dataLayerName/thumbnail.jpg @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.thumbnailJpeg(datasetId: String, dataLayerName: String, x: Int, y: Int, z: Int, width: Int, height: Int, mag: String, mappingName: Option[String], intensityMin: Option[Double], intensityMax: Option[Double], color: Option[String], invertColor: Option[Boolean])
19+
GET /wkDatasets/:datasetId/layers/:dataLayerName/findData @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.findData(datasetId: String, dataLayerName: String)
20+
GET /wkDatasets/:datasetId/layers/:dataLayerName/histogram @com.scalableminds.webknossos.datastore.controllers.WKDatasetController.histogram(datasetId: String, dataLayerName: String)
1621

1722
# Knossos compatible routes
1823
GET /datasets/:organizationId/:datasetDirectoryName/layers/:dataLayerName/mag:mag/x:x/y:y/z:z/bucket.raw @com.scalableminds.webknossos.datastore.controllers.BinaryDataController.requestViaKnossos(organizationId: String, datasetDirectoryName: String, dataLayerName: String, mag: Int, x: Int, y: Int, z: Int, cubeSize: Int)

0 commit comments

Comments
 (0)