mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 21:59:54 +01:00
briar-headless: POST text as JSON in body instead of form parameter
This commit is contained in:
@@ -101,7 +101,13 @@ Attention: There can messages of other `type`s where the message `body` is `null
|
|||||||
|
|
||||||
`POST /messages/{contactId}`
|
`POST /messages/{contactId}`
|
||||||
|
|
||||||
The text of the message should be included in the form parameter `text`.
|
The text of the message should be posted as JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"text": "Hello World!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Listing blog posts
|
### Listing blog posts
|
||||||
|
|
||||||
@@ -132,7 +138,13 @@ Returns a JSON array of blog posts:
|
|||||||
|
|
||||||
`POST /v1/blogs/posts`
|
`POST /v1/blogs/posts`
|
||||||
|
|
||||||
The text of the blog post should be included in the form parameter `text`.
|
The text of the blog post should be posted as JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"text": "Hello Blog World!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Websocket API
|
## Websocket API
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package org.briarproject.briar.headless
|
package org.briarproject.briar.headless
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import io.javalin.BadRequestResponse
|
||||||
|
import io.javalin.Context
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.JavalinEvent.SERVER_START_FAILED
|
import io.javalin.JavalinEvent.SERVER_START_FAILED
|
||||||
import io.javalin.JavalinEvent.SERVER_STOPPED
|
import io.javalin.JavalinEvent.SERVER_STOPPED
|
||||||
@@ -103,3 +107,19 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a String from the JSON field or throws [BadRequestResponse] if null or empty.
|
||||||
|
*/
|
||||||
|
fun Context.getFromJson(field: String) : String {
|
||||||
|
try {
|
||||||
|
// TODO use a static object mapper to avoid re-initializations
|
||||||
|
val jsonNode = ObjectMapper().readTree(body())
|
||||||
|
if (!jsonNode.hasNonNull(field)) throw BadRequestResponse("'$field' missing in JSON")
|
||||||
|
val result = jsonNode.get(field).asText()
|
||||||
|
if (result == null || result.isEmpty()) throw BadRequestResponse("'$field' empty in JSON")
|
||||||
|
return result
|
||||||
|
} catch (e: JsonParseException) {
|
||||||
|
throw BadRequestResponse("Invalid JSON")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import io.javalin.Context
|
|||||||
import org.briarproject.bramble.api.identity.IdentityManager
|
import org.briarproject.bramble.api.identity.IdentityManager
|
||||||
import org.briarproject.bramble.api.system.Clock
|
import org.briarproject.bramble.api.system.Clock
|
||||||
import org.briarproject.bramble.util.StringUtils
|
import org.briarproject.bramble.util.StringUtils
|
||||||
import org.briarproject.briar.api.blog.BlogConstants
|
import org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH
|
||||||
import org.briarproject.briar.api.blog.BlogManager
|
import org.briarproject.briar.api.blog.BlogManager
|
||||||
import org.briarproject.briar.api.blog.BlogPostFactory
|
import org.briarproject.briar.api.blog.BlogPostFactory
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import javax.annotation.concurrent.Immutable
|
import javax.annotation.concurrent.Immutable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -26,16 +27,16 @@ constructor(
|
|||||||
override fun listPosts(ctx: Context): Context {
|
override fun listPosts(ctx: Context): Context {
|
||||||
val posts = blogManager.blogs
|
val posts = blogManager.blogs
|
||||||
.flatMap { blog -> blogManager.getPostHeaders(blog.id) }
|
.flatMap { blog -> blogManager.getPostHeaders(blog.id) }
|
||||||
|
.asSequence()
|
||||||
.sortedBy { it.timeReceived }
|
.sortedBy { it.timeReceived }
|
||||||
.map { header -> header.output(blogManager.getPostBody(header.id)) }
|
.map { header -> header.output(blogManager.getPostBody(header.id)) }
|
||||||
|
.toList()
|
||||||
return ctx.json(posts)
|
return ctx.json(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createPost(ctx: Context): Context {
|
override fun createPost(ctx: Context): Context {
|
||||||
val text = ctx.formParam("text")
|
val text = ctx.getFromJson("text")
|
||||||
if (text == null || text.isEmpty())
|
if (StringUtils.utf8IsTooLong(text, MAX_BLOG_POST_BODY_LENGTH))
|
||||||
throw BadRequestResponse("Expecting blog post text")
|
|
||||||
if (StringUtils.utf8IsTooLong(text, BlogConstants.MAX_BLOG_POST_BODY_LENGTH))
|
|
||||||
throw BadRequestResponse("Too long blog post text")
|
throw BadRequestResponse("Too long blog post text")
|
||||||
|
|
||||||
val author = identityManager.localAuthor
|
val author = identityManager.localAuthor
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package org.briarproject.briar.headless.forums
|
|||||||
import io.javalin.BadRequestResponse
|
import io.javalin.BadRequestResponse
|
||||||
import io.javalin.Context
|
import io.javalin.Context
|
||||||
import org.briarproject.bramble.util.StringUtils
|
import org.briarproject.bramble.util.StringUtils
|
||||||
import org.briarproject.briar.api.forum.ForumConstants
|
import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH
|
||||||
import org.briarproject.briar.api.forum.ForumManager
|
import org.briarproject.briar.api.forum.ForumManager
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import javax.annotation.concurrent.Immutable
|
import javax.annotation.concurrent.Immutable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@@ -20,10 +21,8 @@ constructor(private val forumManager: ForumManager) : ForumController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun create(ctx: Context): Context {
|
override fun create(ctx: Context): Context {
|
||||||
val name = ctx.formParam("text")
|
val name = ctx.getFromJson("text")
|
||||||
if (name == null || name.isNullOrEmpty())
|
if (StringUtils.utf8IsTooLong(name, MAX_FORUM_NAME_LENGTH))
|
||||||
throw BadRequestResponse("Expecting Forum Name")
|
|
||||||
if (StringUtils.utf8IsTooLong(name, ForumConstants.MAX_FORUM_NAME_LENGTH))
|
|
||||||
throw BadRequestResponse("Forum name is too long")
|
throw BadRequestResponse("Forum name is too long")
|
||||||
return ctx.json(forumManager.addForum(name).output())
|
return ctx.json(forumManager.addForum(name).output())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest
|
|||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse
|
||||||
import org.briarproject.briar.headless.event.WebSocketController
|
import org.briarproject.briar.headless.event.WebSocketController
|
||||||
import org.briarproject.briar.headless.event.output
|
import org.briarproject.briar.headless.event.output
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import javax.annotation.concurrent.Immutable
|
import javax.annotation.concurrent.Immutable
|
||||||
@@ -59,9 +60,7 @@ constructor(
|
|||||||
override fun write(ctx: Context): Context {
|
override fun write(ctx: Context): Context {
|
||||||
val contact = getContact(ctx)
|
val contact = getContact(ctx)
|
||||||
|
|
||||||
val message = ctx.formParam("text")
|
val message = ctx.getFromJson("text")
|
||||||
if (message == null || message.isEmpty())
|
|
||||||
throw BadRequestResponse("Expecting Message text")
|
|
||||||
if (utf8IsTooLong(message, MAX_PRIVATE_MESSAGE_BODY_LENGTH))
|
if (utf8IsTooLong(message, MAX_PRIVATE_MESSAGE_BODY_LENGTH))
|
||||||
throw BadRequestResponse("Message text too large")
|
throw BadRequestResponse("Message text too large")
|
||||||
|
|
||||||
|
|||||||
@@ -30,23 +30,15 @@ internal class BlogControllerTest : ControllerTest() {
|
|||||||
private val rssFeed = false
|
private val rssFeed = false
|
||||||
private val read = true
|
private val read = true
|
||||||
private val header = BlogPostHeader(
|
private val header = BlogPostHeader(
|
||||||
POST,
|
POST, group.id, message.id, parentId, message.timestamp, timestamp, author, OURSELVES,
|
||||||
group.id,
|
rssFeed, read
|
||||||
message.id,
|
|
||||||
parentId,
|
|
||||||
message.timestamp,
|
|
||||||
timestamp,
|
|
||||||
author,
|
|
||||||
OURSELVES,
|
|
||||||
rssFeed,
|
|
||||||
read
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreate() {
|
fun testCreate() {
|
||||||
val post = BlogPost(message, null, localAuthor)
|
val post = BlogPost(message, null, localAuthor)
|
||||||
|
|
||||||
every { ctx.formParam("text") } returns body
|
every { ctx.body() } returns """{"text": "$body"}"""
|
||||||
every { identityManager.localAuthor } returns localAuthor
|
every { identityManager.localAuthor } returns localAuthor
|
||||||
every { blogManager.getPersonalBlog(localAuthor) } returns blog
|
every { blogManager.getPersonalBlog(localAuthor) } returns blog
|
||||||
every { clock.currentTimeMillis() } returns message.timestamp
|
every { clock.currentTimeMillis() } returns message.timestamp
|
||||||
@@ -68,21 +60,21 @@ internal class BlogControllerTest : ControllerTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateNoText() {
|
fun testCreateNoText() {
|
||||||
every { ctx.formParam("text") } returns null
|
every { ctx.body() } returns """{"foo": "bar"}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateEmptyText() {
|
fun testCreateEmptyText() {
|
||||||
every { ctx.formParam("text") } returns ""
|
every { ctx.body() } returns """{"text": ""}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCreateTooLongText() {
|
fun testCreateTooLongText() {
|
||||||
every { ctx.formParam("text") } returns getRandomString(MAX_BLOG_POST_BODY_LENGTH + 1)
|
every { ctx.body() } returns """{"text": "${getRandomString(MAX_BLOG_POST_BODY_LENGTH + 1)}"}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.createPost(ctx) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ internal class ForumControllerTest : ControllerTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun create() {
|
fun create() {
|
||||||
every { ctx.formParam("text") } returns forum.name
|
every { ctx.body() } returns """{"text": "${forum.name}"}"""
|
||||||
every { forumManager.addForum(forum.name) } returns forum
|
every { forumManager.addForum(forum.name) } returns forum
|
||||||
every { ctx.json(forum.output()) } returns ctx
|
every { ctx.json(forum.output()) } returns ctx
|
||||||
|
|
||||||
@@ -39,21 +39,35 @@ internal class ForumControllerTest : ControllerTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createNoName() {
|
fun createNoName() {
|
||||||
every { ctx.formParam("text") } returns null
|
every { ctx.body() } returns "{}"
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createEmptyName() {
|
fun createEmptyName() {
|
||||||
every { ctx.formParam("text") } returns ""
|
every { ctx.body() } returns """{"text": ""}"""
|
||||||
|
|
||||||
|
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createNullName() {
|
||||||
|
every { ctx.body() } returns """{"text": null}"""
|
||||||
|
|
||||||
|
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createNoJsonName() {
|
||||||
|
every { ctx.body() } returns "foo"
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createTooLongName() {
|
fun createTooLongName() {
|
||||||
every { ctx.formParam("text") } returns getRandomString(MAX_FORUM_NAME_LENGTH + 1)
|
every { ctx.body() } returns """{"text": "${getRandomString(MAX_FORUM_NAME_LENGTH + 1)}"}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.create(ctx) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
val slot = CapturingSlot<JsonDict>()
|
val slot = CapturingSlot<JsonDict>()
|
||||||
|
|
||||||
expectGetContact()
|
expectGetContact()
|
||||||
every { ctx.formParam("text") } returns body
|
every { ctx.body() } returns """{"text": "$body"}"""
|
||||||
every { messagingManager.getContactGroup(contact) } returns group
|
every { messagingManager.getContactGroup(contact) } returns group
|
||||||
every { clock.currentTimeMillis() } returns timestamp
|
every { clock.currentTimeMillis() } returns timestamp
|
||||||
every {
|
every {
|
||||||
@@ -126,7 +126,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun writeNonexistentBody() {
|
fun writeNonexistentBody() {
|
||||||
expectGetContact()
|
expectGetContact()
|
||||||
every { ctx.formParam("text") } returns null
|
every { ctx.body() } returns """{"foo": "bar"}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun writeEmptyBody() {
|
fun writeEmptyBody() {
|
||||||
expectGetContact()
|
expectGetContact()
|
||||||
every { ctx.formParam("text") } returns ""
|
every { ctx.body() } returns """{"text": ""}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun writeTooLongBody() {
|
fun writeTooLongBody() {
|
||||||
expectGetContact()
|
expectGetContact()
|
||||||
every { ctx.formParam("text") } returns getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1)
|
every { ctx.body() } returns """{"text": "${getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1)}"}"""
|
||||||
|
|
||||||
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user