Skip to content

Commit

Permalink
Merge pull request #9 from AO-StreetArt/thumbnailSupport
Browse files Browse the repository at this point in the history
#2: Add support for thumbnails and querying asset metadata
  • Loading branch information
AO-StreetArt committed Oct 7, 2018
2 parents ab2b3b0 + 40e6be9 commit 19ce9d6
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 33 deletions.
29 changes: 29 additions & 0 deletions docs/pages/Asset_API.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Asset Creation
Create a new asset.

:query string content-type: Optional. The content type of the asset (ie. application/json).
:query string file-type: Optional. The file type of the asset (ie. json).
:query string asset-type: Optional. Valid options are 'standard' (for normal assets), and 'thumbnail' for thumbnail assets.
:query string scene: Optional. Used to associate a thumbnail to a scene.
:query string object: Optional. Used to associate a thumbnail to an object.
:query string parent: Optional. Used to associate a thumbnail to an asset.
:reqheader Content-Type: multipart/*
:statuscode 200: Success

Expand All @@ -27,6 +33,12 @@ Asset Update
Update an existing Asset. This returns a new key for the asset, and adds
an entry to the associated Asset History.

:query string content-type: Optional. The content type of the asset (ie. application/json).
:query string file-type: Optional. The file type of the asset (ie. json).
:query string asset-type: Optional. Valid options are 'standard' (for normal assets), and 'thumbnail' for thumbnail assets.
:query string scene: Optional. Used to associate a thumbnail to a scene.
:query string object: Optional. Used to associate a thumbnail to an object.
:query string parent: Optional. Used to associate a thumbnail to an asset.
:reqheader Content-Type: multipart/*
:statuscode 200: Success

Expand All @@ -43,6 +55,23 @@ Asset Retrieval

.. include:: _examples/asset/asset_get.rst

Asset Metadata Query
~~~~~~~~~~~~~~~~~~~~

.. http:get:: /v1/asset
Query Asset Metadata based on various attributes.

:query string content-type: Optional. The content type of the asset (ie. application/json).
:query string file-type: Optional. The file type of the asset (ie. json).
:query string asset-type: Optional. Valid options are 'standard' (for normal assets), and 'thumbnail' for thumbnail assets.
:query string scene: Optional. Used to associate a thumbnail to a scene.
:query string object: Optional. Used to associate a thumbnail to an object.
:query string parent: Optional. Used to associate a thumbnail to an asset.
:statuscode 200: Success

.. include:: _examples/asset/asset_query.rst

Asset Deletion
~~~~~~~~~~~~~~

Expand Down
33 changes: 33 additions & 0 deletions docs/pages/Thumbnail_API.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.. _thumbnail_api:

Thumbnail API
=============

A Thumbnail is an Asset with asset-type = 'thumbnail'. In general, these are
2D images which can be displayed in a UI for an asset or scene.

In general, thumbnails are not expected to be updated or versions like other
assets. Because of this, users should delete an old thumbnail and add a new
one, rather than calling the Asset Update API.

Asset Thumbnail Retrieval
~~~~~~~~~~~~~~~~~~~~~~~~~

.. http:post:: /v1/asset-thumbnail/{parent}
Get an asset thumbnail by the Id of it's parent asset.

:statuscode 200: Success

.. include:: _examples/asset/get_asset_thumbnail.rst

Scene Thumbnail Retrieval
~~~~~~~~~~~~~~~~~~~~~~~~~

.. http:post:: /v1/scene-thumbnail/{scene}
Get a scene thumbnail by the Id of it's scene.

:statuscode 200: Success

.. include:: _examples/asset/get_scene_thumbnail.rst
4 changes: 4 additions & 0 deletions docs/pages/_examples/asset/asset_query.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. http:example:: curl wget httpie python-requests
GET /v1/asset?file-type=obj HTTP/1.1
Host: localhost:5635
4 changes: 4 additions & 0 deletions docs/pages/_examples/asset/get_asset_thumbnail.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. http:example:: curl wget httpie python-requests
GET /v1/asset-thumbnail/{parent} HTTP/1.1
Host: localhost:5635
4 changes: 4 additions & 0 deletions docs/pages/_examples/asset/get_scene_thumbnail.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. http:example:: curl wget httpie python-requests
GET /v1/scene-thumbnail/{scene} HTTP/1.1
Host: localhost:5635
2 changes: 1 addition & 1 deletion src/main/java/com/ao/avc/AvcApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
Expand Down
192 changes: 161 additions & 31 deletions src/main/java/com/ao/avc/controller/AssetController.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@

import com.ao.avc.dao.AssetHistoryRepository;
import com.ao.avc.model.AssetHistory;
import com.ao.avc.model.AssetMetadata;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.GridFSFindIterable;
import com.mongodb.client.gridfs.model.GridFSFile;

import java.io.IOException;
import java.net.MalformedURLException;
Expand All @@ -38,6 +40,8 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.bson.Document;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
Expand Down Expand Up @@ -83,19 +87,14 @@ public class AssetController {
private static final Logger logger =
LogManager.getLogger("avc.AssetController");

private String saveAsset(MultipartFile file, String contentType, String fileType) {
DBObject metaData = new BasicDBObject();
metaData.put("content-type", contentType);
metaData.put("file-type", fileType);
String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
metaData.put("created-dttm", timeStamp);
private String saveAsset(MultipartFile file, DBObject metaData, String fileType) {
String newId = "";
if (file == null) {
return newId;
}
try {
newId =
gridFsTemplate.store(file.getInputStream(), "asset." + fileType, contentType, metaData).toString();
gridFsTemplate.store(file.getInputStream(), "asset." + fileType, metaData).toString();
} catch (Exception e) {
logger.error("Error Saving Asset to Mongo: ", e);
}
Expand Down Expand Up @@ -125,20 +124,26 @@ private void updateAssetHistory(String sceneName, String objectName,
}
}

/**
* Retrieve an Asset.
*/
@GetMapping("/v1/asset/{key}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String key)
throws MalformedURLException, IOException {
private ResponseEntity<Resource> getAsset(String id, boolean isThumbnail,
boolean isSceneThumbnail) throws MalformedURLException {
logger.info("Responding to Asset Get Request");
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "text/plain");
// Load the file from Mongo
GridFSFile gridFsdbFile;
try {
gridFsdbFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(key)));
Query query = new Query();
if (isThumbnail) {
query.addCriteria(Criteria.where("metadata.asset-type").is("thumbnail"));
if (isSceneThumbnail) {
query.addCriteria(Criteria.where("metadata.scene").is(id));
} else {
query.addCriteria(Criteria.where("metadata.parent").is(id));
}
} else {
query.addCriteria(Criteria.where("_id").is(id));
}
gridFsdbFile = gridFsTemplate.findOne(query);
} catch (Exception e) {
logger.error("Error Retrieving Asset from Mongo: ", e);
return new ResponseEntity<Resource>(new UrlResource("http://server.error"), responseHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
Expand All @@ -147,11 +152,113 @@ public ResponseEntity<Resource> serveFile(@PathVariable String key)
logger.error("Null Asset Retrieved from Mongo");
return new ResponseEntity<Resource>(new UrlResource("http://server.error"), responseHeaders, HttpStatus.NO_CONTENT);
}
GridFSDownloadStream gridFSDownloadStream = gridFsBucket.openDownloadStream(gridFsdbFile.getObjectId());
GridFsResource gridResource = new GridFsResource(gridFsdbFile,gridFSDownloadStream);
InputStreamResource fileResource = new InputStreamResource(gridResource.getInputStream());
return ResponseEntity.ok().contentLength(gridFsdbFile.getLength()).header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + key + "\"").body(fileResource);

