src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java

Sat, 29 Aug 2020 17:13:09 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 29 Aug 2020 17:13:09 +0200
changeset 105
250c5cbb8276
parent 104
8be80ea4f52b
child 107
b5f740a87af4
permissions
-rw-r--r--

simplifies issues per version view and re-adds edit version button

universe@41 1 /*
universe@41 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@41 3 *
universe@41 4 * Copyright 2018 Mike Becker. All rights reserved.
universe@41 5 *
universe@41 6 * Redistribution and use in source and binary forms, with or without
universe@41 7 * modification, are permitted provided that the following conditions are met:
universe@41 8 *
universe@41 9 * 1. Redistributions of source code must retain the above copyright
universe@41 10 * notice, this list of conditions and the following disclaimer.
universe@41 11 *
universe@41 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@41 13 * notice, this list of conditions and the following disclaimer in the
universe@41 14 * documentation and/or other materials provided with the distribution.
universe@41 15 *
universe@41 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@41 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@41 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@41 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@41 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@41 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@41 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@41 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@41 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@41 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@41 26 * POSSIBILITY OF SUCH DAMAGE.
universe@41 27 *
universe@41 28 */
universe@41 29 package de.uapcore.lightpit.modules;
universe@41 30
universe@41 31
universe@41 32 import de.uapcore.lightpit.*;
universe@41 33 import de.uapcore.lightpit.dao.DataAccessObjects;
universe@64 34 import de.uapcore.lightpit.entities.*;
universe@86 35 import de.uapcore.lightpit.viewmodel.*;
universe@59 36 import org.slf4j.Logger;
universe@59 37 import org.slf4j.LoggerFactory;
universe@41 38
universe@41 39 import javax.servlet.annotation.WebServlet;
universe@41 40 import javax.servlet.http.HttpServletRequest;
universe@59 41 import javax.servlet.http.HttpServletResponse;
universe@59 42 import java.io.IOException;
universe@75 43 import java.sql.Date;
universe@47 44 import java.sql.SQLException;
universe@86 45 import java.util.ArrayList;
universe@86 46 import java.util.NoSuchElementException;
universe@99 47 import java.util.Optional;
universe@83 48 import java.util.stream.Collectors;
universe@83 49 import java.util.stream.Stream;
universe@41 50
universe@52 51 import static de.uapcore.lightpit.Functions.fqn;
universe@52 52
universe@41 53 @WebServlet(
universe@41 54 name = "ProjectsModule",
universe@41 55 urlPatterns = "/projects/*"
universe@41 56 )
universe@41 57 public final class ProjectsModule extends AbstractLightPITServlet {
universe@41 58
universe@59 59 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
universe@59 60
universe@99 61 private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project");
universe@99 62 private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version");
universe@99 63 private static final String PARAMETER_SELECTED_PROJECT = "pid";
universe@99 64 private static final String PARAMETER_SELECTED_VERSION = "vid";
universe@71 65
universe@78 66 @Override
universe@78 67 protected String getResourceBundleName() {
universe@78 68 return "localization.projects";
universe@78 69 }
universe@71 70
universe@99 71 private String queryParams(Project p, Version v) {
universe@99 72 return String.format("pid=%d&vid=%d",
universe@97 73 p == null ? -1 : p.getId(),
universe@99 74 v == null ? -1 : v.getId()
universe@97 75 );
universe@97 76 }
universe@80 77
universe@71 78 /**
universe@96 79 * Creates the navigation menu.
universe@71 80 *
universe@99 81 * @param req the servlet request
universe@99 82 * @param viewModel the current view model
universe@71 83 */
universe@99 84 private void setNavigationMenu(HttpServletRequest req, ProjectView viewModel) {
universe@99 85 final Project selectedProject = Optional.ofNullable(viewModel.getProjectInfo()).map(ProjectInfo::getProject).orElse(null);
universe@99 86
universe@97 87 final var navigation = new ArrayList<MenuEntry>();
universe@71 88
universe@99 89 for (ProjectInfo plistInfo : viewModel.getProjectList()) {
universe@99 90 final var proj = plistInfo.getProject();
universe@97 91 final var projEntry = new MenuEntry(
universe@97 92 proj.getName(),
universe@99 93 "projects/view?" + queryParams(proj, null)
universe@97 94 );
universe@97 95 navigation.add(projEntry);
universe@99 96 if (proj.equals(selectedProject)) {
universe@99 97 final var projInfo = viewModel.getProjectInfo();
universe@97 98 projEntry.setActive(true);
universe@71 99
universe@97 100 // ****************
universe@97 101 // Versions Section
universe@97 102 // ****************
universe@97 103 {
universe@97 104 final var entry = new MenuEntry(1,
universe@99 105 new ResourceKey(getResourceBundleName(), "menu.versions"),
universe@99 106 "projects/view?" + queryParams(proj, null)
universe@97 107 );
universe@97 108 navigation.add(entry);
universe@97 109 }
universe@97 110
universe@97 111 final var level2 = new ArrayList<MenuEntry>();
universe@97 112 {
universe@97 113 final var entry = new MenuEntry(
universe@105 114 new ResourceKey(getResourceBundleName(), "filter.none"),
universe@99 115 "projects/view?" + queryParams(proj, null)
universe@97 116 );
universe@99 117 if (viewModel.getVersionFilter() == null) entry.setActive(true);
universe@97 118 level2.add(entry);
universe@97 119 }
universe@97 120
universe@97 121 for (Version version : projInfo.getVersions()) {
universe@97 122 final var entry = new MenuEntry(
universe@97 123 version.getName(),
universe@99 124 "projects/view?" + queryParams(proj, version)
universe@97 125 );
universe@99 126 if (version.equals(viewModel.getVersionFilter())) entry.setActive(true);
universe@97 127 level2.add(entry);
universe@97 128 }
universe@97 129
universe@97 130 level2.forEach(e -> e.setLevel(2));
universe@97 131 navigation.addAll(level2);
universe@75 132 }
universe@75 133 }
universe@75 134
universe@99 135 setNavigationMenu(req, navigation);
universe@99 136 }
universe@99 137
universe@99 138 private int syncParamWithSession(HttpServletRequest req, String param, String attr) {
universe@99 139 final var session = req.getSession();
universe@99 140 final var idParam = getParameter(req, Integer.class, param);
universe@99 141 final int id;
universe@99 142 if (idParam.isPresent()) {
universe@99 143 id = idParam.get();
universe@99 144 session.setAttribute(attr, id);
universe@99 145 } else {
universe@99 146 id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1);
universe@99 147 }
universe@99 148 return id;
universe@99 149 }
universe@99 150
universe@99 151 private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException {
universe@99 152 final var projectDao = dao.getProjectDao();
universe@99 153 final var versionDao = dao.getVersionDao();
universe@99 154
universe@99 155 projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
universe@99 156
universe@99 157 // Select Project
universe@99 158 final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT);
universe@99 159 if (pid >= 0) {
universe@99 160 final var project = projectDao.find(pid);
universe@99 161 final var info = new ProjectInfo(project);
universe@99 162 info.setVersions(versionDao.list(project));
universe@99 163 info.setIssueSummary(projectDao.getIssueSummary(project));
universe@99 164 viewModel.setProjectInfo(info);
universe@99 165 }
universe@99 166
universe@99 167 // Select Version
universe@99 168 final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION);
universe@99 169 if (vid >= 0) {
universe@99 170 viewModel.setVersionFilter(versionDao.find(vid));
universe@99 171 }
universe@99 172 }
universe@99 173
universe@99 174 private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
universe@99 175 setViewModel(req, viewModel);
universe@99 176 setContentPage(req, name);
universe@99 177 setStylesheet(req, "projects");
universe@99 178 setNavigationMenu(req, viewModel);
universe@99 179 return ResponseType.HTML;
universe@64 180 }
universe@64 181
universe@61 182 @RequestMapping(method = HttpMethod.GET)
universe@47 183 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
universe@99 184 final var viewModel = new ProjectView();
universe@99 185 populate(viewModel, req, dao);
universe@86 186
universe@86 187 final var projectDao = dao.getProjectDao();
universe@86 188 final var versionDao = dao.getVersionDao();
universe@86 189
universe@99 190 for (var info : viewModel.getProjectList()) {
universe@99 191 info.setVersions(versionDao.list(info.getProject()));
universe@99 192 info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
universe@86 193 }
universe@86 194
universe@99 195 return forwardView(req, viewModel, "projects");
universe@45 196 }
universe@45 197
universe@99 198 private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
universe@99 199 viewModel.setProject(project);
universe@86 200 viewModel.setUsers(dao.getUserDao().list());
universe@71 201 }
universe@71 202
universe@47 203 @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
universe@51 204 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
universe@99 205 final var viewModel = new ProjectEditView();
universe@99 206 populate(viewModel, req, dao);
universe@47 207
universe@99 208 final var project = Optional.ofNullable(viewModel.getProjectInfo())
universe@99 209 .map(ProjectInfo::getProject)
universe@99 210 .orElse(new Project(-1));
universe@99 211 configure(viewModel, project, dao);
universe@47 212
universe@99 213 return forwardView(req, viewModel, "project-form");
universe@47 214 }
universe@47 215
universe@47 216 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
universe@68 217 public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
universe@47 218
universe@75 219 Project project = new Project(-1);
universe@47 220 try {
universe@99 221 project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
universe@47 222 project.setName(getParameter(req, String.class, "name").orElseThrow());
universe@47 223 getParameter(req, String.class, "description").ifPresent(project::setDescription);
universe@47 224 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
universe@47 225 getParameter(req, Integer.class, "owner").map(
universe@47 226 ownerId -> ownerId >= 0 ? new User(ownerId) : null
universe@47 227 ).ifPresent(project::setOwner);
universe@47 228
universe@47 229 dao.getProjectDao().saveOrUpdate(project);
universe@47 230
universe@99 231 setRedirectLocation(req, "./projects/view?pid="+project.getId());
universe@74 232 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
universe@59 233 LOG.debug("Successfully updated project {}", project.getName());
universe@99 234
universe@99 235 return ResponseType.HTML;
universe@75 236 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
universe@59 237 LOG.warn("Form validation failure: {}", ex.getMessage());
universe@59 238 LOG.debug("Details:", ex);
universe@99 239 final var viewModel = new ProjectEditView();
universe@99 240 populate(viewModel, req, dao);
universe@99 241 configure(viewModel, project, dao);
universe@99 242 // TODO: error text
universe@99 243 return forwardView(req, viewModel, "project-form");
universe@47 244 }
universe@47 245 }
universe@47 246
universe@70 247 @RequestMapping(requestPath = "view", method = HttpMethod.GET)
universe@80 248 public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
universe@99 249 final var viewModel = new ProjectDetailsView();
universe@99 250 populate(viewModel, req, dao);
universe@86 251
universe@99 252 if (viewModel.getProjectInfo() == null) {
universe@80 253 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
universe@80 254 return ResponseType.NONE;
universe@80 255 }
universe@47 256
universe@86 257 final var issueDao = dao.getIssueDao();
universe@70 258
universe@105 259 final var version = viewModel.getVersionFilter();
universe@99 260
universe@99 261 final var detailView = viewModel.getProjectDetails();
universe@105 262 final var issues = issueDao.list(version);
universe@100 263 for (var issue : issues) issueDao.joinVersionInformation(issue);
universe@105 264 detailView.updateDetails(issues, version);
universe@80 265
universe@99 266 return forwardView(req, viewModel, "project-details");
universe@71 267 }
universe@71 268
universe@59 269 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
universe@86 270 public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
universe@99 271 final var viewModel = new VersionEditView();
universe@99 272 populate(viewModel, req, dao);
universe@99 273
universe@99 274 if (viewModel.getVersionFilter() == null) {
universe@99 275 viewModel.setVersion(new Version(-1));
universe@86 276 } else {
universe@99 277 viewModel.setVersion(viewModel.getVersionFilter());
universe@86 278 }
universe@59 279
universe@99 280 return forwardView(req, viewModel, "version-form");
universe@59 281 }
universe@59 282
universe@59 283 @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
universe@80 284 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
universe@59 285
universe@86 286 var version = new Version(-1);
universe@59 287 try {
universe@86 288 version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
universe@86 289 version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
universe@59 290 version.setName(getParameter(req, String.class, "name").orElseThrow());
universe@59 291 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
universe@59 292 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
universe@59 293 dao.getVersionDao().saveOrUpdate(version);
universe@59 294
universe@75 295 // specifying the pid parameter will purposely reset the session selected version!
universe@96 296 setRedirectLocation(req, "./projects/view?pid=" + version.getProject().getId());
universe@74 297 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
universe@75 298 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
universe@59 299 LOG.warn("Form validation failure: {}", ex.getMessage());
universe@59 300 LOG.debug("Details:", ex);
universe@99 301 final var viewModel = new VersionEditView();
universe@99 302 populate(viewModel, req, dao);
universe@99 303 viewModel.setVersion(version);
universe@86 304 // TODO: set Error Text
universe@99 305 return forwardView(req, viewModel, "version-form");
universe@59 306 }
universe@41 307
universe@43 308 return ResponseType.HTML;
universe@41 309 }
universe@64 310
universe@99 311 private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
universe@99 312 issue.setProject(viewModel.getProjectInfo().getProject());
universe@99 313 viewModel.setIssue(issue);
universe@99 314 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
universe@86 315 viewModel.setUsers(dao.getUserDao().list());
universe@71 316 }
universe@71 317
universe@64 318 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
universe@80 319 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
universe@99 320 final var viewModel = new IssueEditView();
universe@99 321
universe@99 322 final var issueParam = getParameter(req, Integer.class, "issue");
universe@99 323 if (issueParam.isPresent()) {
universe@104 324 final var issueDao = dao.getIssueDao();
universe@104 325 final var issue = issueDao.find(issueParam.get());
universe@104 326 issueDao.joinVersionInformation(issue);
universe@99 327 req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId());
universe@99 328 populate(viewModel, req, dao);
universe@99 329 configure(viewModel, issue, dao);
universe@86 330 } else {
universe@99 331 populate(viewModel, req, dao);
universe@99 332 configure(viewModel, new Issue(-1), dao);
universe@86 333 }
universe@64 334
universe@99 335 return forwardView(req, viewModel, "issue-form");
universe@64 336 }
universe@64 337
universe@64 338 @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
universe@80 339 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
universe@86 340 Issue issue = new Issue(-1);
universe@64 341 try {
universe@86 342 issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
universe@86 343 issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
universe@75 344 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
universe@75 345 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
universe@75 346 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
universe@75 347 getParameter(req, Integer.class, "assignee").map(
universe@75 348 userid -> userid >= 0 ? new User(userid) : null
universe@75 349 ).ifPresent(issue::setAssignee);
universe@75 350 getParameter(req, String.class, "description").ifPresent(issue::setDescription);
universe@75 351 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
universe@83 352
universe@83 353 getParameter(req, Integer[].class, "affected")
universe@83 354 .map(Stream::of)
universe@83 355 .map(stream ->
universe@96 356 stream.map(Version::new).collect(Collectors.toList())
universe@83 357 ).ifPresent(issue::setAffectedVersions);
universe@83 358 getParameter(req, Integer[].class, "resolved")
universe@83 359 .map(Stream::of)
universe@83 360 .map(stream ->
universe@86 361 stream.map(Version::new).collect(Collectors.toList())
universe@83 362 ).ifPresent(issue::setResolvedVersions);
universe@83 363
universe@64 364 dao.getIssueDao().saveOrUpdate(issue);
universe@64 365
universe@96 366 // specifying the issue parameter keeps the edited issue as menu item
universe@102 367 setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId());
universe@74 368 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
universe@75 369 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
universe@64 370 // TODO: set request attribute with error text
universe@64 371 LOG.warn("Form validation failure: {}", ex.getMessage());
universe@64 372 LOG.debug("Details:", ex);
universe@99 373 final var viewModel = new IssueEditView();
universe@99 374 configure(viewModel, issue, dao);
universe@86 375 // TODO: set Error Text
universe@99 376 return forwardView(req, viewModel, "issue-form");
universe@64 377 }
universe@64 378
universe@64 379 return ResponseType.HTML;
universe@64 380 }
universe@41 381 }

mercurial