/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. * <p> */ package org.olat.restapi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.UUID; import javax.imageio.ImageIO; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import org.junit.Before; import org.junit.Test; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; import org.olat.core.util.FileUtils; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.modules.fo.Forum; import org.olat.modules.fo.Message; import org.olat.modules.fo.manager.ForumManager; import org.olat.modules.fo.restapi.MessageVO; import org.olat.modules.fo.restapi.MessageVOes; import org.olat.modules.fo.restapi.ReplyVO; import org.olat.restapi.support.vo.File64VO; import org.olat.restapi.support.vo.FileVO; import org.olat.test.JunitTestHelper; import org.olat.test.OlatJerseyTestCase; import org.springframework.beans.factory.annotation.Autowired; public class ForumTest extends OlatJerseyTestCase { private static Forum forum; private static Message m1, m2, m3, m4 ,m5; private static Identity id1; @Autowired private ForumManager forumManager; @Before public void setUp() throws Exception { super.setUp(); id1 = JunitTestHelper.createAndPersistIdentityAsUser("rest-zero"); forum = forumManager.addAForum(); m1 = forumManager.createMessage(forum, id1, false); m1.setTitle("Thread-1"); m1.setBody("Body of Thread-1"); forumManager.addTopMessage(m1); m2 = forumManager.createMessage(forum, id1, false); m2.setTitle("Thread-2"); m2.setBody("Body of Thread-2"); forumManager.addTopMessage(m2); DBFactory.getInstance().intermediateCommit(); m3 = forumManager.createMessage(forum, id1, false); m3.setTitle("Message-1.1"); m3.setBody("Body of Message-1.1"); forumManager.replyToMessage(m3, m1); m4 = forumManager.createMessage(forum, id1, false); m4.setTitle("Message-1.1.1"); m4.setBody("Body of Message-1.1.1"); forumManager.replyToMessage(m4, m3); m5 = forumManager.createMessage(forum, id1, false); m5.setTitle("Message-1.2"); m5.setBody("Body of Message-1.2"); forumManager.replyToMessage(m5, m1); DBFactory.getInstance().intermediateCommit(); } @Test public void testGetThreads() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("threads").build(); HttpGet method = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); InputStream body = response.getEntity().getContent(); List<MessageVO> threads = parseMessageArray(body); assertNotNull(threads); assertFalse(threads.isEmpty()); conn.shutdown(); } @Test public void testGetThreadsWithPaging() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("threads") .queryParam("start", "0").queryParam("limit", "2").build(); HttpGet method = conn.createGet(uri, MediaType.APPLICATION_JSON + ";pagingspec=1.0", true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVOes threads = conn.parse(response, MessageVOes.class); assertNotNull(threads); assertNotNull(threads.getMessages()); assertTrue(threads.getTotalCount() >= 2); conn.shutdown(); } @Test public void testGetThread() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).build(); HttpGet method = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); InputStream body = response.getEntity().getContent(); List<MessageVO> threads = parseMessageArray(body); assertNotNull(threads); assertFalse(threads.isEmpty()); conn.shutdown(); } @Test public void testGetThreadWithPaging() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("start", "0").queryParam("limit", "2").build(); HttpGet method = conn.createGet(uri, MediaType.APPLICATION_JSON + ";pagingspec=1.0", true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVOes threads = conn.parse(response, MessageVOes.class); assertNotNull(threads); assertNotNull(threads.getMessages()); assertTrue(threads.getTotalCount() >= 2); conn.shutdown(); } @Test public void testNewThread() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("threads").queryParam("authorKey", id1.getKey()) .queryParam("title", "New thread") .queryParam("body", "A very interesting thread").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO thread = conn.parse(response, MessageVO.class); assertNotNull(thread); assertNotNull(thread.getKey()); assertEquals(thread.getForumKey(), forum.getKey()); assertEquals(thread.getAuthorKey(), id1.getKey()); //really saved? boolean saved = false; ForumManager fm = ForumManager.getInstance(); List<Message> allMessages = fm.getMessagesByForum(forum); for(Message message:allMessages) { if(message.getKey().equals(thread.getKey())) { saved = true; } } assertTrue(saved); conn.shutdown(); } @Test public void testNewMessage() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("authorKey", id1.getKey()) .queryParam("title", "New message") .queryParam("body", "A very interesting response in Thread-1").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); assertNotNull(message.getKey()); assertEquals(message.getForumKey(), forum.getKey()); assertEquals(message.getAuthorKey(), id1.getKey()); assertEquals(message.getParentKey(), m1.getKey()); //really saved? boolean saved = false; ForumManager fm = ForumManager.getInstance(); List<Message> allMessages = fm.getMessagesByForum(forum); for(Message msg:allMessages) { if(msg.getKey().equals(message.getKey())) { saved = true; } } assertTrue(saved); conn.shutdown(); } @Test public void testGetAttachment() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); //set a attachment VFSContainer container = forumManager.getMessageContainer(m1.getForum().getKey(), m1.getKey()); InputStream portraitIn = CoursesElementsTest.class.getResourceAsStream("portrait.jpg"); assertNotNull(portraitIn); VFSLeaf attachment = container.createChildLeaf(UUID.randomUUID().toString().replace("-", "") + ".jpg"); FileUtils.bcopy(portraitIn, attachment.getOutputStream(false), ""); assertTrue(conn.login("administrator", "openolat")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); HttpGet method = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); List<FileVO> files = parseFileArray(response.getEntity().getContent()); assertNotNull(files); FileVO attachmentVO = null; for(FileVO file:files) { if(attachment.getName().equals(file.getTitle())) { attachmentVO = file; } } assertNotNull(attachmentVO); URI downloadURI = new URI(attachmentVO.getHref()); HttpGet download = conn.createGet(downloadURI, MediaType.APPLICATION_JSON, true); HttpResponse downloadResponse = conn.execute(download); assertEquals(200, downloadResponse.getStatusLine().getStatusCode()); //String contentType = downloadResponse.getEntity().getContentType().getValue(); //doesn't work with grizzly assertEquals("image/jpeg", contentType); conn.shutdown(); } @Test public void testUploadAttachment() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login(id1.getName(), "A6B7C8")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("authorKey", id1.getKey()) .queryParam("title", "New message with attachment ") .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); //attachment URL portraitUrl = CoursesElementsTest.class.getResource("portrait.jpg"); assertNotNull(portraitUrl); File portrait = new File(portraitUrl.toURI()); //upload portrait URI attachUri = getForumUriBuilder().path("posts").path(message.getKey().toString()).path("attachments").build(); HttpPost attachMethod = conn.createPost(attachUri, MediaType.APPLICATION_JSON); conn.addMultipart(attachMethod, "portrait.jpg", portrait); HttpResponse attachResponse = conn.execute(attachMethod); assertEquals(200, attachResponse.getStatusLine().getStatusCode()); //check if the file exists ForumManager fm = ForumManager.getInstance(); VFSContainer container = fm.getMessageContainer(message.getForumKey(), message.getKey()); VFSItem uploadedFile = container.resolve("portrait.jpg"); assertNotNull(uploadedFile); assertTrue(uploadedFile instanceof VFSLeaf); //check if the image is still an image VFSLeaf uploadedImage = (VFSLeaf)uploadedFile; InputStream uploadedStream = uploadedImage.getInputStream(); BufferedImage image = ImageIO.read(uploadedStream); FileUtils.closeSafely(uploadedStream); assertNotNull(image); conn.shutdown(); } @Test public void testUpload64Attachment() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login(id1.getName(), "A6B7C8")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("authorKey", id1.getKey()) .queryParam("title", "New message with attachment ") .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); //attachment InputStream portraitStream = CoursesElementsTest.class.getResourceAsStream("portrait.jpg"); assertNotNull(portraitStream); //upload portrait URI attachUri = getForumUriBuilder().path("posts").path(message.getKey().toString()).path("attachments").build(); byte[] portraitBytes = IOUtils.toByteArray(portraitStream); byte[] portrait64 = Base64.encodeBase64(portraitBytes, true); HttpPost attachMethod = conn.createPost(attachUri, MediaType.APPLICATION_JSON); attachMethod.addHeader("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); conn.addEntity(attachMethod, new BasicNameValuePair("file", new String(portrait64)), new BasicNameValuePair("filename", "portrait64.jpg")); HttpResponse attachCode = conn.execute(attachMethod); assertEquals(200, attachCode.getStatusLine().getStatusCode()); //check if the file exists ForumManager fm = ForumManager.getInstance(); VFSContainer container = fm.getMessageContainer(message.getForumKey(), message.getKey()); VFSItem uploadedFile = container.resolve("portrait64.jpg"); assertNotNull(uploadedFile); assertTrue(uploadedFile instanceof VFSLeaf); //check if the image is still an image VFSLeaf uploadedImage = (VFSLeaf)uploadedFile; InputStream uploadedStream = uploadedImage.getInputStream(); BufferedImage image = ImageIO.read(uploadedStream); FileUtils.closeSafely(uploadedStream); assertNotNull(image); conn.shutdown(); } @Test public void testReplyWithTwoAttachments() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login(id1.getName(), "A6B7C8")); ReplyVO vo = new ReplyVO(); vo.setTitle("Reply with attachment"); vo.setBody("Reply with attachment body"); File64VO[] files = new File64VO[2]; //upload portrait InputStream portraitStream = CoursesElementsTest.class.getResourceAsStream("portrait.jpg"); assertNotNull(portraitStream); byte[] portraitBytes = IOUtils.toByteArray(portraitStream); byte[] portrait64 = Base64.encodeBase64(portraitBytes, true); files[0] = new File64VO("portrait64.jpg", new String(portrait64)); //upload single page InputStream indexStream = ForumTest.class.getResourceAsStream("singlepage.html"); assertNotNull(indexStream); byte[] indexBytes = IOUtils.toByteArray(indexStream); byte[] index64 = Base64.encodeBase64(indexBytes, true); files[1] = new File64VO("singlepage64.html", new String(index64)); vo.setAttachments(files); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); conn.addJsonEntity(method, vo); method.addHeader("Accept-Language", "en"); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); assertNotNull(message.getAttachments()); assertEquals(2, message.getAttachments().length); for(FileVO attachment:message.getAttachments()) { String title = attachment.getTitle(); assertNotNull(title); String href = attachment.getHref(); URI attachmentUri = new URI(href); HttpGet getAttachment = conn.createGet(attachmentUri, "*/*", true); HttpResponse attachmentCode = conn.execute(getAttachment); assertEquals(200, attachmentCode.getStatusLine().getStatusCode()); EntityUtils.consume(attachmentCode.getEntity()); } //check if the file exists ForumManager fm = ForumManager.getInstance(); VFSContainer container = fm.getMessageContainer(message.getForumKey(), message.getKey()); VFSItem uploadedFile = container.resolve("portrait64.jpg"); assertNotNull(uploadedFile); assertTrue(uploadedFile instanceof VFSLeaf); //check if the image is still an image VFSLeaf uploadedImage = (VFSLeaf)uploadedFile; InputStream uploadedStream = uploadedImage.getInputStream(); BufferedImage image = ImageIO.read(uploadedStream); FileUtils.closeSafely(uploadedStream); assertNotNull(image); //check if the single page exists VFSItem uploadedPage = container.resolve("singlepage64.html"); assertNotNull(uploadedPage); assertTrue(uploadedPage instanceof VFSLeaf); conn.shutdown(); } @Test public void testUploadAttachmentWithFile64VO() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login(id1.getName(), "A6B7C8")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("authorKey", id1.getKey()) .queryParam("title", "New message with attachment ") .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); //attachment InputStream portraitStream = CoursesElementsTest.class.getResourceAsStream("portrait.jpg"); assertNotNull(portraitStream); //upload portrait URI attachUri = getForumUriBuilder().path("posts").path(message.getKey().toString()).path("attachments").build(); HttpPut attachMethod = conn.createPut(attachUri, MediaType.APPLICATION_JSON, true); attachMethod.addHeader("Content-Type", MediaType.APPLICATION_JSON); byte[] portraitBytes = IOUtils.toByteArray(portraitStream); byte[] portrait64 = Base64.encodeBase64(portraitBytes, true); File64VO fileVo = new File64VO(); fileVo.setFile(new String(portrait64)); fileVo.setFilename("portrait64vo.jpg"); conn.addJsonEntity(attachMethod, fileVo); HttpResponse attachCode = conn.execute(attachMethod); assertEquals(200, attachCode.getStatusLine().getStatusCode()); //check if the file exists ForumManager fm = ForumManager.getInstance(); VFSContainer container = fm.getMessageContainer(message.getForumKey(), message.getKey()); VFSItem uploadedFile = container.resolve("portrait64vo.jpg"); assertNotNull(uploadedFile); assertTrue(uploadedFile instanceof VFSLeaf); //check if the image is still an image VFSLeaf uploadedImage = (VFSLeaf)uploadedFile; InputStream uploadedStream = uploadedImage.getInputStream(); BufferedImage image = ImageIO.read(uploadedStream); FileUtils.closeSafely(uploadedStream); assertNotNull(image); conn.shutdown(); } @Test public void testUploadAttachmentAndRename() throws IOException, URISyntaxException { RestConnection conn = new RestConnection(); assertTrue(conn.login(id1.getName(), "A6B7C8")); URI uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()) .queryParam("authorKey", id1.getKey()) .queryParam("title", "New message with attachment ") .queryParam("body", "A very interesting response in Thread-1 with an attachment").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); MessageVO message = conn.parse(response, MessageVO.class); assertNotNull(message); //attachment URL portraitUrl = CoursesElementsTest.class.getResource("portrait.jpg"); assertNotNull(portraitUrl); File portrait = new File(portraitUrl.toURI()); //upload portrait URI attachUri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); HttpPost attachMethod = conn.createPost(attachUri, MediaType.APPLICATION_JSON); conn.addMultipart(attachMethod, "portrait.jpg", portrait); HttpResponse attachCode = conn.execute(attachMethod); assertEquals(200, attachCode.getStatusLine().getStatusCode()); EntityUtils.consume(attachCode.getEntity()); //upload portrait a second time URI attach2Uri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); HttpPost attach2Method = conn.createPost(attach2Uri, MediaType.APPLICATION_JSON); conn.addMultipart(attach2Method, "portrait.jpg", portrait); HttpResponse attach2Code = conn.execute(attach2Method); assertEquals(200, attach2Code.getStatusLine().getStatusCode()); EntityUtils.consume(attach2Code.getEntity()); // load the attachments URI loadUri = getForumUriBuilder().path("posts").path(m1.getKey().toString()).path("attachments").build(); HttpGet loadMethod = conn.createGet(loadUri, MediaType.APPLICATION_JSON, true); HttpResponse loadResponse = conn.execute(loadMethod); assertEquals(200, loadResponse.getStatusLine().getStatusCode()); InputStream loadBody = loadResponse.getEntity().getContent(); List<FileVO> files = parseFileArray(loadBody); assertNotNull(files); assertEquals(2, files.size()); conn.shutdown(); } private UriBuilder getForumUriBuilder() { return UriBuilder.fromUri(getContextURI()).path("repo").path("forums").path(forum.getKey().toString()); } protected List<MessageVO> parseMessageArray(InputStream body) { try { ObjectMapper mapper = new ObjectMapper(jsonFactory); return mapper.readValue(body, new TypeReference<List<MessageVO>>(){/* */}); } catch (Exception e) { e.printStackTrace(); return null; } } }