// Send the file back in a response
InputStreamResource fileResource;
try {
GridFSDownloadStream gridFsDownloadStream =
gridFsBucket.openDownloadStream(gridFsdbFile.getObjectId());
GridFsResource gridResource = new GridFsResource(gridFsdbFile,gridFsDownloadStream);
fileResource = new InputStreamResource(gridResource.getInputStream());
} catch (Exception e) {
logger.error("Error Loading Asset from Mongo: ", e);
return new ResponseEntity<Resource>(new UrlResource("http://server.error"), responseHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok().contentLength(gridFsdbFile.getLength())
.header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\"" + id + "\"")
.body(fileResource);
}

/**
* Find Assets by metadata.
*/
@GetMapping("/v1/asset")
@ResponseBody
public ResponseEntity<List<AssetMetadata>> findAssets(
@RequestParam(value = "scene", defaultValue = "") String sceneId,
@RequestParam(value = "object", defaultValue = "") String objectId,
@RequestParam(value = "parent", defaultValue = "") String parentId,
@RequestParam(value = "content-type", defaultValue = "") String contentType,
@RequestParam(value = "file-type", defaultValue = "") String fileType,
@RequestParam(value = "asset-type", defaultValue = "standard") String assetType)
throws MalformedURLException, IOException {
logger.info("Responding to Asset Find Request");
HttpHeaders responseHeaders = new HttpHeaders();
// Load the file from Mongo
GridFSFindIterable gridFsdbFiles;
try {
Query query = new Query();
if (!(sceneId.isEmpty())) {
query.addCriteria(Criteria.where("metadata.scene").is(sceneId));
} else if (!(objectId.isEmpty())) {
query.addCriteria(Criteria.where("metadata.object").is(objectId));
} else if (!(parentId.isEmpty())) {
query.addCriteria(Criteria.where("metadata.parent").is(parentId));
} else if (!(contentType.isEmpty())) {
query.addCriteria(Criteria.where("metadata.content-type").is(contentType));
} else if (!(fileType.isEmpty())) {
query.addCriteria(Criteria.where("metadata.file-type").is(fileType));
} else if (!(assetType.isEmpty())) {
query.addCriteria(Criteria.where("metadata.asset-type").is(assetType));
}
gridFsdbFiles = gridFsTemplate.find(query);
} catch (Exception e) {
logger.error("Error Retrieving Asset from Mongo: ", e);
return new ResponseEntity<List<AssetMetadata>>(
new ArrayList<AssetMetadata>(), responseHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}
if (gridFsdbFiles == null) {
logger.error("Null Asset Retrieved from Mongo");
return new ResponseEntity<List<AssetMetadata>>(
new ArrayList<AssetMetadata>(), responseHeaders, HttpStatus.NO_CONTENT);
}
List<AssetMetadata> returnList = new ArrayList<AssetMetadata>();
for (GridFSFile mongoFile : gridFsdbFiles) {
Document metaDoc = mongoFile.getMetadata();
AssetMetadata returnDoc = new AssetMetadata();
logger.info(metaDoc.toString());
returnDoc.setKey(mongoFile.getId().toString());
returnDoc.setScene(metaDoc.getString("scene"));
returnDoc.setObject(metaDoc.getString("object"));
returnDoc.setParent(metaDoc.getString("parent"));
returnDoc.setContentType(metaDoc.getString("content-type"));
returnDoc.setFileType(metaDoc.getString("file-type"));
returnDoc.setAssetType(metaDoc.getString("asset-type"));
returnDoc.setCreatedTimestamp(metaDoc.getString("created-dttm"));
returnList.add(returnDoc);
}
responseHeaders.set("Content-Type", "application/json");
return new ResponseEntity<List<AssetMetadata>>(returnList, responseHeaders, HttpStatus.OK);
}

/**
* Retrieve an Asset.
*/
@GetMapping("/v1/asset/{key}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String key)
throws MalformedURLException, IOException {
return getAsset(key, false, false);
}

/**
* Retrieve a Thumbnail by Parent ID.
*/
@GetMapping("/v1/asset-thumbnail/{parent}")
@ResponseBody
public ResponseEntity<Resource> serveThumbnail(@PathVariable String parent)
throws MalformedURLException, IOException {
return getAsset(parent, true, false);
}

/**
* Retrieve a Thumbnail by Scene ID.
*/
@GetMapping("/v1/scene-thumbnail/{scene}")
@ResponseBody
public ResponseEntity<Resource> serveSceneThumbnail(@PathVariable String scene)
throws MalformedURLException, IOException {
return getAsset(scene, true, true);
}

/**
Expand All @@ -162,19 +269,30 @@ public ResponseEntity<Resource> serveFile(@PathVariable String key)
headers = ("content-type=multipart/*"),
method = RequestMethod.POST)
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "scene", defaultValue = "") String sceneId,
@RequestParam(value = "object", defaultValue = "") String objectId,
@RequestParam(value = "parent", defaultValue = "") String parentId,
@RequestParam(value = "content-type", defaultValue = "text/plain") String contentType,
@RequestParam(value = "file-type", defaultValue = "txt") String fileType,
@RequestParam(value = "scene", defaultValue = "") String sceneName,
@RequestParam(value = "object", defaultValue = "") String objectName) {
@RequestParam(value = "asset-type", defaultValue = "standard") String assetType) {
logger.info("Responding to Asset Save Request");
HttpStatus returnCode = HttpStatus.OK;
String newId = saveAsset(file, contentType, fileType);
DBObject metaData = new BasicDBObject();
metaData.put("scene", sceneId);
metaData.put("object", objectId);
metaData.put("parent", parentId);
metaData.put("content-type", contentType);
metaData.put("file-type", fileType);
metaData.put("asset-type", assetType);
String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
metaData.put("created-dttm", timeStamp);
String newId = saveAsset(file, metaData, fileType);
HttpHeaders responseHeaders = new HttpHeaders();
if (newId.isEmpty()) {
return new ResponseEntity<String>("Failure", responseHeaders,
HttpStatus.INTERNAL_SERVER_ERROR);
}
// TO-DO: Add Asset to scene and/or object in CLyman and Crazy Ivan
HttpStatus returnCode = HttpStatus.OK;
return new ResponseEntity<String>(newId, responseHeaders, returnCode);
}

Expand All @@ -187,19 +305,31 @@ public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFi
method = RequestMethod.POST)
public ResponseEntity<String> handleFileUpdate(@PathVariable String key,
@RequestParam("file") MultipartFile file,
@RequestParam(value = "scene", defaultValue = "") String sceneId,
@RequestParam(value = "object", defaultValue = "") String objectId,
@RequestParam(value = "parent", defaultValue = "") String parentId,
@RequestParam(value = "content-type", defaultValue = "text/plain") String contentType,
@RequestParam(value = "file-type", defaultValue = "txt") String fileType,
@RequestParam(value = "scene", defaultValue = "") String sceneName,
@RequestParam(value = "object", defaultValue = "") String objectName) {
HttpStatus returnCode = HttpStatus.OK;
String newId = saveAsset(file, contentType, fileType);
@RequestParam(value = "asset-type", defaultValue = "standard") String assetType) {
// TO-DO: Only accept updates for non-thumbnail assets
DBObject metaData = new BasicDBObject();
metaData.put("scene", sceneId);
metaData.put("object", objectId);
metaData.put("parent", parentId);
metaData.put("content-type", contentType);
metaData.put("file-type", fileType);
metaData.put("asset-type", assetType);
String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
metaData.put("created-dttm", timeStamp);
String newId = saveAsset(file, metaData, fileType);
HttpHeaders responseHeaders = new HttpHeaders();
if (newId.isEmpty()) {
return new ResponseEntity<String>("Failure", responseHeaders,
HttpStatus.INTERNAL_SERVER_ERROR);
}
// TO-DO: Update Scene and/or Object in CLyman/CrazyIvan
updateAssetHistory(sceneName, objectName, newId, key);
updateAssetHistory(sceneId, objectId, newId, key);
HttpStatus returnCode = HttpStatus.OK;
return new ResponseEntity<String>(newId, responseHeaders, returnCode);
}

Expand Down

0 comments on commit 19ce9d6

Please sign in to comment.