Mon, 05 Aug 2024 19:38:47 +0200
fix removing filter not working
fixes #407
/* * Copyright 2021 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package de.uapcore.lightpit.servlet import com.github.difflib.text.DiffRow import com.github.difflib.text.DiffRowGenerator import de.uapcore.lightpit.AbstractServlet import de.uapcore.lightpit.HttpRequest import de.uapcore.lightpit.dao.DataAccessObject import de.uapcore.lightpit.entities.IssueCommentHistoryEntry import de.uapcore.lightpit.entities.IssueHistoryEntry import de.uapcore.lightpit.entities.Project import de.uapcore.lightpit.types.IssueHistoryType import de.uapcore.lightpit.viewmodel.CommentDiff import de.uapcore.lightpit.viewmodel.IssueDiff import de.uapcore.lightpit.viewmodel.IssueFeed import de.uapcore.lightpit.viewmodel.IssueFeedEntry import jakarta.servlet.annotation.WebServlet import java.text.SimpleDateFormat @WebServlet(urlPatterns = ["/feed/*"]) class FeedServlet : AbstractServlet() { init { get("/%project/issues.rss", this::issues) } private val diffGenerator: DiffRowGenerator by lazyOf(DiffRowGenerator.create() .showInlineDiffs(true) .mergeOriginalRevised(true) .inlineDiffByWord(true) .oldTag { start -> if (start) "<strike style=\"color:red\">" else "</strike>" } .newTag { start -> if (start) "<i style=\"color: green\">" else "</i>" } .build() ) private fun fullContent(data: IssueCommentHistoryEntry) = CommentDiff( data.issueid, data.commentid, data.subject, data.comment.replace("\r", "") ) private fun diffContent(cur: IssueCommentHistoryEntry, next: IssueCommentHistoryEntry) = CommentDiff( cur.issueid, cur.commentid, cur.subject, diffGenerator.generateDiffRows( next.comment.replace("\r", "").split('\n'), cur.comment.replace("\r", "").split('\n') ).joinToString("\n", transform = DiffRow::getOldLine) ) private fun fullContent(issue: IssueHistoryEntry) = IssueDiff( issue.issueid, issue.subject, issue.project, issue.component, issue.status.name, issue.category.name, issue.subject, issue.description.replace("\r", ""), issue.assignee, issue.eta?.let { SimpleDateFormat("dd.MM.yyyy").format(it) } ?: "", issue.affected, issue.resolved ) private fun diffContent(cur: IssueHistoryEntry, next: IssueHistoryEntry): IssueDiff { val prev = fullContent(next) val diff = fullContent(cur) val result = diffGenerator.generateDiffRows( listOf( prev.subject, prev.component, prev.status, prev.category, prev.assignee, prev.eta, prev.affected, prev.resolved ), listOf( diff.subject, diff.component, diff.status, diff.category, diff.assignee, diff.eta, diff.affected, diff.resolved ) ) diff.subject = result[0].oldLine diff.component = result[1].oldLine diff.status = result[2].oldLine diff.category = result[3].oldLine diff.assignee = result[4].oldLine diff.eta = result[5].oldLine diff.affected = result[6].oldLine diff.resolved = result[7].oldLine diff.description = diffGenerator.generateDiffRows( prev.description.split('\n'), diff.description.split('\n') ).joinToString("\n", transform = DiffRow::getOldLine) return diff } /** * Generates the feed entries. * Assumes that [issueEntries] and [commentEntries] are already sorted by timestamp (descending). */ private fun generateFeedEntries( issueEntries: List<IssueHistoryEntry>, commentEntries: List<IssueCommentHistoryEntry> ): List<IssueFeedEntry> = (generateIssueFeedEntries(issueEntries) + generateCommentFeedEntries(commentEntries)).sortedByDescending { it.time } private fun generateIssueFeedEntries(entries: List<IssueHistoryEntry>): List<IssueFeedEntry> = if (entries.isEmpty()) { emptyList() } else { entries.groupBy { it.issueid }.mapValues { (_, history) -> history.zipWithNext().map { (cur, next) -> IssueFeedEntry( cur.time, cur.type, issue = diffContent(cur, next) ) }.plus( history.last().let { IssueFeedEntry(it.time, it.type, issue = fullContent(it)) } ) }.flatMap { it.value } } private fun generateCommentFeedEntries(entries: List<IssueCommentHistoryEntry>): List<IssueFeedEntry> = if (entries.isEmpty()) { emptyList() } else { entries.groupBy { it.commentid }.mapValues { (_, history) -> history.zipWithNext().map { (cur, next) -> IssueFeedEntry( cur.time, cur.type, comment = diffContent(cur, next) ) }.plus( history.last().let { IssueFeedEntry(it.time, it.type, comment = fullContent(it)) } ) }.flatMap { it.value } } private fun issues(http: HttpRequest, dao: DataAccessObject) { val projectNode = http.pathParams["project"].orEmpty() val project: Project? if (projectNode == "-") { project = null } else { project = dao.findProjectByNode(projectNode) if (project == null) { http.response.sendError(404) return } } val assignees = http.param("assignee")?.split(',') val comments = http.param("comments") ?: "all" val days = http.param("days")?.toIntOrNull() ?: 30 val issuesFromDb = dao.listIssueHistory(project, days) val issueHistory = if (assignees == null) issuesFromDb else issuesFromDb.filter { assignees.contains(it.currentAssignee) } val commentsFromDb = dao.listIssueCommentHistory(project, days) val commentHistory = when (comments) { "all" -> commentsFromDb "new" -> commentsFromDb.filter { it.type == IssueHistoryType.NewComment } else -> emptyList() } http.view = IssueFeed(project, generateFeedEntries(issueHistory, commentHistory)) http.renderFeed("issues-feed") } }