Skip to content

Commit cefe45a

Browse files
authored
Merge pull request #285 from clowder-framework/release/1.19.0
Release/1.19.0
2 parents 3e27a54 + 3c40c0e commit cefe45a

File tree

110 files changed

+1077
-399
lines changed

Some content is hidden

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

110 files changed

+1077
-399
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Bug report
33
about: Create a report to help us improve
44
title: ''
5-
labels: ''
5+
labels: bug
66
assignees: ''
77

88
---

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Feature request
33
about: Suggest an idea for this project
44
title: ''
5-
labels: ''
5+
labels: enhancement
66
assignees: ''
77

88
---

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## 1.19.0 - 2021-10-05
8+
**_Important:_** This update requires a MongoDB update schema due to the new ability of showing summary statistics at the
9+
space level. Make sure to start the application with -DMONGOUPDATE=1.
10+
11+
### Fixed
12+
- Adding dataset to space. Space list on dataset page would be empty - fixed error when no spaces would load. [#274](https://github.com/clowder-framework/clowder/issues/274)
13+
- Typos "success" when returning status from API and "occurred" when logging to console.
14+
- If a dataset had multiple folders the layout would be wrong.
15+
- Collections created using api route are now indexed upon creation. [#257](https://github.com/clowder-framework/clowder/issues/257)
16+
17+
### Added
18+
- Mime type for geojson
19+
- "when" parameter in a few GET API endpoints to enable pagination [#266](https://github.com/clowder-framework/clowder/issues/266)
20+
- Show space statistics (bytes, users. etc) [#119](https://github.com/clowder-framework/clowder/issues/119)
21+
- "id" in GET metadata.jsonld endpoints [#278](https://github.com/clowder-framework/clowder/issues/278)
22+
- 'POST /api/files/uploadToDataset' now allows folder_id for uploading file to folder. [#232](https://github.com/clowder-framework/clowder/issues/232)
23+
724
## 1.18.1 - 2021-08-16
825

926
This release fixes a critical issue where invalid zip files could result in the files not being uploaded correctly. To check to see if you are affected, please use the following query:
@@ -55,7 +72,6 @@ If any files are returned, you should check to see if these files affected and a
5572

5673
### Changed
5774
- Updated Sphinx dependencies due to security and changes in required packages.
58-
5975
- Updated the three.js libraries for the FBX previewer
6076

6177
## 1.16.0 - 2021-03-31

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Following is a list of contributors in alphabetical order:
3434
- Sandeep Puthanveetil Satheesan
3535
- Smruti Padhy
3636
- Theerasit Issaranon
37+
- Tim Yardley
3738
- Todd Nicholson
3839
- Varun Kethineedi
3940
- Ward Poelmans

app/api/Collections.scala

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,28 @@
11
package api
22

3-
import java.io.{ByteArrayInputStream, InputStream, ByteArrayOutputStream}
4-
import java.security.{DigestInputStream, MessageDigest}
5-
import java.text.SimpleDateFormat
6-
import java.util.zip.{ZipEntry, ZipOutputStream, Deflater}
7-
83
import Iterators.RootCollectionIterator
9-
import _root_.util.JSONLD
4+
import util.SearchUtils
105
import api.Permission.Permission
11-
import org.apache.commons.codec.binary.Hex
6+
import controllers.Utils
7+
import models._
128
import play.api.Logger
139
import play.api.Play.current
14-
import models._
10+
import play.api.libs.concurrent.Execution.Implicits._
1511
import play.api.libs.iteratee.Enumerator
16-
import services._
17-
import play.api.libs.json._
18-
import play.api.libs.json.{JsObject, JsValue}
1912
import play.api.libs.json.Json.toJson
20-
import javax.inject.{ Singleton, Inject}
21-
import scala.collection.mutable.ListBuffer
22-
import scala.concurrent.{Future, ExecutionContext}
23-
import play.api.libs.concurrent.Execution.Implicits._
24-
import scala.util.parsing.json.JSONArray
25-
import scala.util.{Try, Success, Failure}
26-
import java.util.{Calendar, Date}
27-
import controllers.Utils
13+
import play.api.libs.json.{JsObject, JsValue, _}
14+
import services._
2815

16+
17+
import java.io.ByteArrayOutputStream
18+
import java.security.MessageDigest
19+
import java.util.zip.{Deflater, ZipOutputStream}
20+
import java.util.{Calendar, Date}
21+
import javax.inject.{Inject, Singleton}
2922
import scala.collection.immutable.List
23+
import scala.collection.mutable.ListBuffer
24+
import scala.concurrent.{ExecutionContext, Future}
25+
import scala.util.{Failure, Success, Try}
3026

3127

3228
/**
@@ -72,6 +68,13 @@ class Collections @Inject() (datasets: DatasetService,
7268
collections.addToRootSpaces(c.id, s.id)
7369
events.addSourceEvent(request.user, c.id, c.name, s.id, s.name, EventType.ADD_COLLECTION_SPACE.toString)
7470
})
71+
// index collection
72+
current.plugin[ElasticsearchPlugin].foreach{
73+
_.index(SearchUtils.getElasticsearchObject(c))
74+
}
75+
//Add to Events Table
76+
val option_user = userService.findByIdentity(identity)
77+
events.addObjectEvent(option_user, c.id, c.name, EventType.CREATE_COLLECTION.toString)
7578
Ok(toJson(Map("id" -> id)))
7679
}
7780
case None => Ok(toJson(Map("status" -> "error")))
@@ -237,15 +240,15 @@ class Collections @Inject() (datasets: DatasetService,
237240
}
238241
}
239242

240-
def list(title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
241-
Ok(toJson(listCollections(title, date, limit, Set[Permission](Permission.ViewCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)))
243+
def list(when: Option[String], title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
244+
Ok(toJson(listCollections(when, title, date, limit, Set[Permission](Permission.ViewCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)))
242245
}
243246

244-
def listCanEdit(title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
245-
Ok(toJson(listCollections(title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)))
247+
def listCanEdit(when: Option[String], title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
248+
Ok(toJson(listCollections(when, title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)))
246249
}
247250

248-
def addDatasetToCollectionOptions(datasetId: UUID, title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
251+
def addDatasetToCollectionOptions(when: Option[String], datasetId: UUID, title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
249252
implicit val user = request.user
250253
var listAll = false
251254
var collectionList: List[Collection] = List.empty
@@ -265,7 +268,7 @@ class Collections @Inject() (datasets: DatasetService,
265268
}
266269
}
267270
if(listAll) {
268-
collectionList = listCollections(title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)
271+
collectionList = listCollections(when, title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false, request.user, request.user.fold(false)(_.superAdminMode), exact)
269272
}
270273
Ok(toJson(collectionList))
271274
}
@@ -274,10 +277,10 @@ class Collections @Inject() (datasets: DatasetService,
274277
collections.get(current_collections.map(_.child_collection_ids).flatten).found
275278
}
276279

277-
def listPossibleParents(currentCollectionId : String, title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
280+
def listPossibleParents(when: Option[String], currentCollectionId : String, title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
278281
val selfAndAncestors = collections.getSelfAndAncestors(UUID(currentCollectionId))
279282
val descendants = collections.getAllDescendants(UUID(currentCollectionId)).toList
280-
val allCollections = listCollections(title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false,
283+
val allCollections = listCollections(when, title, date, limit, Set[Permission](Permission.AddResourceToCollection, Permission.EditCollection), false,
281284
request.user, request.user.fold(false)(_.superAdminMode), exact)
282285
val possibleNewParents = allCollections.filter(c =>
283286
if(play.api.Play.current.plugin[services.SpaceSharingPlugin].isDefined) {
@@ -303,29 +306,55 @@ class Collections @Inject() (datasets: DatasetService,
303306
* Returns list of collections based on parameters and permissions.
304307
* TODO this needs to be cleaned up when do permissions for adding to a resource
305308
*/
306-
private def listCollections(title: Option[String], date: Option[String], limit: Int, permission: Set[Permission], mine: Boolean, user: Option[User], superAdmin: Boolean, exact: Boolean) : List[Collection] = {
309+
private def listCollections(when: Option[String], title: Option[String], date: Option[String], limit: Int, permission: Set[Permission], mine: Boolean, user: Option[User], superAdmin: Boolean, exact: Boolean) : List[Collection] = {
307310
if (mine && user.isEmpty) return List.empty[Collection]
308311

309-
(title, date) match {
310-
case (Some(t), Some(d)) => {
312+
(when, title, date) match {
313+
case (Some(w), Some(t), Some(d)) => {
314+
if (mine)
315+
collections.listUser(d, nextPage=(w=="a"), limit, t, user, superAdmin, user.get, exact)
316+
else
317+
collections.listAccess(d, nextPage=(w=="a"), limit, t, permission, user, superAdmin, true,false, exact)
318+
}
319+
case (Some(w), Some(t), None) => {
320+
if (mine)
321+
collections.listUser(limit, t, user, superAdmin, user.get, exact)
322+
else
323+
collections.listAccess(limit, t, permission, user, superAdmin, true,false, exact)
324+
}
325+
case (Some(w), None, Some(d)) => {
326+
if (mine)
327+
collections.listUser(d, nextPage=(w=="a"), limit, user, superAdmin, user.get)
328+
else
329+
collections.listAccess(d, nextPage=(w=="a"), limit, permission, user, superAdmin, true,false)
330+
}
331+
case (Some(w), None, None) => {
332+
if (mine)
333+
collections.listUser(limit, user, superAdmin, user.get)
334+
else
335+
collections.listAccess(limit, permission, user, superAdmin, true,false)
336+
}
337+
338+
// default when to be "after" if not present in parameters. i.e. nextPage=true
339+
case (None, Some(t), Some(d)) => {
311340
if (mine)
312341
collections.listUser(d, true, limit, t, user, superAdmin, user.get, exact)
313342
else
314343
collections.listAccess(d, true, limit, t, permission, user, superAdmin, true,false, exact)
315344
}
316-
case (Some(t), None) => {
345+
case (None, Some(t), None) => {
317346
if (mine)
318347
collections.listUser(limit, t, user, superAdmin, user.get, exact)
319348
else
320349
collections.listAccess(limit, t, permission, user, superAdmin, true,false, exact)
321350
}
322-
case (None, Some(d)) => {
351+
case (None, None, Some(d)) => {
323352
if (mine)
324353
collections.listUser(d, true, limit, user, superAdmin, user.get)
325354
else
326355
collections.listAccess(d, true, limit, permission, user, superAdmin, true,false)
327356
}
328-
case (None, None) => {
357+
case (None, None, None) => {
329358
if (mine)
330359
collections.listUser(limit, user, superAdmin, user.get)
331360
else
@@ -572,6 +601,14 @@ class Collections @Inject() (datasets: DatasetService,
572601
events.addSourceEvent(request.user, c.id, c.name, s.id, s.name, EventType.ADD_COLLECTION_SPACE.toString)
573602
}
574603

604+
// index collection
605+
current.plugin[ElasticsearchPlugin].foreach{
606+
_.index(SearchUtils.getElasticsearchObject(c))
607+
}
608+
//Add to Events Table
609+
val option_user = userService.findByIdentity(identity)
610+
events.addObjectEvent(option_user, c.id, c.name, EventType.CREATE_COLLECTION.toString)
611+
575612
//do stuff with parent here
576613
(request.body \"parentId").asOpt[String] match {
577614
case Some(parentId) => {

app/api/Datasets.scala

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,19 @@ class Datasets @Inject()(
6565
}
6666
}
6767

68-
def list(title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
69-
Ok(toJson(listDatasets(title, date, limit, Set[Permission](Permission.ViewDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
68+
def list(when: Option[String], title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
69+
Ok(toJson(listDatasets(when, title, date, limit, Set[Permission](Permission.ViewDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
7070
}
7171

72-
def listCanEdit(title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
73-
Ok(toJson(listDatasets(title, date, limit, Set[Permission](Permission.AddResourceToDataset, Permission.EditDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
72+
def listCanEdit(when: Option[String], title: Option[String], date: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
73+
Ok(toJson(listDatasets(when, title, date, limit, Set[Permission](Permission.AddResourceToDataset, Permission.EditDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
7474
}
7575

7676
def listMoveFileToDataset(file_id: UUID, title: Option[String], limit: Int, exact: Boolean) = PrivateServerAction { implicit request =>
7777
if (play.Play.application().configuration().getBoolean("datasetFileWithinSpace")) {
7878
Ok(toJson(listDatasetsInSpace(file_id, title, limit, Set[Permission](Permission.AddResourceToDataset, Permission.EditDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
7979
} else {
80-
Ok(toJson(listDatasets(title, None, limit, Set[Permission](Permission.AddResourceToDataset, Permission.EditDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
80+
Ok(toJson(listDatasets(None, title, None, limit, Set[Permission](Permission.AddResourceToDataset, Permission.EditDataset), request.user, request.user.fold(false)(_.superAdminMode), exact)))
8181
}
8282
}
8383

@@ -152,18 +152,31 @@ class Datasets @Inject()(
152152
/**
153153
* Returns list of datasets based on parameters and permissions.
154154
*/
155-
private def listDatasets(title: Option[String], date: Option[String], limit: Int, permission: Set[Permission], user: Option[User], superAdmin: Boolean, exact: Boolean) : List[Dataset] = {
156-
(title, date) match {
157-
case (Some(t), Some(d)) => {
155+
private def listDatasets(when: Option[String], title: Option[String], date: Option[String], limit: Int, permission: Set[Permission], user: Option[User], superAdmin: Boolean, exact: Boolean) : List[Dataset] = {
156+
(when, title, date) match {
157+
case (Some(w), Some(t), Some(d)) => {
158+
datasets.listAccess(d, nextPage=(w=="a"), limit, t, permission, user, superAdmin, true,false, exact)
159+
}
160+
case (Some(w), Some(t), None) => {
161+
datasets.listAccess(limit, t, permission, user, superAdmin, true,false, exact)
162+
}
163+
case (Some(w), None, Some(d)) => {
164+
datasets.listAccess(d, nextPage=(w=="a"), limit, permission, user, superAdmin, true,false)
165+
}
166+
case (Some(w), None, None) => {
167+
datasets.listAccess(limit, permission, user, superAdmin, true,false)
168+
}
169+
// default when to be "after" if not present in parameters. i.e. nextPage=true
170+
case (None, Some(t), Some(d)) => {
158171
datasets.listAccess(d, true, limit, t, permission, user, superAdmin, true,false, exact)
159172
}
160-
case (Some(t), None) => {
173+
case (None, Some(t), None) => {
161174
datasets.listAccess(limit, t, permission, user, superAdmin, true,false, exact)
162175
}
163-
case (None, Some(d)) => {
176+
case (None, None, Some(d)) => {
164177
datasets.listAccess(d, true, limit, permission, user, superAdmin, true,false)
165178
}
166-
case (None, None) => {
179+
case (None, None, None) => {
167180
datasets.listAccess(limit, permission, user, superAdmin, true,false)
168181
}
169182
}

app/api/Files.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,16 @@ class Files @Inject()(
523523
/**
524524
* Upload a file to a specific dataset
525525
*/
526-
def uploadToDataset(dataset_id: UUID, showPreviews: String = "DatasetLevel", originalZipFile: String = "", flagsFromPrevious: String = "", extract: Boolean = true) = PermissionAction(Permission.AddResourceToDataset, Some(ResourceRef(ResourceRef.dataset, dataset_id)))(parse.multipartFormData) { implicit request =>
526+
def uploadToDataset(dataset_id: UUID, showPreviews: String = "DatasetLevel", originalZipFile: String = "", flagsFromPrevious: String = "", extract: Boolean = true, folder_id: Option[String]) = PermissionAction(Permission.AddResourceToDataset, Some(ResourceRef(ResourceRef.dataset, dataset_id)))(parse.multipartFormData) { implicit request =>
527527
datasets.get(dataset_id) match {
528528
case Some(dataset) => {
529-
val uploadedFiles = FileUtils.uploadFilesMultipart(request, Some(dataset), showPreviews = showPreviews, originalZipFile = originalZipFile, flagsFromPrevious = flagsFromPrevious, runExtractors = extract, apiKey = request.apiKey)
529+
var current_folder : Option[Folder] = None
530+
if (folder_id != None) {
531+
if (UUID.isValid(folder_id.get)){
532+
current_folder = folders.get(UUID(folder_id.get))
533+
}
534+
}
535+
val uploadedFiles = FileUtils.uploadFilesMultipart(request, Some(dataset), current_folder, showPreviews = showPreviews, originalZipFile = originalZipFile, flagsFromPrevious = flagsFromPrevious, runExtractors = extract, apiKey = request.apiKey)
530536
uploadedFiles.length match {
531537
case 0 => BadRequest("No files uploaded")
532538
case 1 => Ok(Json.obj("id" -> uploadedFiles.head.id))
@@ -1669,6 +1675,10 @@ class Files @Inject()(
16691675
// notify rabbitmq
16701676
datasets.findByFileIdAllContain(file.id).foreach { ds =>
16711677
routing.fileRemovedFromDataset(file, ds, Utils.baseUrl(request), request.apiKey)
1678+
val ds_spaces = ds.spaces
1679+
for (ds_s <- ds_spaces) {
1680+
spaces.decrementSpaceBytes(ds_s, file.length)
1681+
}
16721682
}
16731683

16741684
//this stmt has to be before files.removeFile

app/api/Selected.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class Selected @Inject()(selections: SelectionService,
8787
selections.get(user.email.get).map(d => {
8888
selections.remove(d.id, user.email.get)
8989
})
90-
Ok(toJson(Map("sucess"->"true")))
90+
Ok(toJson(Map("success"->"true")))
9191
}
9292
}
9393
}
@@ -100,7 +100,7 @@ class Selected @Inject()(selections: SelectionService,
100100
datasets.removeDataset(d.id, Utils.baseUrl(request), request.apiKey, request.user)
101101
selections.remove(d.id, user.email.get)
102102
})
103-
Ok(toJson(Map("sucess"->"true")))
103+
Ok(toJson(Map("success"->"true")))
104104
}
105105
}
106106
}
@@ -189,7 +189,7 @@ class Selected @Inject()(selections: SelectionService,
189189
events.addObjectEvent(request.user, d.id, d.name, EventType.ADD_TAGS_DATASET.toString)
190190
datasets.index(d.id)
191191
})
192-
Ok(toJson(Map("sucess"->"true")))
192+
Ok(toJson(Map("success"->"true")))
193193
}
194194
}
195195
}

app/api/Sensors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import play.api.Play.current
55
import services.PostgresPlugin
66

77
/**
8-
* Metadata about sensors registered with the system. Datastreams can be associalted with sensors.
8+
* Metadata about sensors registered with the system. Datastreams can be associated with sensors.
99
*/
1010
object Sensors extends Controller with ApiController {
1111

0 commit comments

Comments
 (0)