mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 07:07:57 +01:00
#256: versioning API
This commit is contained in:
parent
d8d5249a23
commit
3902d6361e
@ -148,6 +148,8 @@ public class FileDao {
|
|||||||
fileDb.setContent(file.getContent());
|
fileDb.setContent(file.getContent());
|
||||||
fileDb.setOrder(file.getOrder());
|
fileDb.setOrder(file.getOrder());
|
||||||
fileDb.setMimeType(file.getMimeType());
|
fileDb.setMimeType(file.getMimeType());
|
||||||
|
fileDb.setVersionId(file.getVersionId());
|
||||||
|
fileDb.setLatestVersion(file.isLatestVersion());
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
@ -180,11 +182,11 @@ public class FileDao {
|
|||||||
public List<File> getByDocumentId(String userId, String documentId) {
|
public List<File> getByDocumentId(String userId, String documentId) {
|
||||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||||
if (documentId == null) {
|
if (documentId == null) {
|
||||||
Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.userId = :userId order by f.createDate asc");
|
Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.latestVersion = true and f.userId = :userId order by f.createDate asc");
|
||||||
q.setParameter("userId", userId);
|
q.setParameter("userId", userId);
|
||||||
return q.getResultList();
|
return q.getResultList();
|
||||||
}
|
}
|
||||||
Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.deleteDate is null order by f.order asc");
|
Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.latestVersion = true and f.deleteDate is null order by f.order asc");
|
||||||
q.setParameter("documentId", documentId);
|
q.setParameter("documentId", documentId);
|
||||||
return q.getResultList();
|
return q.getResultList();
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,24 @@ public class File implements Loggable {
|
|||||||
@Column(name = "FIL_ORDER_N")
|
@Column(name = "FIL_ORDER_N")
|
||||||
private Integer order;
|
private Integer order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version ID.
|
||||||
|
*/
|
||||||
|
@Column(name = "FIL_IDVERSION_C")
|
||||||
|
private String versionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version number (starting at 0).
|
||||||
|
*/
|
||||||
|
@Column(name = "FIL_VERSION_N", nullable = false)
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if it's the latest version of the file.
|
||||||
|
*/
|
||||||
|
@Column(name = "FIL_LATESTVERSION_B", nullable = false)
|
||||||
|
private boolean latestVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private key to decrypt the file.
|
* Private key to decrypt the file.
|
||||||
* Not saved to database, of course.
|
* Not saved to database, of course.
|
||||||
@ -160,6 +178,33 @@ public class File implements Loggable {
|
|||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getVersionId() {
|
||||||
|
return versionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File setVersionId(String versionId) {
|
||||||
|
this.versionId = versionId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File setVersion(Integer version) {
|
||||||
|
this.version = version;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLatestVersion() {
|
||||||
|
return latestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File setLatestVersion(boolean latestVersion) {
|
||||||
|
this.latestVersion = latestVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
|
@ -212,7 +212,7 @@ public class InboxService extends AbstractScheduledService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the document, create the base ACLs
|
// Save the document, create the base ACLs
|
||||||
document = DocumentUtil.createDocument(document, "admin");
|
DocumentUtil.createDocument(document, "admin");
|
||||||
|
|
||||||
// Add the tag
|
// Add the tag
|
||||||
String tagId = ConfigUtil.getConfigStringValue(ConfigType.INBOX_TAG);
|
String tagId = ConfigUtil.getConfigStringValue(ConfigType.INBOX_TAG);
|
||||||
@ -232,7 +232,7 @@ public class InboxService extends AbstractScheduledService {
|
|||||||
|
|
||||||
// Add files to the document
|
// Add files to the document
|
||||||
for (EmailUtil.FileContent fileContent : mailContent.getFileContentList()) {
|
for (EmailUtil.FileContent fileContent : mailContent.getFileContentList()) {
|
||||||
FileUtil.createFile(fileContent.getName(), fileContent.getFile(), fileContent.getSize(),
|
FileUtil.createFile(fileContent.getName(), null, fileContent.getFile(), fileContent.getSize(),
|
||||||
document.getLanguage(), "admin", document.getId());
|
document.getLanguage(), "admin", document.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ public class FileUtil {
|
|||||||
* Create a new file.
|
* Create a new file.
|
||||||
*
|
*
|
||||||
* @param name File name, can be null
|
* @param name File name, can be null
|
||||||
|
* @param previousFileId ID of the previous version of the file, if the new file is a new version
|
||||||
* @param unencryptedFile Path to the unencrypted file
|
* @param unencryptedFile Path to the unencrypted file
|
||||||
* @param fileSize File size
|
* @param fileSize File size
|
||||||
* @param language File language, can be null if associated to no document
|
* @param language File language, can be null if associated to no document
|
||||||
@ -106,7 +107,7 @@ public class FileUtil {
|
|||||||
* @return File ID
|
* @return File ID
|
||||||
* @throws Exception e
|
* @throws Exception e
|
||||||
*/
|
*/
|
||||||
public static String createFile(String name, Path unencryptedFile, long fileSize, String language, String userId, String documentId) throws Exception {
|
public static String createFile(String name, String previousFileId, Path unencryptedFile, long fileSize, String language, String userId, String documentId) throws Exception {
|
||||||
// Validate mime type
|
// Validate mime type
|
||||||
String mimeType;
|
String mimeType;
|
||||||
try {
|
try {
|
||||||
@ -132,22 +133,42 @@ public class FileUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get files of this document
|
// Prepare the file
|
||||||
FileDao fileDao = new FileDao();
|
|
||||||
int order = 0;
|
|
||||||
if (documentId != null) {
|
|
||||||
for (File file : fileDao.getByDocumentId(userId, documentId)) {
|
|
||||||
file.setOrder(order++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file
|
|
||||||
File file = new File();
|
File file = new File();
|
||||||
file.setOrder(order);
|
file.setOrder(0);
|
||||||
|
file.setVersion(0);
|
||||||
|
file.setLatestVersion(true);
|
||||||
file.setDocumentId(documentId);
|
file.setDocumentId(documentId);
|
||||||
file.setName(StringUtils.abbreviate(name, 200));
|
file.setName(StringUtils.abbreviate(name, 200));
|
||||||
file.setMimeType(mimeType);
|
file.setMimeType(mimeType);
|
||||||
file.setUserId(userId);
|
file.setUserId(userId);
|
||||||
|
|
||||||
|
// Get files of this document
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
if (documentId != null) {
|
||||||
|
if (previousFileId == null) {
|
||||||
|
// It's not a new version, so put it in last order
|
||||||
|
file.setOrder(fileDao.getByDocumentId(userId, documentId).size());
|
||||||
|
} else {
|
||||||
|
// It's a new version, update the previous version
|
||||||
|
File previousFile = fileDao.getActiveById(previousFileId);
|
||||||
|
if (previousFile == null || !previousFile.getDocumentId().equals(documentId)) {
|
||||||
|
throw new IOException("Previous version mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousFile.getVersionId() == null) {
|
||||||
|
previousFile.setVersionId(UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
previousFile.setLatestVersion(false);
|
||||||
|
file.setVersionId(previousFile.getVersionId());
|
||||||
|
file.setVersion(previousFile.getVersion() + 1);
|
||||||
|
|
||||||
|
fileDao.update(previousFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file
|
||||||
String fileId = fileDao.create(file, userId);
|
String fileId = fileDao.create(file, userId);
|
||||||
|
|
||||||
// Save the file
|
// Save the file
|
||||||
|
@ -1 +1 @@
|
|||||||
db.version=21
|
db.version=22
|
@ -0,0 +1,5 @@
|
|||||||
|
alter table T_FILE add column FIL_VERSION_N int not null default 0;
|
||||||
|
alter table T_FILE add column FIL_LATESTVERSION_B bit not null default 1;
|
||||||
|
alter table T_FILE add column FIL_IDVERSION_C varchar(36);
|
||||||
|
|
||||||
|
update T_CONFIG set CFG_VALUE_C = '22' where CFG_ID_C = 'DB_VERSION';
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=21
|
db.version=22
|
@ -900,7 +900,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the document, create the base ACLs
|
// Save the document, create the base ACLs
|
||||||
document = DocumentUtil.createDocument(document, principal.getId());
|
DocumentUtil.createDocument(document, principal.getId());
|
||||||
|
|
||||||
// Raise a document created event
|
// Raise a document created event
|
||||||
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
DocumentCreatedAsyncEvent documentCreatedAsyncEvent = new DocumentCreatedAsyncEvent();
|
||||||
@ -911,7 +911,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
// Add files to the document
|
// Add files to the document
|
||||||
try {
|
try {
|
||||||
for (EmailUtil.FileContent fileContent : mailContent.getFileContentList()) {
|
for (EmailUtil.FileContent fileContent : mailContent.getFileContentList()) {
|
||||||
FileUtil.createFile(fileContent.getName(), fileContent.getFile(), fileContent.getSize(),
|
FileUtil.createFile(fileContent.getName(), null, fileContent.getFile(), fileContent.getSize(),
|
||||||
document.getLanguage(), principal.getId(), document.getId());
|
document.getLanguage(), principal.getId(), document.getId());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -66,6 +66,7 @@ public class FileResource extends BaseResource {
|
|||||||
* @apiName PutFile
|
* @apiName PutFile
|
||||||
* @apiGroup File
|
* @apiGroup File
|
||||||
* @apiParam {String} id Document ID
|
* @apiParam {String} id Document ID
|
||||||
|
* @apiParam {String} previousFileId ID of the file to replace by this new version
|
||||||
* @apiParam {String} file File data
|
* @apiParam {String} file File data
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiSuccess {String} id File ID
|
* @apiSuccess {String} id File ID
|
||||||
@ -88,6 +89,7 @@ public class FileResource extends BaseResource {
|
|||||||
@Consumes("multipart/form-data")
|
@Consumes("multipart/form-data")
|
||||||
public Response add(
|
public Response add(
|
||||||
@FormDataParam("id") String documentId,
|
@FormDataParam("id") String documentId,
|
||||||
|
@FormDataParam("previousFileId") String previousFileId,
|
||||||
@FormDataParam("file") FormDataBodyPart fileBodyPart) {
|
@FormDataParam("file") FormDataBodyPart fileBodyPart) {
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
@ -122,7 +124,7 @@ public class FileResource extends BaseResource {
|
|||||||
try {
|
try {
|
||||||
String name = fileBodyPart.getContentDisposition() != null ?
|
String name = fileBodyPart.getContentDisposition() != null ?
|
||||||
URLDecoder.decode(fileBodyPart.getContentDisposition().getFileName(), "UTF-8") : null;
|
URLDecoder.decode(fileBodyPart.getContentDisposition().getFileName(), "UTF-8") : null;
|
||||||
String fileId = FileUtil.createFile(name, unencryptedFile, fileSize, documentDto == null ?
|
String fileId = FileUtil.createFile(name, previousFileId, unencryptedFile, fileSize, documentDto == null ?
|
||||||
null : documentDto.getLanguage(), principal.getId(), documentId);
|
null : documentDto.getLanguage(), principal.getId(), documentId);
|
||||||
|
|
||||||
// Always return OK
|
// Always return OK
|
||||||
@ -392,6 +394,7 @@ public class FileResource extends BaseResource {
|
|||||||
* @apiSuccess {String} files.id ID
|
* @apiSuccess {String} files.id ID
|
||||||
* @apiSuccess {String} files.mimetype MIME type
|
* @apiSuccess {String} files.mimetype MIME type
|
||||||
* @apiSuccess {String} files.name File name
|
* @apiSuccess {String} files.name File name
|
||||||
|
* @apiSuccess {String} files.version Zero-based version number
|
||||||
* @apiSuccess {String} files.processing True if the file is currently processing
|
* @apiSuccess {String} files.processing True if the file is currently processing
|
||||||
* @apiSuccess {String} files.document_id Document ID
|
* @apiSuccess {String} files.document_id Document ID
|
||||||
* @apiSuccess {String} files.create_date Create date (timestamp)
|
* @apiSuccess {String} files.create_date Create date (timestamp)
|
||||||
@ -433,6 +436,7 @@ public class FileResource extends BaseResource {
|
|||||||
.add("id", fileDb.getId())
|
.add("id", fileDb.getId())
|
||||||
.add("processing", FileUtil.isProcessingFile(fileDb.getId()))
|
.add("processing", FileUtil.isProcessingFile(fileDb.getId()))
|
||||||
.add("name", JsonUtil.nullable(fileDb.getName()))
|
.add("name", JsonUtil.nullable(fileDb.getName()))
|
||||||
|
.add("version", fileDb.getVersion())
|
||||||
.add("mimetype", fileDb.getMimeType())
|
.add("mimetype", fileDb.getMimeType())
|
||||||
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
|
.add("document_id", JsonUtil.nullable(fileDb.getDocumentId()))
|
||||||
.add("create_date", fileDb.getCreateDate().getTime())
|
.add("create_date", fileDb.getCreateDate().getTime())
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=21
|
db.version=22
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=21
|
db.version=22
|
@ -140,9 +140,11 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(2, files.size());
|
Assert.assertEquals(2, files.size());
|
||||||
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
|
Assert.assertEquals(file1Id, files.getJsonObject(0).getString("id"));
|
||||||
Assert.assertEquals("PIA00452.jpg", files.getJsonObject(0).getString("name"));
|
Assert.assertEquals("PIA00452.jpg", files.getJsonObject(0).getString("name"));
|
||||||
|
Assert.assertEquals(0, files.getJsonObject(0).getInt("version"));
|
||||||
Assert.assertEquals(163510L, files.getJsonObject(0).getJsonNumber("size").longValue());
|
Assert.assertEquals(163510L, files.getJsonObject(0).getJsonNumber("size").longValue());
|
||||||
Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id"));
|
Assert.assertEquals(file2Id, files.getJsonObject(1).getString("id"));
|
||||||
Assert.assertEquals("PIA00452.jpg", files.getJsonObject(1).getString("name"));
|
Assert.assertEquals("PIA00452.jpg", files.getJsonObject(1).getString("name"));
|
||||||
|
Assert.assertEquals(0, files.getJsonObject(1).getInt("version"));
|
||||||
|
|
||||||
// Rename a file
|
// Rename a file
|
||||||
target().path("file/" + file1Id)
|
target().path("file/" + file1Id)
|
||||||
@ -225,6 +227,38 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
target().path("/file/" + file2Id + "/process").request()
|
target().path("/file/" + file2Id + "/process").request()
|
||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token)
|
||||||
.post(Entity.form(new Form()), JsonObject.class);
|
.post(Entity.form(new Form()), JsonObject.class);
|
||||||
|
|
||||||
|
// Add a new version to a file
|
||||||
|
String file3Id;
|
||||||
|
try (InputStream is0 = Resources.getResource("file/document.txt").openStream()) {
|
||||||
|
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is0, "document.txt");
|
||||||
|
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||||
|
json = target()
|
||||||
|
.register(MultiPartFeature.class)
|
||||||
|
.path("/file").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token)
|
||||||
|
.put(Entity.entity(
|
||||||
|
multiPart
|
||||||
|
.field("id", document1Id)
|
||||||
|
.field("previousFileId", file2Id)
|
||||||
|
.bodyPart(streamDataBodyPart),
|
||||||
|
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||||
|
file3Id = json.getString("id");
|
||||||
|
Assert.assertNotNull(file2Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the newly created version
|
||||||
|
json = target().path("/file/list")
|
||||||
|
.queryParam("id", document1Id)
|
||||||
|
.request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, file1Token)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
files = json.getJsonArray("files");
|
||||||
|
Assert.assertEquals(1, files.size());
|
||||||
|
Assert.assertEquals(file3Id, files.getJsonObject(0).getString("id"));
|
||||||
|
Assert.assertEquals("document.txt", files.getJsonObject(0).getString("name"));
|
||||||
|
Assert.assertEquals(1, files.getJsonObject(0).getInt("version"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user