public CompletableFuture<Void> storeAttachmentAsync(Attachment attachment, MessageId ownerMessageId) { return blobStore.save(attachment.getBytes()) .thenApply(blobId -> CassandraAttachmentDAOV2.from(attachment, blobId)) .thenCompose(daoAttachment -> storeAttachmentWithIndex(daoAttachment, ownerMessageId)); }
@Override public void storeAttachmentForOwner(Attachment attachment, Username owner) throws MailboxException { ownerDAO.addOwner(attachment.getAttachmentId(), owner) .thenCompose(any -> blobStore.save(attachment.getBytes())) .thenApply(blobId -> CassandraAttachmentDAOV2.from(attachment, blobId)) .thenCompose(attachmentDAOV2::storeAttachment) .join(); }
private Result migrateAttachment(Attachment attachment) { try { blobStore.save(attachment.getBytes()) .thenApply(blobId -> CassandraAttachmentDAOV2.from(attachment, blobId)) .thenCompose(attachmentDAOV2::storeAttachment) .thenCompose(any -> attachmentDAOV1.deleteAttachment(attachment.getAttachmentId())) .join(); return Result.COMPLETED; } catch (Exception e) { LOGGER.error("Error while performing attachmentDAO V2 migration", e); return Result.PARTIAL; } } }
@Test void runShouldReturnPartialWhenSavingAttachmentV2Fail() { CassandraAttachmentDAO attachmentDAO = mock(CassandraAttachmentDAO.class); CassandraAttachmentDAOV2 attachmentDAOV2 = mock(CassandraAttachmentDAOV2.class); CassandraBlobsDAO blobsDAO = mock(CassandraBlobsDAO.class); migration = new AttachmentV2Migration(attachmentDAO, attachmentDAOV2, blobsDAO); when(attachmentDAO.retrieveAll()).thenReturn(Stream.of( attachment1, attachment2)); when(blobsDAO.save(attachment1.getBytes())) .thenReturn(CompletableFuture.completedFuture(BLOB_ID_FACTORY.forPayload(attachment1.getBytes()))); when(blobsDAO.save(attachment2.getBytes())) .thenReturn(CompletableFuture.completedFuture(BLOB_ID_FACTORY.forPayload(attachment2.getBytes()))); when(attachmentDAOV2.storeAttachment(any())).thenThrow(new RuntimeException()); assertThat(migration.run()).isEqualTo(Migration.Result.PARTIAL); }
@Test void runShouldReturnPartialWhenAtLeastOneAttachmentMigrationFails() { CassandraAttachmentDAO attachmentDAO = mock(CassandraAttachmentDAO.class); CassandraAttachmentDAOV2 attachmentDAOV2 = mock(CassandraAttachmentDAOV2.class); CassandraBlobsDAO blobsDAO = mock(CassandraBlobsDAO.class); migration = new AttachmentV2Migration(attachmentDAO, attachmentDAOV2, blobsDAO); when(attachmentDAO.retrieveAll()).thenReturn(Stream.of( attachment1, attachment2)); when(blobsDAO.save(attachment1.getBytes())) .thenReturn(CompletableFuture.completedFuture(BLOB_ID_FACTORY.forPayload(attachment1.getBytes()))); when(blobsDAO.save(attachment2.getBytes())) .thenThrow(new RuntimeException()); when(attachmentDAOV2.storeAttachment(any())).thenReturn(CompletableFuture.completedFuture(null)); when(attachmentDAO.deleteAttachment(any())).thenReturn(CompletableFuture.completedFuture(null)); assertThat(migration.run()).isEqualTo(Migration.Result.PARTIAL); }
@Test void runShouldReturnPartialWhenDeleteV1AttachmentFail() { CassandraAttachmentDAO attachmentDAO = mock(CassandraAttachmentDAO.class); CassandraAttachmentDAOV2 attachmentDAOV2 = mock(CassandraAttachmentDAOV2.class); CassandraBlobsDAO blobsDAO = mock(CassandraBlobsDAO.class); migration = new AttachmentV2Migration(attachmentDAO, attachmentDAOV2, blobsDAO); when(attachmentDAO.retrieveAll()).thenReturn(Stream.of( attachment1, attachment2)); when(blobsDAO.save(attachment1.getBytes())) .thenReturn(CompletableFuture.completedFuture(BLOB_ID_FACTORY.forPayload(attachment1.getBytes()))); when(blobsDAO.save(attachment2.getBytes())) .thenReturn(CompletableFuture.completedFuture(BLOB_ID_FACTORY.forPayload(attachment2.getBytes()))); when(attachmentDAOV2.storeAttachment(any())).thenReturn(CompletableFuture.completedFuture(null)); when(attachmentDAO.deleteAttachment(any())).thenThrow(new RuntimeException()); assertThat(migration.run()).isEqualTo(Migration.Result.PARTIAL); }
@Test public void getByteShouldReturnByteArrayRepresentingTheAttachment() { String input = "mystream"; Attachment attachment = Attachment.builder() .bytes(input.getBytes(CHARSET)) .type("content") .build(); byte[] bytes = attachment.getBytes(); assertThat(new String(bytes, CHARSET)).isEqualTo(input); }
@Test void migrationShouldMoveAttachmentsToV2() throws Exception { attachmentDAO.storeAttachment(attachment1).join(); attachmentDAO.storeAttachment(attachment2).join(); migration.run(); assertThat(attachmentDAOV2.getAttachment(ATTACHMENT_ID).join()) .contains(CassandraAttachmentDAOV2.from(attachment1, BLOB_ID_FACTORY.forPayload(attachment1.getBytes()))); assertThat(attachmentDAOV2.getAttachment(ATTACHMENT_ID_2).join()) .contains(CassandraAttachmentDAOV2.from(attachment2, BLOB_ID_FACTORY.forPayload(attachment2.getBytes()))); assertThat(blobsDAO.readBytes(BLOB_ID_FACTORY.forPayload(attachment1.getBytes())).join()) .isEqualTo(attachment1.getBytes()); assertThat(blobsDAO.readBytes(BLOB_ID_FACTORY.forPayload(attachment2.getBytes())).join()) .isEqualTo(attachment2.getBytes()); }
public CompletableFuture<Void> storeAttachment(Attachment attachment) throws IOException { return cassandraAsyncExecutor.executeVoid( insertStatement.bind() .setString(ID, attachment.getAttachmentId().getId()) .setLong(SIZE, attachment.getSize()) .setString(TYPE, attachment.getType()) .setBytes(PAYLOAD, ByteBuffer.wrap(attachment.getBytes()))); }
@Test void getAttachmentsShouldReturnV2WhenV2AndV1() throws Exception { Attachment attachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_1) .type("application/json") .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) .build(); Attachment otherAttachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_1) .type("application/json") .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) .build(); BlobId blobId = blobsDAO.save(attachment.getBytes()).join(); attachmentDAOV2.storeAttachment(CassandraAttachmentDAOV2.from(attachment, blobId)).join(); attachmentDAO.storeAttachment(otherAttachment).join(); assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1))) .containsExactly(attachment); }
@Test void getAttachmentsShouldCombineElementsFromV1AndV2() throws Exception { Attachment attachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_1) .type("application/json") .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) .build(); Attachment otherAttachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_2) .type("application/json") .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) .build(); BlobId blobId = blobsDAO.save(attachment.getBytes()).join(); attachmentDAOV2.storeAttachment(CassandraAttachmentDAOV2.from(attachment, blobId)).join(); attachmentDAO.storeAttachment(otherAttachment).join(); assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1, ATTACHMENT_ID_2))) .containsExactly(attachment, otherAttachment); } }
@Test void getAttachmentShouldReturnV2WhenPresentInV1AndV2() throws Exception { Attachment attachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_1) .type("application/json") .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) .build(); Attachment otherAttachment = Attachment.builder() .attachmentId(ATTACHMENT_ID_1) .type("application/json") .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) .build(); BlobId blobId = blobsDAO.save(attachment.getBytes()).join(); attachmentDAOV2.storeAttachment(CassandraAttachmentDAOV2.from(attachment, blobId)).join(); attachmentDAO.storeAttachment(otherAttachment).join(); assertThat(attachmentMapper.getAttachment(ATTACHMENT_ID_1)) .isEqualTo(attachment); }