From 3ef366568695ff12fd945a506e53d6279b2337c5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Wed, 1 Jul 2020 21:22:03 +0200 Subject: [PATCH] =?utf8?q?=F0=9F=8E=A8=20Add=20container=20for=20new=20pos?= =?utf8?q?ts/replies?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/WebInterface.java | 61 +++----------- .../net/pterodactylus/sone/web/NewElements.kt | 52 ++++++++++++ .../kotlin/net/pterodactylus/sone/test/Guice.kt | 19 +++-- .../net/pterodactylus/sone/web/NewElementsTest.kt | 92 ++++++++++++++++++++++ 4 files changed, 170 insertions(+), 54 deletions(-) create mode 100644 src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 4650625..41ff052 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -18,15 +18,12 @@ package net.pterodactylus.sone.web; import static java.util.logging.Logger.getLogger; -import static java.util.stream.Collectors.toSet; import java.util.Collection; -import java.util.Set; import java.util.TimeZone; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.inject.Named; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.ElementLoader; @@ -41,10 +38,8 @@ import net.pterodactylus.sone.main.PluginHomepage; import net.pterodactylus.sone.main.PluginVersion; import net.pterodactylus.sone.main.PluginYear; import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.sone.notify.ListNotification; import net.pterodactylus.sone.notify.ListNotificationFilter; import net.pterodactylus.sone.notify.PostVisibilityFilter; -import net.pterodactylus.sone.notify.ReplyVisibilityFilter; import net.pterodactylus.sone.template.LinkedElementRenderFilter; import net.pterodactylus.sone.template.ParserFilter; import net.pterodactylus.sone.template.RenderFilter; @@ -89,7 +84,6 @@ import net.pterodactylus.util.web.TemplatePage; import com.codahale.metrics.*; import com.google.common.base.Optional; -import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import freenet.clients.http.ToadletContext; @@ -100,9 +94,6 @@ import freenet.clients.http.ToadletContext; */ public class WebInterface implements SessionProvider { - /** The logger. */ - private static final Logger logger = getLogger(WebInterface.class.getName()); - /** The loaders for templates, pages, and classpath providers. */ private final Loaders loaders; @@ -126,7 +117,6 @@ public class WebInterface implements SessionProvider { private final ListNotificationFilter listNotificationFilter; private final PostVisibilityFilter postVisibilityFilter; - private final ReplyVisibilityFilter replyVisibilityFilter; private final ElementLoader elementLoader; private final LinkedElementRenderFilter linkedElementRenderFilter; @@ -138,37 +128,23 @@ public class WebInterface implements SessionProvider { private final Translation translation; private final SessionProvider sessionProvider; - /** The “new post” notification. */ - private final ListNotification newPostNotification; - - /** The “new reply” notification. */ - private final ListNotification newReplyNotification; - - /** The invisible “local post” notification. */ - private final ListNotification localPostNotification; - - /** The invisible “local reply” notification. */ - private final ListNotification localReplyNotification; + private final NewElements newElements; @Inject public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter, - PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter, - ElementLoader elementLoader, TemplateContextFactory templateContextFactory, - TemplateRenderer templateRenderer, - ParserFilter parserFilter, ShortenFilter shortenFilter, - RenderFilter renderFilter, - LinkedElementRenderFilter linkedElementRenderFilter, - PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter, - NotificationManager notificationManager, SessionProvider sessionProvider, - @Named("newRemotePost") ListNotification newPostNotification, - @Named("newRemotePostReply") ListNotification newReplyNotification, - @Named("localPost") ListNotification localPostNotification, - @Named("localReply") ListNotification localReplyNotification) { + PostVisibilityFilter postVisibilityFilter, + ElementLoader elementLoader, TemplateContextFactory templateContextFactory, + TemplateRenderer templateRenderer, + ParserFilter parserFilter, ShortenFilter shortenFilter, + RenderFilter renderFilter, + LinkedElementRenderFilter linkedElementRenderFilter, + PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter, + NotificationManager notificationManager, SessionProvider sessionProvider, + NewElements newElements) { this.sonePlugin = sonePlugin; this.loaders = loaders; this.listNotificationFilter = listNotificationFilter; this.postVisibilityFilter = postVisibilityFilter; - this.replyVisibilityFilter = replyVisibilityFilter; this.elementLoader = elementLoader; this.templateRenderer = templateRenderer; this.parserFilter = parserFilter; @@ -181,10 +157,7 @@ public class WebInterface implements SessionProvider { this.translation = translation; this.notificationManager = notificationManager; this.sessionProvider = sessionProvider; - this.newPostNotification = newPostNotification; - this.newReplyNotification = newReplyNotification; - this.localPostNotification = localPostNotification; - this.localReplyNotification = localReplyNotification; + this.newElements = newElements; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); this.templateContextFactory = templateContextFactory; @@ -260,20 +233,12 @@ public class WebInterface implements SessionProvider { @Nonnull public Collection getNewPosts(@Nullable Sone currentSone) { - Set allNewPosts = ImmutableSet. builder() - .addAll(newPostNotification.getElements()) - .addAll(localPostNotification.getElements()) - .build(); - return allNewPosts.stream().filter(p -> postVisibilityFilter.isPostVisible(currentSone, p)).collect(toSet()); + return newElements.getNewPosts(); } @Nonnull public Collection getNewReplies(@Nullable Sone currentSone) { - Set allNewReplies = ImmutableSet.builder() - .addAll(newReplyNotification.getElements()) - .addAll(localReplyNotification.getElements()) - .build(); - return allNewReplies.stream().filter(r -> replyVisibilityFilter.isReplyVisible(currentSone, r)).collect(toSet()); + return newElements.getNewReplies(); } // diff --git a/src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt b/src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt new file mode 100644 index 0000000..1026f36 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/NewElements.kt @@ -0,0 +1,52 @@ +/** + * Sone - NewElements.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.pterodactylus.sone.web + +import net.pterodactylus.sone.data.Post +import net.pterodactylus.sone.data.PostReply +import net.pterodactylus.sone.notify.ListNotification +import net.pterodactylus.sone.notify.PostVisibilityFilter +import net.pterodactylus.sone.notify.ReplyVisibilityFilter +import javax.inject.Inject +import javax.inject.Named + +/** + * Container for new elements that should be shown in the web interface. + * + * This is just a wrapper around the notifications that store the new elements. + */ +class NewElements @Inject constructor( + @Named("newRemotePost") private val newPostNotification: ListNotification, + @Named("newRemotePostReply") private val newReplyNotification: ListNotification, + @Named("localPost") private val localPostNotification: ListNotification, + @Named("localReply") private val localReplyNotification: ListNotification, + private val postVisibilityFilter: PostVisibilityFilter, + private val replyVisibilityFilter: ReplyVisibilityFilter +) { + + val newPosts: Collection + get() = listOf(newPostNotification, localPostNotification) + .flatMap(ListNotification::elements) + .filter { postVisibilityFilter.isPostVisible(null, it) } + + val newReplies: Collection + get() = listOf(newReplyNotification, localReplyNotification) + .flatMap(ListNotification::elements) + .filter { replyVisibilityFilter.isReplyVisible(null, it) } +} + +private fun Collection<*>.cast(): List = map { it as R } diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Guice.kt b/src/test/kotlin/net/pterodactylus/sone/test/Guice.kt index b0bed5c..e6c0fdb 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Guice.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Guice.kt @@ -1,20 +1,27 @@ package net.pterodactylus.sone.test -import com.google.inject.* -import com.google.inject.name.* -import org.hamcrest.MatcherAssert.* -import org.hamcrest.Matchers.* -import org.mockito.* +import com.google.inject.Injector +import com.google.inject.Key +import com.google.inject.Module +import com.google.inject.TypeLiteral +import com.google.inject.name.Names +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.sameInstance +import org.mockito.Mockito import javax.inject.Provider -import kotlin.reflect.* +import kotlin.reflect.KClass fun KClass.isProvidedBy(instance: T) = Module { it.bind(this.java).toProvider(Provider { instance }) } fun KClass.withNameIsProvidedBy(instance: T, name: String) = Module { it.bind(this.java).annotatedWith(Names.named(name)).toProvider(Provider { instance }) } fun KClass.isProvidedBy(provider: com.google.inject.Provider) = Module { it.bind(this.java).toProvider(provider) } fun KClass.isProvidedBy(provider: KClass>) = Module { it.bind(this.java).toProvider(provider.java) } +fun Key.isProvidedBy(instance: T) = Module { it.bind(this).toProvider(Provider { instance }) } inline fun KClass.isProvidedByMock() = Module { it.bind(this.java).toProvider(Provider { mock() }) } inline fun KClass.isProvidedByDeepMock() = Module { it.bind(this.java).toProvider(Provider { deepMock() }) } +inline fun key(): Key = Key.get(object : TypeLiteral() {}) +inline fun key(annotation: Annotation): Key = Key.get(object : TypeLiteral() {}, annotation) + inline fun Injector.getInstance(annotation: Annotation? = null): T = annotation ?.let { getInstance(Key.get(object : TypeLiteral() {}, it)) } ?: getInstance(Key.get(object : TypeLiteral() {})) diff --git a/src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt new file mode 100644 index 0000000..34dfe66 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/NewElementsTest.kt @@ -0,0 +1,92 @@ +/** + * Sone - NewElementsTest.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.pterodactylus.sone.web + +import com.google.inject.Guice +import com.google.inject.name.Names.named +import net.pterodactylus.sone.data.Post +import net.pterodactylus.sone.data.PostReply +import net.pterodactylus.sone.notify.ListNotification +import net.pterodactylus.sone.notify.matchThisPost +import net.pterodactylus.sone.notify.matchThisReply +import net.pterodactylus.sone.notify.showAllPosts +import net.pterodactylus.sone.notify.showAllReplies +import net.pterodactylus.sone.test.createPost +import net.pterodactylus.sone.test.createPostReply +import net.pterodactylus.sone.test.getInstance +import net.pterodactylus.sone.test.isProvidedBy +import net.pterodactylus.sone.test.key +import net.pterodactylus.util.template.Template +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.containsInAnyOrder +import org.junit.Test + +class NewElementsTest { + + private val newPostNotification = ListNotification("", "", Template()) + private val newReplyNotification = ListNotification("", "", Template()) + private val localPostNotification = ListNotification("", "", Template()) + private val localReplyNotification = ListNotification("", "", Template()) + private val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, showAllPosts, showAllReplies) + + init { + localPostNotification.add(post1) + newPostNotification.add(post2) + localReplyNotification.add(reply1) + newReplyNotification.add(reply2) + } + + @Test + fun `new elements container can be created by guice`() { + val injector = Guice.createInjector( + key>(named("newRemotePost")).isProvidedBy(newPostNotification), + key>(named("localPost")).isProvidedBy(localPostNotification), + key>(named("newRemotePostReply")).isProvidedBy(newReplyNotification), + key>(named("localReply")).isProvidedBy(localReplyNotification) + ) + injector.getInstance() + } + + @Test + fun `new posts include new and local posts`() { + assertThat(newElements.newPosts, containsInAnyOrder(post1, post2)) + } + + @Test + fun `new posts are filtered`() { + val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, matchThisPost(post2), showAllReplies) + assertThat(newElements.newPosts, contains(post2)) + } + + @Test + fun `new replies include new and local replies`() { + assertThat(newElements.newReplies, containsInAnyOrder(reply1, reply2)) + } + + @Test + fun `new replies are filtered`() { + val newElements = NewElements(newPostNotification, newReplyNotification, localPostNotification, localReplyNotification, showAllPosts, matchThisReply(reply2)) + assertThat(newElements.newReplies, containsInAnyOrder(reply2)) + } + +} + +private val post1 = createPost() +private val post2 = createPost() +private val reply1 = createPostReply() +private val reply2 = createPostReply() -- 2.7.4