Migrate REST classes to Kotlin and upgrade Javalin

parent 33c509cd
......@@ -36,6 +36,9 @@
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
......@@ -258,14 +261,10 @@
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="1" />
</codeStyleSettings>
</code_scheme>
</component>
</component>
\ No newline at end of file
......@@ -14,11 +14,12 @@ dependencies {
implementation project(path: ':briar-core', configuration: 'default')
implementation project(path: ':bramble-java', configuration: 'default')
implementation 'io.javalin:javalin:1.7.0'
implementation 'io.javalin:javalin:2.1.0'
implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
apt 'com.google.dagger:dagger-compiler:2.0.2'
kapt 'com.google.dagger:dagger-compiler:2.0.2'
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
......
package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
@SuppressWarnings("WeakerAccess")
public class OutputAuthor {
public final byte[] id;
public final String name;
public final byte[] publicKey;
public OutputAuthor(Author author) {
this.id = author.getId().getBytes();
this.name = author.getName();
this.publicKey = author.getPublicKey();
}
}
package org.briarproject.bramble.identity
import org.briarproject.bramble.api.identity.Author
import javax.annotation.concurrent.Immutable
@Immutable
@Suppress("unused")
class OutputAuthor(author: Author) {
val id: ByteArray = author.id.bytes
val name: String = author.name
val publicKey: ByteArray = author.publicKey
}
package org.briarproject.briar.headless
import org.briarproject.bramble.api.identity.Author
import org.briarproject.bramble.identity.OutputAuthor
import org.briarproject.briar.api.blog.MessageType
fun Author.output() = OutputAuthor(this)
fun Author.Status.output() = name.toLowerCase()
fun MessageType.output() = name.toLowerCase()
package org.briarproject.briar.headless;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.briar.headless.blogs.BlogController;
import org.briarproject.briar.headless.contact.ContactController;
import org.briarproject.briar.headless.forums.ForumController;
import org.briarproject.briar.headless.messaging.MessagingController;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.javalin.Javalin;
import static io.javalin.ApiBuilder.get;
import static io.javalin.ApiBuilder.path;
import static io.javalin.ApiBuilder.post;
import static io.javalin.event.EventType.SERVER_START_FAILED;
import static io.javalin.event.EventType.SERVER_STOPPED;
import static java.lang.Runtime.getRuntime;
@Immutable
@Singleton
@MethodsNotNullByDefault
@ParametersAreNonnullByDefault
public class Router {
private final BriarService briarService;
private final ContactController contactController;
private final MessagingController messagingController;
private final ForumController forumController;
private final BlogController blogController;
@Inject
public Router(BriarService briarService,
ContactController contactController,
MessagingController messagingController,
ForumController forumController,
BlogController blogController) {
this.briarService = briarService;
this.contactController = contactController;
this.messagingController = messagingController;
this.forumController = forumController;
this.blogController = blogController;
}
public void start() {
briarService.start();
getRuntime().addShutdownHook(new Thread(briarService::stop));
Javalin app = Javalin.create()
.port(7000)
.disableStartupBanner()
.enableStandardRequestLogging()
.enableRouteOverview("/")
.enableDynamicGzip()
.event(SERVER_START_FAILED, event -> briarService.stop())
.event(SERVER_STOPPED, event -> briarService.stop())
.start();
app.routes(() -> {
path("/contacts", () -> get(contactController::list));
path("/messages/:contactId", () -> {
get(messagingController::list);
post(messagingController::write);
});
path("/forums", () -> {
get(forumController::list);
post(forumController::create);
});
path("/blogs", () -> path("/posts", () -> {
get(blogController::listPosts);
post(blogController::createPost);
}));
});
}
}
package org.briarproject.briar.headless
import io.javalin.Javalin
import io.javalin.JavalinEvent.SERVER_START_FAILED
import io.javalin.JavalinEvent.SERVER_STOPPED
import io.javalin.apibuilder.ApiBuilder.*
import org.briarproject.briar.headless.blogs.BlogController
import org.briarproject.briar.headless.contact.ContactController
import org.briarproject.briar.headless.forums.ForumController
import org.briarproject.briar.headless.messaging.MessagingController
import java.lang.Runtime.getRuntime
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.system.exitProcess
@Immutable
@Singleton
class Router @Inject
constructor(
private val briarService: BriarService,
private val contactController: ContactController,
private val messagingController: MessagingController,
private val forumController: ForumController,
private val blogController: BlogController
) {
fun start() {
briarService.start()
getRuntime().addShutdownHook(Thread(Runnable { briarService.stop() }))
val app = Javalin.create()
.port(7000)
.disableStartupBanner()
.enableDebugLogging()
.enableCaseSensitiveUrls()
.enableRouteOverview("/")
.event(SERVER_START_FAILED) { stop() }
.event(SERVER_STOPPED) { stop() }
.start()
app.routes {
path("/contacts") {
get { ctx -> contactController.list(ctx) }
}
path("/messages/:contactId") {
get { ctx -> messagingController.list(ctx) }
post { ctx -> messagingController.write(ctx) }
}
path("/forums") {
get { ctx -> forumController.list(ctx) }
post { ctx -> forumController.create(ctx) }
}
path("/blogs") {
path("/posts") {
get { ctx -> blogController.listPosts(ctx) }
post { ctx -> blogController.createPost(ctx) }
}
}
}
}
private fun stop() {
briarService.stop()
exitProcess(1)
}
}
package org.briarproject.briar.headless.blogs;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPost;
import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogPostHeader;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.javalin.Context;
@Immutable
@Singleton
@MethodsNotNullByDefault
@ParametersAreNonnullByDefault
public class BlogController {
private final BlogManager blogManager;
private final BlogPostFactory blogPostFactory;
private final IdentityManager identityManager;
private final Clock clock;
@Inject
public BlogController(BlogManager blogManager,
BlogPostFactory blogPostFactory, IdentityManager identityManager,
Clock clock) {
this.blogManager = blogManager;
this.blogPostFactory = blogPostFactory;
this.identityManager = identityManager;
this.clock = clock;
}
public Context listPosts(Context ctx) throws DbException {
List<OutputBlogPost> posts = new ArrayList<>();
for (Blog b : blogManager.getBlogs()) {
Collection<BlogPostHeader> headers =
blogManager.getPostHeaders(b.getId());
for (BlogPostHeader header : headers) {
String body = blogManager.getPostBody(header.getId());
OutputBlogPost post = new OutputBlogPost(header, body);
posts.add(post);
}
}
return ctx.json(posts);
}
public Context createPost(Context ctx)
throws DbException, GeneralSecurityException, FormatException {
String text = ctx.formParam("text");
if (text == null || text.length() < 1) {
return ctx.status(500).result("Expecting Blog text");
} else {
LocalAuthor author = identityManager.getLocalAuthor();
Blog blog = blogManager.getPersonalBlog(author);
long now = clock.currentTimeMillis();
BlogPost post = blogPostFactory
.createBlogPost(blog.getId(), now, null, author, text);
blogManager.addLocalPost(post);
BlogPostHeader header = blogManager
.getPostHeader(blog.getId(), post.getMessage().getId());
OutputBlogPost outputPost = new OutputBlogPost(header, text);
return ctx.json(outputPost);
}
}
}
package org.briarproject.briar.headless.blogs
import io.javalin.BadRequestResponse
import io.javalin.Context
import org.briarproject.bramble.api.identity.IdentityManager
import org.briarproject.bramble.api.system.Clock
import org.briarproject.briar.api.blog.BlogManager
import org.briarproject.briar.api.blog.BlogPostFactory
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
@Immutable
@Singleton
class BlogController @Inject
constructor(
private val blogManager: BlogManager,
private val blogPostFactory: BlogPostFactory,
private val identityManager: IdentityManager,
private val clock: Clock
) {
fun listPosts(ctx: Context): Context {
val posts = blogManager.blogs.flatMap { blog ->
blogManager.getPostHeaders(blog.id).map { header ->
val body = blogManager.getPostBody(header.id)
header.output(body)
}
}
return ctx.json(posts)
}
fun createPost(ctx: Context): Context {
val text = ctx.formParam("text")
if (text == null || text.isEmpty())
throw BadRequestResponse("Expecting Blog text")
val author = identityManager.localAuthor
val blog = blogManager.getPersonalBlog(author)
val now = clock.currentTimeMillis()
val post = blogPostFactory.createBlogPost(blog.id, now, null, author, text)
blogManager.addLocalPost(post)
val header = blogManager.getPostHeader(blog.id, post.message.id)
return ctx.json(header.output(text))
}
}
package org.briarproject.briar.headless.blogs
import org.briarproject.briar.api.blog.BlogPostHeader
internal fun BlogPostHeader.output(body: String) = OutputBlogPost(this, body)
package org.briarproject.briar.headless.blogs;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.identity.OutputAuthor;
import org.briarproject.briar.api.blog.BlogPostHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
@SuppressWarnings("WeakerAccess")
class OutputBlogPost {
public final String body;
public final OutputAuthor author;
public final String authorStatus, type;
public final byte[] id;
@Nullable
public final byte[] parentId;
public final boolean read, rssFeed;
public final long timestamp, timestampReceived;
OutputBlogPost(BlogPostHeader header, String body) {
this.body = body;
this.author = new OutputAuthor(header.getAuthor());
this.authorStatus = header.getAuthorStatus().name().toLowerCase();
this.type = header.getType().name().toLowerCase();
this.id = header.getId().getBytes();
this.parentId = header.getParentId() == null ? null :
header.getParentId().getBytes();
this.read = header.isRead();
this.rssFeed = header.isRssFeed();
this.timestamp = header.getTimestamp();
this.timestampReceived = header.getTimeReceived();
}
}
package org.briarproject.briar.headless.blogs
import org.briarproject.bramble.identity.OutputAuthor
import org.briarproject.briar.api.blog.BlogPostHeader
import org.briarproject.briar.headless.output
import javax.annotation.concurrent.Immutable
@Immutable
@Suppress("unused")
internal class OutputBlogPost(header: BlogPostHeader, val body: String) {
val author: OutputAuthor = OutputAuthor(header.author)
val authorStatus: String = header.authorStatus.output()
val type = header.type.output()
val id: ByteArray = header.id.bytes
val parentId = header.parentId?.bytes
val read = header.isRead
val rssFeed = header.isRssFeed
val timestamp = header.timestamp
val timestampReceived = header.timeReceived
}
package org.briarproject.briar.headless.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.javalin.Context;
@Immutable
@Singleton
@NotNullByDefault
public class ContactController {
private final ContactManager contactManager;
@Inject
public ContactController(ContactManager contactManager) {
this.contactManager = contactManager;
}
public Context list(Context ctx) throws DbException {
Collection<Contact> contacts = contactManager.getActiveContacts();
List<OutputContact> outputContacts = new ArrayList<>(contacts.size());
for (Contact c : contacts) {
outputContacts.add(new OutputContact(c));
}
return ctx.json(outputContacts);
}
}
package org.briarproject.briar.headless.contact
import io.javalin.Context
import org.briarproject.bramble.api.contact.ContactManager
import javax.annotation.concurrent.Immutable
import javax.inject.Inject
import javax.inject.Singleton
@Immutable
@Singleton
class ContactController @Inject
constructor(private val contactManager: ContactManager) {
fun list(ctx: Context): Context {
val contacts = contactManager.activeContacts.map { contact ->
contact.output()
}
return ctx.json(contacts)
}
}
package org.briarproject.briar.headless.contact
import org.briarproject.bramble.api.contact.Contact
internal fun Contact.output() = OutputContact(this)
package org.briarproject.briar.headless.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.identity.OutputAuthor;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
@SuppressWarnings("WeakerAccess")
public class OutputContact {
public final int id;
public final OutputAuthor author;
public final boolean verified;
public OutputContact(Contact c) {
this.id = c.getId().getInt();
this.author = new OutputAuthor(c.getAuthor());
this.verified = c.isVerified();
}
}
package org.briarproject.briar.headless.contact
import org.briarproject.bramble.api.contact.Contact
import org.briarproject.briar.headless.output
import javax.annotation.concurrent.Immutable
@Immutable
@Suppress("unused")
internal class OutputContact(c: Contact) {
val id = c.id.int
val author = c.author.output()
val verified = c.isVerified
}
package org.briarproject.briar.headless.forums
import org.briarproject.briar.api.forum.Forum
internal fun Forum.output() = OutputForum(this)
internal fun Collection<Forum>.output() = map { it.output() }
package org.briarproject.briar.headless.forums;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumManager;
import java.util.Collection;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.javalin.Context;
import static io.javalin.translator.json.JavalinJsonPlugin.getObjectToJsonMapper;
@Immutable
@Singleton
@MethodsNotNullByDefault
@ParametersAreNonnullByDefault
public class ForumController {
private final ForumManager forumManager;
@Inject
public ForumController(ForumManager forumManager) {
this.forumManager = forumManager;
}
public