From 65516728446bd99568851a66929a17c0a427e6ae Mon Sep 17 00:00:00 2001 From: Florian M Date: Tue, 28 Apr 2020 13:50:03 +0200 Subject: [PATCH 1/5] Use Client Timestamp for info/finish timetracking --- app/controllers/AnnotationController.scala | 16 ++++++------- app/controllers/LegacyApiController.scala | 14 ++++++++++-- app/models/user/time/TimeSpanService.scala | 5 ++-- conf/webknossos.latest.routes | 6 ++--- conf/webknossos.versioned.routes | 8 +++++-- frontend/javascripts/admin/admin_rest_api.js | 24 ++++++++++++-------- 6 files changed, 46 insertions(+), 27 deletions(-) diff --git a/app/controllers/AnnotationController.scala b/app/controllers/AnnotationController.scala index e7b9ba47b97..f47a5ece204 100755 --- a/app/controllers/AnnotationController.scala +++ b/app/controllers/AnnotationController.scala @@ -49,7 +49,7 @@ class AnnotationController @Inject()( implicit val timeout = Timeout(5 seconds) val taskReopenAllowed = (conf.Features.taskReopenAllowed + (10 seconds)).toMillis - def info(typ: String, id: String) = sil.UserAwareAction.async { implicit request => + def info(typ: String, id: String, timestamp: Long) = sil.UserAwareAction.async { implicit request => log { val notFoundMessage = if (request.identity.isEmpty) "annotation.notFound.considerLoggingIn" else "annotation.notFound" @@ -63,7 +63,7 @@ class AnnotationController @Inject()( .publicWrites(annotation, request.identity, Some(restrictions)) ?~> "annotation.write.failed" _ <- Fox.runOptional(request.identity) { user => if (typedTyp == AnnotationType.Task || typedTyp == AnnotationType.Explorational) { - timeSpanService.logUserInteraction(user, annotation) // log time when a user starts working + timeSpanService.logUserInteraction(timestamp, user, annotation) // log time when a user starts working } else Fox.successful(()) } } yield { @@ -174,22 +174,22 @@ class AnnotationController @Inject()( } } - private def finishAnnotation(typ: String, id: String, issuingUser: User)( + private def finishAnnotation(typ: String, id: String, issuingUser: User, timestamp: Long)( implicit ctx: DBAccessContext): Fox[(Annotation, String)] = for { annotation <- provider.provideAnnotation(typ, id, issuingUser) ~> NOT_FOUND restrictions <- provider.restrictionsFor(typ, id) ?~> "restrictions.notFound" ~> NOT_FOUND message <- annotationService.finish(annotation, issuingUser, restrictions) ?~> "annotation.finish.failed" updated <- provider.provideAnnotation(typ, id, issuingUser) - _ <- timeSpanService.logUserInteraction(issuingUser, annotation) // log time on tracing end + _ <- timeSpanService.logUserInteraction(timestamp, issuingUser, annotation) // log time on tracing end } yield { (updated, message) } - def finish(typ: String, id: String) = sil.SecuredAction.async { implicit request => + def finish(typ: String, id: String, timestamp: Long) = sil.SecuredAction.async { implicit request => log { for { - (updated, message) <- finishAnnotation(typ, id, request.identity) ?~> "annotation.finish.failed" + (updated, message) <- finishAnnotation(typ, id, request.identity, timestamp) ?~> "annotation.finish.failed" restrictions <- provider.restrictionsFor(typ, id) json <- annotationService.publicWrites(updated, Some(request.identity), Some(restrictions)) } yield { @@ -198,11 +198,11 @@ class AnnotationController @Inject()( } } - def finishAll(typ: String) = sil.SecuredAction.async(parse.json) { implicit request => + def finishAll(typ: String, timestamp: Long) = sil.SecuredAction.async(parse.json) { implicit request => log { withJsonAs[JsArray](request.body \ "annotations") { annotationIds => val results = Fox.serialSequence(annotationIds.value.toList) { jsValue => - jsValue.asOpt[String].toFox.flatMap(id => finishAnnotation(typ, id, request.identity)) + jsValue.asOpt[String].toFox.flatMap(id => finishAnnotation(typ, id, request.identity, timestamp)) } results.map { results => diff --git a/app/controllers/LegacyApiController.scala b/app/controllers/LegacyApiController.scala index 3449a122848..2d5d42263a1 100644 --- a/app/controllers/LegacyApiController.scala +++ b/app/controllers/LegacyApiController.scala @@ -17,6 +17,16 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, sil: Silhouette[WkEnv])(implicit ec: ExecutionContext, bodyParsers: PlayBodyParsers) extends Controller { + /* to provide v2, insert automatic timestamp in finish and info request */ + + def annotationFinishV2(typ: String, id: String) = + annotationController.finish(typ, id, System.currentTimeMillis) + + def annotationInfoV2(typ: String, id: String) = + annotationController.info(typ, id, System.currentTimeMillis) + + /* to provide v1, replace new field isVisible in annotation json by old field visibility */ + def annotationDuplicate(typ: String, id: String) = sil.SecuredAction.async { implicit request => for { result <- annotationController.duplicate(typ, id)(request) @@ -25,7 +35,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, def annotationFinish(typ: String, id: String) = sil.SecuredAction.async { implicit request => for { - result <- annotationController.finish(typ, id)(request) + result <- annotationController.finish(typ, id, System.currentTimeMillis)(request) } yield replaceVisibilityInResultJson(result) } @@ -49,7 +59,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, def annotationInfo(typ: String, id: String) = sil.SecuredAction.async { implicit request => for { - result <- annotationController.info(typ, id)(request) + result <- annotationController.info(typ, id, System.currentTimeMillis)(request) } yield replaceVisibilityInResultJson(result) } diff --git a/app/models/user/time/TimeSpanService.scala b/app/models/user/time/TimeSpanService.scala index a2a01f433ba..7eb6dc6e637 100644 --- a/app/models/user/time/TimeSpanService.scala +++ b/app/models/user/time/TimeSpanService.scala @@ -34,10 +34,9 @@ class TimeSpanService @Inject()(annotationDAO: AnnotationDAO, private val MaxTracingPause = conf.WebKnossos.User.Time.tracingPauseInSeconds.toMillis - def logUserInteraction(user: User, annotation: Annotation)(implicit ctx: DBAccessContext): Fox[Unit] = { - val timestamp = System.currentTimeMillis + def logUserInteraction(timestamp: Long, user: User, annotation: Annotation)( + implicit ctx: DBAccessContext): Fox[Unit] = logUserInteraction(Seq(timestamp), user, annotation) - } def logUserInteraction(timestamps: Seq[Long], user: User, annotation: Annotation)( implicit ctx: DBAccessContext): Fox[Unit] = diff --git a/conf/webknossos.latest.routes b/conf/webknossos.latest.routes index 0042baed26a..56f6478ac90 100644 --- a/conf/webknossos.latest.routes +++ b/conf/webknossos.latest.routes @@ -99,13 +99,13 @@ POST /annotations/upload POST /annotations/:typ/:id/duplicate controllers.AnnotationController.duplicate(typ: String, id: String) PATCH /annotations/:typ/:id/edit controllers.AnnotationController.editAnnotation(typ: String, id: String) -PATCH /annotations/:typ/:id/finish controllers.AnnotationController.finish(typ: String, id: String) -PATCH /annotations/:typ/finish controllers.AnnotationController.finishAll(typ: String) +PATCH /annotations/:typ/:id/finish controllers.AnnotationController.finish(typ: String, id: String, timestamp: Long) +PATCH /annotations/:typ/finish controllers.AnnotationController.finishAll(typ: String, timestamp: Long) PATCH /annotations/:typ/:id/reopen controllers.AnnotationController.reopen(typ: String, id: String) PUT /annotations/:typ/:id/reset controllers.AnnotationController.reset(typ: String, id: String) PATCH /annotations/:typ/:id/transfer controllers.AnnotationController.transfer(typ: String, id: String) -GET /annotations/:typ/:id/info controllers.AnnotationController.info(typ: String, id: String) +GET /annotations/:typ/:id/info controllers.AnnotationController.info(typ: String, id: String, timestamp: Long) PATCH /annotations/:typ/:id/makeHybrid controllers.AnnotationController.makeHybrid(typ: String, id: String) DELETE /annotations/:typ/:id controllers.AnnotationController.cancel(typ: String, id: String) POST /annotations/:typ/:id/merge/:mergedTyp/:mergedId controllers.AnnotationController.merge(typ: String, id: String, mergedTyp: String, mergedId: String) diff --git a/conf/webknossos.versioned.routes b/conf/webknossos.versioned.routes index f3162420a5d..cd78c06a390 100644 --- a/conf/webknossos.versioned.routes +++ b/conf/webknossos.versioned.routes @@ -1,10 +1,14 @@ # API versioning is handled here. Higher-Priority routes first # example: assume, the features route has changed, introducing v2. The older v1 needs to be provided in the legacyApiController --> /v2/ webknossos.latest.Routes +-> /v3/ webknossos.latest.Routes + +PATCH /v2/annotations/:typ/:id/finish controllers.LegacyApiController.annotationFinishV2(typ: String, id: String) +GET /v2/annotations/:typ/:id/info controllers.LegacyApiController.annotationInfoV2(typ: String, id: String) + +-> /v2/ webknossos.latest.Routes -# GET /v1/features controllers.LegacyApiController.v1features # PATCH /v1/annotations/:typ/:id/edit controllers.LegacyApiController.editAnnotation(typ: String, id: String) # input needs to change diff --git a/frontend/javascripts/admin/admin_rest_api.js b/frontend/javascripts/admin/admin_rest_api.js index bc03bbf4de2..a6a5e647ad5 100644 --- a/frontend/javascripts/admin/admin_rest_api.js +++ b/frontend/javascripts/admin/admin_rest_api.js @@ -540,9 +540,12 @@ export function finishAnnotation( annotationId: string, annotationType: APIAnnotationType, ): Promise { - return Request.receiveJSON(`/api/annotations/${annotationType}/${annotationId}/finish`, { - method: "PATCH", - }); + return Request.receiveJSON( + `/api/annotations/${annotationType}/${annotationId}/finish?timestamp=${Date.now()}`, + { + method: "PATCH", + }, + ); } export function resetAnnotation( @@ -566,12 +569,15 @@ export function deleteAnnotation( export function finishAllAnnotations( selectedAnnotationIds: Array, ): Promise<{ messages: Array }> { - return Request.sendJSONReceiveJSON("/api/annotations/Explorational/finish", { - method: "PATCH", - data: { - annotations: selectedAnnotationIds, + return Request.sendJSONReceiveJSON( + `/api/annotations/Explorational/finish?timestamp=${Date.now()}`, + { + method: "PATCH", + data: { + annotations: selectedAnnotationIds, + }, }, - }); + ); } export function copyAnnotationToUserAccount( @@ -587,7 +593,7 @@ export function getAnnotationInformation( annotationType: APIAnnotationType, options?: RequestOptions = {}, ): Promise { - const infoUrl = `/api/annotations/${annotationType}/${annotationId}/info`; + const infoUrl = `/api/annotations/${annotationType}/${annotationId}/info?timestamp=${Date.now()}`; return Request.receiveJSON(infoUrl, options); } From 06c271e9e68622c48560cd2a80f75e4cfcd6e016 Mon Sep 17 00:00:00 2001 From: Florian M Date: Tue, 28 Apr 2020 15:05:56 +0200 Subject: [PATCH 2/5] fix tests --- frontend/javascripts/test/helpers/apiHelpers.js | 2 +- frontend/javascripts/test/model/model.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/javascripts/test/helpers/apiHelpers.js b/frontend/javascripts/test/helpers/apiHelpers.js index 944145b5661..a6de32b9a59 100644 --- a/frontend/javascripts/test/helpers/apiHelpers.js +++ b/frontend/javascripts/test/helpers/apiHelpers.js @@ -122,7 +122,7 @@ export function __setupOxalis(t, mode, apiVersion) { const ANNOTATION = modelData[mode].annotation; Request.receiveJSON - .withArgs(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info`) + .withArgs(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info?timestamp=${Date.now()}`) .returns(Promise.resolve(_.cloneDeep(ANNOTATION))); const datasetClone = _.cloneDeep(DATASET); diff --git a/frontend/javascripts/test/model/model.spec.js b/frontend/javascripts/test/model/model.spec.js index 88d6576699d..b1a382d4ff6 100644 --- a/frontend/javascripts/test/model/model.spec.js +++ b/frontend/javascripts/test/model/model.spec.js @@ -57,7 +57,7 @@ test.beforeEach(t => { model.state = { position: [1, 2, 3] }; Request.receiveJSON - .withArgs(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info`) + .withArgs(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info?timestamp=${Date.now()}`) .returns(Promise.resolve(_.cloneDeep(ANNOTATION))); Request.receiveJSON .withArgs(`/api/datasets/${ANNOTATION.dataSetName}`) From ec9c903addb1869baa255b0774a031c2312f31ce Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Tue, 28 Apr 2020 16:47:45 +0200 Subject: [PATCH 3/5] ignore timestamp when mocking info request in test --- frontend/javascripts/test/helpers/apiHelpers.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/javascripts/test/helpers/apiHelpers.js b/frontend/javascripts/test/helpers/apiHelpers.js index a6de32b9a59..1168df1d1a3 100644 --- a/frontend/javascripts/test/helpers/apiHelpers.js +++ b/frontend/javascripts/test/helpers/apiHelpers.js @@ -122,7 +122,14 @@ export function __setupOxalis(t, mode, apiVersion) { const ANNOTATION = modelData[mode].annotation; Request.receiveJSON - .withArgs(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info?timestamp=${Date.now()}`) + .withArgs( + sinon.match( + arg => + // Match against the URL while ignoring further GET parameters (such as timestamps) + typeof arg === "string" && + arg.startsWith(`/api/annotations/${ANNOTATION_TYPE}/${ANNOTATION_ID}/info`), + ), + ) .returns(Promise.resolve(_.cloneDeep(ANNOTATION))); const datasetClone = _.cloneDeep(DATASET); From 8339bc38fd819b696695d87d888997bf7fdaaed6 Mon Sep 17 00:00:00 2001 From: Florian M Date: Tue, 28 Apr 2020 17:25:07 +0200 Subject: [PATCH 4/5] update api docs + changelog --- CHANGELOG.unreleased.md | 3 +- docs/rest_api.md | 88 +++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 643a0cdaa50..99c86f9af26 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -26,8 +26,9 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Toggling the "Render missing data black" option now automatically reloads all layers making it unnecessary to reload the whole page. [#4516](https://github.com/scalableminds/webknossos/pull/4516) - The "mappings" attribute of segmentation layers in datasource jsons can now be omitted. [#4532](https://github.com/scalableminds/webknossos/pull/4532) - Uploading a single nml, allows to wrap the tracing in a new tree group. [#4563](https://github.com/scalableminds/webknossos/pull/4563) -- Unconnected trees no longer cause an error during NML import in the tracing view. Instead, unconnected trees are split into their components. The split components are wrapped in a tree group with the original tree's name. [#4541](https://github.com/scalableminds/webknossos/pull/4541) +- Unconnected trees no longer cause an error during NML import in the tracing view. Instead, unconnected trees are split into their components. The split components are wrapped in a tree group with the original tree's name. [#4541](https://github.com/scalableminds/webknossos/pull/4541) - Made the NML importer in the tracing view less strict. Incorrect timestamps, missing tree names or missing node radii no longer cause an error. [#4541](https://github.com/scalableminds/webknossos/pull/4541) +- REST API endpoints finish and info now expect additional GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent). [#4580](https://github.com/scalableminds/webknossos/pull/4580) ### Fixed - Users only get tasks of datasets that they can access. [#4488](https://github.com/scalableminds/webknossos/pull/4488) diff --git a/docs/rest_api.md b/docs/rest_api.md index 410d60b585d..d6ab187b19b 100644 --- a/docs/rest_api.md +++ b/docs/rest_api.md @@ -14,9 +14,11 @@ The API is subject to frequent changes. However, older versions will be supporte New versions will be documented here, detailing the changes. Note, however, that some changes are not considered to be breaking the API and will not lead to new versions. Such changes include new optional parameters as well as new fields in the responses. The same goes for error message wording. -### Current api version is `v2` +### Current api version is `v3` -In comparison to `v1` the annotation `isPublic` flag was replaced by a `visibility` field. This field can have the following values: `Public, Internal, Private`. +* New in v3: the info and finish requests of annotations now expect an additional `timestamp` GET parameter that should be set to the time the request is sent (e.g. Date.now()). + +* New in v2: in comparison to `v1` the annotation `isPublic` flag was replaced by a `visibility` field. This field can have the following values: `Public, Internal, Private`. ## Routes @@ -72,8 +74,8 @@ List your own task annotations - JSON list of objects containing annotation information about your own task annotations, also including task and task type information - total count of task annotations in the HTTP header `X-Total-Count` if parameter is set -#### `v1` differences - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field --- ### `GET /user/annotations` @@ -95,8 +97,8 @@ List your own explorative annotations - JSON list of objects containing annotation information about your own explorative annotations - total count of explorative annotations in the HTTP header `X-Total-Count` if parameter is set -#### `v1` differences - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field @@ -131,10 +133,10 @@ List the task annotations of a user - JSON list of objects containing annotation information about the task annotations of the user, also including task and task type information - total count of task annotations in the HTTP header `X-Total-Count` if parameter is set - total count of task annotations in the HTTP header `X-Total-Count` if parameter is set - - -#### `v1` differences - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field + + +#### Changes Introduced in `v2` + - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field --- ### `GET /users/:id/annotations` @@ -157,8 +159,8 @@ List the explorative annotations of a user - JSON list of objects containing annotation information about the explorative annotations of the user - total count of explorative annotations in the HTTP header `X-Total-Count` if parameter is set -#### `v1` differences - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field --- @@ -225,14 +227,14 @@ Create a new datastore - `"isScratch"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore is hosted on a scratch/experimental environment - `"isForeign"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore belongs to this wk instance or belongs to a foreign wk instance - `"isConnector"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore is a wk-connect instance - + #### Returns - JSON object containing information about the newly created datastore - + #### Note - This route is only accessible for administrators. - + --- ### `DELETE /datastores/:name` @@ -240,10 +242,10 @@ Deletes a datastore from the wk database #### Expects - In the url: `:name` - the name of the datastore which should be deleted - + #### Note - This route is only accessible for administrators. - + --- ### `PUT /datastores/:name` @@ -258,14 +260,14 @@ Update an existing datastore - `"isScratch"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore is hosted on a scratch/experimental environment - `"isForeign"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore belongs to this wk instance or belongs to a foreign wk instance - `"isConnector"` `[BOOLEAN]` (optional, default: `false`) whether or not the datastore is a wk-connect instance - + #### Returns - JSON object containing information about the updated datastore - + #### Note - This route is only accessible for administrators. - + --- ### `PUT /tracingstores/:name` @@ -277,10 +279,10 @@ Update an existing tracingstore - `"name"` `[STRING]` name of the tracingstore - `"url"` `[STRING]` url from the tracingstore, used for communication with wk - `"publicUrl"` `[STRING]` publicly accessible url from the tracingstore, used for user facing links - + #### Returns - JSON object containing information about the updated tracingstore - + #### Note - This route is only accessible for administrators. @@ -294,6 +296,7 @@ Update an existing tracingstore - for `CompoundTask` `:id` is a task id - for `CompoundProject` `:id` is a project id - for `CompoundTaskType` `:id` is a task type id + - GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) #### Returns - JSON object containing annotation information about the selected annotation @@ -302,8 +305,11 @@ Update an existing tracingstore The compound annotations are created as merged from the finished annotations associated with the Task/Project/TaskType. This merging is performed before this info request is answered and can be slow for large numbers of annotations. The merged annotations are then stored in a cache for a few minutes, but not on disk. If requested again within this time, the request will be answered more quickly, but newly finished annotations will not be included yet. This cache is shared between this info request and the download request. -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v3` + - Expects additional GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) + +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- ### `GET /annotations/:typ/:id/download` @@ -346,9 +352,9 @@ Duplicate an annotation (“copy to my account”) #### Returns - JSON object containing annotation information about the newly created (duplicated) annotation, including the assigned id - -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- @@ -368,8 +374,8 @@ Edit metadata of an annotation #### Returns - JSON object containing annotation information about the edited annotation -#### `v1` differences - - The request and returned JSON contain the `isPublic` `[BOOLEAN]` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The request and returned JSON contain the `isPublic` `[BOOLEAN]` flag instead of the `visibility` field --- ### `PATCH /annotations/:typ/:id/finish` @@ -377,12 +383,16 @@ Edit metadata of an annotation #### Expects - In the url: `:typ` – one of `Task`, `Explorational` - In the url: `:id` an annotation id + - GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) #### Returns - JSON object containing annotation information about the finished annotation - -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + +#### Changes Introduced in `v3` + - Expects additional GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) + +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- @@ -395,8 +405,8 @@ Edit metadata of an annotation #### Returns - JSON object containing annotation information about the reopened annotation -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- ### `PUT /annotations/Task/:id/reset` @@ -409,8 +419,8 @@ Reset a task annotation to its base state #### Returns - JSON object containing annotation information about the reset task annotation -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- ### `POST /annotations/:typ/:id/merge/:mergedTyp/:mergedId` @@ -424,8 +434,8 @@ Merge two annotations, creating a new explorative. #### Returns - JSON object containing annotation information about the merged annotation -#### `v1` differences - - The returned JSON contains the `isPublic` flag instead of the `visibility` field +#### Changes Introduced in `v2` + - The returned JSON contains the `isPublic` flag instead of the `visibility` field --- ### `POST /tasks` @@ -561,7 +571,7 @@ List annotations of a task - JSON list of objects containing annotation information on the annotations of the task - Cancelled annotations are not returned -#### `v1` differences +#### Changes Introduced in `v2` - The annotation objects in the returned JSON contain the `isPublic` flag instead of the `visibility` field --- From b2d7da177fb254dd58d3d64d66e6a21a958d6ff1 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 29 Apr 2020 09:32:07 +0200 Subject: [PATCH 5/5] fix wording in documentation + comment --- app/controllers/LegacyApiController.scala | 2 +- docs/rest_api.md | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/controllers/LegacyApiController.scala b/app/controllers/LegacyApiController.scala index 2d5d42263a1..8bb73335053 100644 --- a/app/controllers/LegacyApiController.scala +++ b/app/controllers/LegacyApiController.scala @@ -25,7 +25,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, def annotationInfoV2(typ: String, id: String) = annotationController.info(typ, id, System.currentTimeMillis) - /* to provide v1, replace new field isVisible in annotation json by old field visibility */ + /* to provide v1, replace new field “visibility” in annotation json by old boolean field “isPublic” */ def annotationDuplicate(typ: String, id: String) = sil.SecuredAction.async { implicit request => for { diff --git a/docs/rest_api.md b/docs/rest_api.md index d6ab187b19b..d53aa66fdba 100644 --- a/docs/rest_api.md +++ b/docs/rest_api.md @@ -75,7 +75,7 @@ List your own task annotations - total count of task annotations in the HTTP header `X-Total-Count` if parameter is set #### Changes Introduced in `v2` - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field + - The annotation objects in the returned JSON contain the `visibility` field instead of the old `isPublic` field --- ### `GET /user/annotations` @@ -98,7 +98,7 @@ List your own explorative annotations - total count of explorative annotations in the HTTP header `X-Total-Count` if parameter is set #### Changes Introduced in `v2` - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field + - The annotation objects in the returned JSON contain the `visibility` field instead of the old `isPublic` field @@ -136,7 +136,7 @@ List the task annotations of a user #### Changes Introduced in `v2` - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field + - The annotation objects in the returned JSON contain the `visibility` field instead of the old `isPublic` field --- ### `GET /users/:id/annotations` @@ -160,7 +160,7 @@ List the explorative annotations of a user - total count of explorative annotations in the HTTP header `X-Total-Count` if parameter is set #### Changes Introduced in `v2` - - The annotation objects int the returned JSON contain the `isPublic` flag instead of the `visibility` field + - The annotation objects in the returned JSON contain the `visibility` field instead of the old `isPublic` field --- @@ -309,7 +309,7 @@ The compound annotations are created as merged from the finished annotations ass - Expects additional GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- ### `GET /annotations/:typ/:id/download` @@ -354,7 +354,7 @@ Duplicate an annotation (“copy to my account”) - JSON object containing annotation information about the newly created (duplicated) annotation, including the assigned id #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- @@ -392,7 +392,7 @@ Edit metadata of an annotation - Expects additional GET parameter `timestamp=[INT]` timestamp in milliseconds (time the request is sent) #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- @@ -406,7 +406,7 @@ Edit metadata of an annotation - JSON object containing annotation information about the reopened annotation #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- ### `PUT /annotations/Task/:id/reset` @@ -420,7 +420,7 @@ Reset a task annotation to its base state - JSON object containing annotation information about the reset task annotation #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- ### `POST /annotations/:typ/:id/merge/:mergedTyp/:mergedId` @@ -435,7 +435,7 @@ Merge two annotations, creating a new explorative. - JSON object containing annotation information about the merged annotation #### Changes Introduced in `v2` - - The returned JSON contains the `isPublic` flag instead of the `visibility` field + - The returned JSON contains the `visibility` field instead of the `isPublic` flag --- ### `POST /tasks` @@ -572,7 +572,7 @@ List annotations of a task - Cancelled annotations are not returned #### Changes Introduced in `v2` - - The annotation objects in the returned JSON contain the `isPublic` flag instead of the `visibility` field + - The annotation objects in the returned JSON contain the `visibility` flag instead of the `isPublic` field --- ### `GET /projects`