src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt

changeset 159
86b5d8a1662f
child 164
003b08bb3f25
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/postgres/PGIssueDao.kt	Thu Nov 19 13:58:54 2020 +0100
     1.3 @@ -0,0 +1,249 @@
     1.4 +/*
     1.5 + * Copyright 2020 Mike Becker. All rights reserved.
     1.6 + *
     1.7 + * Redistribution and use in source and binary forms, with or without
     1.8 + * modification, are permitted provided that the following conditions are met:
     1.9 + *
    1.10 + * 1. Redistributions of source code must retain the above copyright
    1.11 + * notice, this list of conditions and the following disclaimer.
    1.12 + *
    1.13 + * 2. Redistributions in binary form must reproduce the above copyright
    1.14 + * notice, this list of conditions and the following disclaimer in the
    1.15 + * documentation and/or other materials provided with the distribution.
    1.16 + *
    1.17 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    1.18 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    1.19 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    1.20 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    1.21 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    1.22 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    1.23 + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    1.24 + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    1.25 + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.27 + *
    1.28 + */
    1.29 +
    1.30 +package de.uapcore.lightpit.dao.postgres
    1.31 +
    1.32 +import de.uapcore.lightpit.dao.AbstractIssueDao
    1.33 +import de.uapcore.lightpit.dao.Functions
    1.34 +import de.uapcore.lightpit.entities.*
    1.35 +import java.sql.Connection
    1.36 +import java.sql.PreparedStatement
    1.37 +import java.sql.ResultSet
    1.38 +import java.sql.Types
    1.39 +
    1.40 +class PGIssueDao(connection: Connection) : AbstractIssueDao() {
    1.41 +
    1.42 +    private val query = "select issueid, i.project, p.name as projectname, p.node as projectnode, " +
    1.43 +            "component, c.name as componentname, c.node as componentnode, " +
    1.44 +            "status, category, subject, i.description, " +
    1.45 +            "userid, username, givenname, lastname, mail, " +
    1.46 +            "created, updated, eta " +
    1.47 +            "from lpit_issue i " +
    1.48 +            "join lpit_project p on i.project = projectid " +
    1.49 +            "left join lpit_component c on component = c.id " +
    1.50 +            "left join lpit_user on userid = assignee "
    1.51 +    private val list = connection.prepareStatement(query +
    1.52 +            "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)")
    1.53 +    private val listForVersion = connection.prepareStatement(
    1.54 +            "with issue_version as ( " +
    1.55 +                    "select issueid, versionid from lpit_issue_affected_version union " +
    1.56 +                    "select issueid, versionid from lpit_issue_resolved_version) " +
    1.57 +                    query +
    1.58 +                    "left join issue_version using (issueid) " +
    1.59 +                    "where i.project = ? " +
    1.60 +                    "and coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)"
    1.61 +    )
    1.62 +    private val find = connection.prepareStatement(query + "where issueid = ? ")
    1.63 +    private val insert = connection.prepareStatement(
    1.64 +            "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " +
    1.65 +                    "values (?, ?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid"
    1.66 +    )
    1.67 +    private val update = connection.prepareStatement(
    1.68 +            "update lpit_issue set " +
    1.69 +                    "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " +
    1.70 +                    "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?"
    1.71 +    )
    1.72 +    private val affectedVersions = connection.prepareStatement(
    1.73 +            "select versionid, name, status, ordinal, node " +
    1.74 +                    "from lpit_version join lpit_issue_affected_version using (versionid) " +
    1.75 +                    "where issueid = ? " +
    1.76 +                    "order by ordinal, name"
    1.77 +    )
    1.78 +    private val clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?")
    1.79 +    private val insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)")
    1.80 +
    1.81 +    private val resolvedVersions = connection.prepareStatement(
    1.82 +            "select versionid, name, status, ordinal, node " +
    1.83 +                    "from lpit_version v join lpit_issue_resolved_version using (versionid) " +
    1.84 +                    "where issueid = ? " +
    1.85 +                    "order by ordinal, name"
    1.86 +    )
    1.87 +    private val clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?")
    1.88 +    private val insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)")
    1.89 +    private val insertComment = connection.prepareStatement(
    1.90 +            "insert into lpit_issue_comment (issueid, comment, userid) values (?, ? ,?)"
    1.91 +    )
    1.92 +    private val updateComment = connection.prepareStatement(
    1.93 +            "update lpit_issue_comment set comment = ?, updated = now(), updatecount = updatecount+1 where commentid = ?"
    1.94 +    )
    1.95 +    private val listComments = connection.prepareStatement(
    1.96 +            "select * from lpit_issue_comment left join lpit_user using (userid) where issueid = ? order by created"
    1.97 +    )
    1.98 +
    1.99 +    override fun mapResult(rs: ResultSet): Issue {
   1.100 +        val project = Project(rs.getInt("project"))
   1.101 +        project.name = rs.getString("projectname")
   1.102 +        project.node = rs.getString("projectnode")
   1.103 +        val issue = Issue(rs.getInt("issueid"))
   1.104 +        issue.project = project
   1.105 +        issue.component = rs.getInt("component").let { id ->
   1.106 +            if (rs.wasNull()) {
   1.107 +                null
   1.108 +            } else {
   1.109 +                val component = Component(id)
   1.110 +                component.name = rs.getString("componentname")
   1.111 +                component.node = rs.getString("componentnode")
   1.112 +                component
   1.113 +            }
   1.114 +        }
   1.115 +        issue.status = IssueStatus.valueOf(rs.getString("status"))
   1.116 +        issue.category = IssueCategory.valueOf(rs.getString("category"))
   1.117 +        issue.subject = rs.getString("subject")
   1.118 +        issue.description = rs.getString("description")
   1.119 +        issue.assignee = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
   1.120 +        issue.created = rs.getTimestamp("created")
   1.121 +        issue.updated = rs.getTimestamp("updated")
   1.122 +        issue.eta = rs.getDate("eta")
   1.123 +        return issue
   1.124 +    }
   1.125 +
   1.126 +    private fun updateVersionLists(instance: Issue) {
   1.127 +        clearAffected.setInt(1, instance.id)
   1.128 +        clearResolved.setInt(1, instance.id)
   1.129 +        insertAffected.setInt(1, instance.id)
   1.130 +        insertResolved.setInt(1, instance.id)
   1.131 +        clearAffected.executeUpdate()
   1.132 +        clearResolved.executeUpdate()
   1.133 +        for (v: Version in instance.affectedVersions) {
   1.134 +            insertAffected.setInt(2, v.id)
   1.135 +            insertAffected.executeUpdate()
   1.136 +        }
   1.137 +        for (v: Version in instance.resolvedVersions) {
   1.138 +            insertResolved.setInt(2, v.id)
   1.139 +            insertResolved.executeUpdate()
   1.140 +        }
   1.141 +    }
   1.142 +
   1.143 +    private fun setData(stmt: PreparedStatement, column: Int, instance: Issue): Int {
   1.144 +        var col = column
   1.145 +        setForeignKeyOrNull(stmt, ++col, instance.component, Component::id)
   1.146 +        stmt.setString(++col, instance.status.name)
   1.147 +        stmt.setString(++col, instance.category.name)
   1.148 +        stmt.setString(++col, instance.subject)
   1.149 +        Functions.setStringOrNull(stmt, ++col, instance.description)
   1.150 +        setForeignKeyOrNull(stmt, ++col, instance.assignee, User::id)
   1.151 +        Functions.setDateOrNull(stmt, ++col, instance.eta)
   1.152 +        return col
   1.153 +    }
   1.154 +
   1.155 +    override fun save(instance: Issue, parent: Project) {
   1.156 +        instance.project = parent
   1.157 +        var column = 0
   1.158 +        insert.setInt(++column, parent.id)
   1.159 +        setData(insert, column, instance)
   1.160 +        // insert and retrieve the ID
   1.161 +        val rs = insert.executeQuery()
   1.162 +        rs.next()
   1.163 +        instance.id = rs.getInt(1)
   1.164 +        updateVersionLists(instance)
   1.165 +    }
   1.166 +
   1.167 +    override fun update(instance: Issue): Boolean {
   1.168 +        var column = setData(update, 0, instance)
   1.169 +        update.setInt(++column, instance.id)
   1.170 +        return if (update.executeUpdate() > 0) {
   1.171 +            updateVersionLists(instance)
   1.172 +            true
   1.173 +        } else {
   1.174 +            false
   1.175 +        }
   1.176 +    }
   1.177 +
   1.178 +    override fun list(parent: Project): List<Issue> {
   1.179 +        list.setInt(1, parent.id)
   1.180 +        list.setNull(2, Types.INTEGER)
   1.181 +        return super.list(list)
   1.182 +    }
   1.183 +
   1.184 +    override fun list(project: Project, component: Component?, version: Version?): List<Issue> {
   1.185 +        listForVersion.setInt(1, project.id)
   1.186 +        listForVersion.setInt(2, version?.id ?: -1)
   1.187 +        listForVersion.setInt(3, component?.id ?: -1)
   1.188 +        return super.list(listForVersion)
   1.189 +    }
   1.190 +
   1.191 +    override fun list(project: Project, version: Version?): List<Issue> {
   1.192 +        listForVersion.setInt(1, project.id)
   1.193 +        listForVersion.setInt(2, version?.id ?: -1)
   1.194 +        listForVersion.setNull(3, Types.INTEGER)
   1.195 +        return super.list(listForVersion)
   1.196 +    }
   1.197 +
   1.198 +    override fun list(project: Project, component: Component?): List<Issue> {
   1.199 +        list.setInt(1, project.id)
   1.200 +        list.setInt(2, component?.id ?: -1)
   1.201 +        return super.list(list)
   1.202 +    }
   1.203 +
   1.204 +    override fun find(id: Int): Issue? {
   1.205 +        find.setInt(1, id)
   1.206 +        return super.find(find)
   1.207 +    }
   1.208 +
   1.209 +    private fun listVersions(stmt: PreparedStatement, issue: Issue): List<Version> {
   1.210 +        stmt.setInt(1, issue.id)
   1.211 +        return sequence {
   1.212 +            stmt.executeQuery().use { result ->
   1.213 +                while (result.next()) yield(PGVersionDao.mapResult(result))
   1.214 +            }
   1.215 +        }.toList()
   1.216 +    }
   1.217 +
   1.218 +    override fun joinVersionInformation(issue: Issue) {
   1.219 +        issue.affectedVersions = listVersions(affectedVersions, issue)
   1.220 +        issue.resolvedVersions = listVersions(resolvedVersions, issue)
   1.221 +    }
   1.222 +
   1.223 +    override fun listComments(issue: Issue): List<IssueComment> {
   1.224 +        listComments.setInt(1, issue.id)
   1.225 +        return sequence {
   1.226 +            listComments.executeQuery().use { rs ->
   1.227 +                while (rs.next()) {
   1.228 +                    val comment = IssueComment(rs.getInt("commentid"))
   1.229 +                    comment.created = rs.getTimestamp("created")
   1.230 +                    comment.updated = rs.getTimestamp("updated")
   1.231 +                    comment.updateCount = rs.getInt("updatecount")
   1.232 +                    comment.comment = rs.getString("comment")
   1.233 +                    comment.author = PGUserDao.mapResult(rs).takeUnless { rs.wasNull() }
   1.234 +                    yield(comment)
   1.235 +                }
   1.236 +            }
   1.237 +        }.toList()
   1.238 +    }
   1.239 +
   1.240 +    override fun saveComment(issue: Issue, comment: IssueComment) {
   1.241 +        if (comment.id >= 0) {
   1.242 +            updateComment.setString(1, comment.comment)
   1.243 +            updateComment.setInt(2, comment.id)
   1.244 +            updateComment.execute()
   1.245 +        } else {
   1.246 +            insertComment.setInt(1, issue.id)
   1.247 +            insertComment.setString(2, comment.comment)
   1.248 +            setForeignKeyOrNull(insertComment, 3, comment.author, User::id)
   1.249 +            insertComment.execute()
   1.250 +        }
   1.251 +    }
   1.252 +}
   1.253 \ No newline at end of file

mercurial