Skip to content

Commit 108acf7

Browse files
committed
feat: admin can add a comment to a series.
Implementation details: a comment can be added only if there is no comment yet. This is a protection against possible race or other unforeseeable circumstances that might lead to data loss. A full implementation of the editing can be added later so this can be considered as the first iteration. Fix #785
1 parent 4e2ef06 commit 108acf7

File tree

16 files changed

+248
-5
lines changed

16 files changed

+248
-5
lines changed

NEWS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- (feature) a series can be marked as a similar to another one
33
- (feature) series images can be replaced
44
- (feature) add preliminary support for hidden images
5+
- (feature) admin can add a comment to a series
56

67
0.4.3
78
- (feature) add support for Ukrainian hryvnia

src/main/frontend/src/components/AddCommentForm.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class AddCommentForm extends React.PureComponent {
5454

5555
if (data.hasOwnProperty('fieldErrors')) {
5656
const fieldErrors = [];
57-
if (data.fieldErrors.comment) {
58-
fieldErrors.push(...data.fieldErrors.comment);
57+
if (data.fieldErrors.value) {
58+
fieldErrors.push(...data.fieldErrors.value);
5959
}
6060
this.setState({
6161
isDisabled: false,

src/main/java/ru/mystamps/web/feature/series/JdbcSeriesDao.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public class JdbcSeriesDao implements SeriesDao {
4848
@Value("${series.create}")
4949
private String createSeriesSql;
5050

51+
@Value("${series.add_comment}")
52+
private String addCommentSql;
53+
5154
@Value("${series.mark_as_modified}")
5255
private String markAsModifiedSql;
5356

@@ -136,6 +139,22 @@ public Integer add(AddSeriesDbDto series) {
136139
return Integer.valueOf(holder.getKey().intValue());
137140
}
138141

142+
@Override
143+
public void addComment(Integer seriesId, String comment) {
144+
Map<String, Object> params = new HashMap<>();
145+
params.put("series_id", seriesId);
146+
params.put("comment", comment);
147+
148+
int affected = jdbcTemplate.update(addCommentSql, params);
149+
150+
// @todo #785 Update series: handle refuse to update an existing comment gracefully
151+
Validate.validState(
152+
affected == 1,
153+
"Unexpected number of affected rows after updating series: %d",
154+
affected
155+
);
156+
}
157+
139158
/**
140159
* @author Sergey Chechenev
141160
*/
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (C) 2009-2020 Slava Semushin <slava.semushin@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
package ru.mystamps.web.feature.series;
19+
20+
import lombok.RequiredArgsConstructor;
21+
import org.springframework.http.ResponseEntity;
22+
import org.springframework.validation.annotation.Validated;
23+
import org.springframework.web.bind.annotation.PatchMapping;
24+
import org.springframework.web.bind.annotation.PathVariable;
25+
import org.springframework.web.bind.annotation.RequestBody;
26+
import org.springframework.web.bind.annotation.RestController;
27+
import ru.mystamps.web.support.spring.mvc.PatchRequest;
28+
import ru.mystamps.web.support.spring.mvc.PatchRequest.Operation;
29+
30+
import javax.servlet.http.HttpServletResponse;
31+
import javax.validation.Valid;
32+
import javax.validation.constraints.NotEmpty;
33+
import java.io.IOException;
34+
import java.util.List;
35+
36+
@Validated
37+
@RestController
38+
@RequiredArgsConstructor
39+
class RestSeriesController {
40+
41+
private final SeriesService seriesService;
42+
43+
// @todo #785 Update series: add integration test
44+
// @todo #785 Update series: add validation for a comment
45+
@PatchMapping(SeriesUrl.INFO_SERIES_PAGE)
46+
@SuppressWarnings("PMD.TooFewBranchesForASwitchStatement")
47+
public ResponseEntity<Void> updateSeries(
48+
@PathVariable("id") Integer seriesId,
49+
@RequestBody @Valid @NotEmpty List<@Valid PatchRequest> patches,
50+
HttpServletResponse response) throws IOException {
51+
52+
if (seriesId == null) {
53+
response.sendError(HttpServletResponse.SC_NOT_FOUND);
54+
return null;
55+
}
56+
57+
if (!seriesService.isSeriesExist(seriesId)) {
58+
response.sendError(HttpServletResponse.SC_NOT_FOUND);
59+
return null;
60+
}
61+
62+
for (PatchRequest patch : patches) {
63+
if (patch.op != Operation.add) {
64+
// @todo #785 Update series: properly fail on non-supported operations
65+
continue;
66+
}
67+
68+
switch (patch.path) {
69+
case "/comment":
70+
seriesService.addComment(seriesId, patch.value);
71+
break;
72+
default:
73+
// @todo #785 Update series: properly fail on invalid path
74+
break;
75+
}
76+
}
77+
78+
return ResponseEntity.noContent().build();
79+
}
80+
81+
}
82+

src/main/java/ru/mystamps/web/feature/series/SeriesConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public SeriesController seriesController() {
6969
);
7070
}
7171

72+
@Bean
73+
public RestSeriesController restSeriesController() {
74+
return new RestSeriesController(seriesService);
75+
}
76+
7277
}
7378

7479
@Import(Daos.class)

src/main/java/ru/mystamps/web/feature/series/SeriesDao.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
@SuppressWarnings("PMD.TooManyMethods")
2525
public interface SeriesDao {
2626
Integer add(AddSeriesDbDto series);
27+
void addComment(Integer seriesId, String comment);
2728
void markAsModified(Integer seriesId, Date updateAt, Integer updatedBy);
2829
List<SitemapInfoDto> findAllForSitemap();
2930
List<SeriesLinkDto> findSimilarSeries(Integer seriesId, String lang);

src/main/java/ru/mystamps/web/feature/series/SeriesService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
@SuppressWarnings("PMD.TooManyMethods")
2525
public interface SeriesService {
2626
Integer add(AddSeriesDto dto, Integer userId, boolean userCanAddComments);
27+
void addComment(Integer seriesId, String comment);
2728
void addImageToSeries(AddImageDto dto, Integer seriesId, Integer userId);
2829
void replaceImage(ReplaceImageDto dto, Integer seriesId, Integer userId);
2930
long countAll();

src/main/java/ru/mystamps/web/feature/series/SeriesServiceImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,23 @@ public Integer add(AddSeriesDto dto, Integer userId, boolean userCanAddComments)
118118
return id;
119119
}
120120

121+
// @todo #785 SeriesServiceImpl.addComment(): add unit tests
122+
@Override
123+
@Transactional
124+
@PreAuthorize(HasAuthority.ADD_COMMENTS_TO_SERIES)
125+
public void addComment(Integer seriesId, String comment) {
126+
Validate.isTrue(seriesId != null, "Series id must be non null");
127+
128+
// We don't touch updated_at/updated_by fields because:
129+
// - a comment is a meta information that is visible only by admins.
130+
// From user's point of view, this field doesn't exist
131+
// - updated_at field is used by logic for sitemap.xml generation
132+
// and we don't want to affect this
133+
seriesDao.addComment(seriesId, comment);
134+
135+
log.info("Series #{}: a comment has been added", seriesId);
136+
}
137+
121138
@Override
122139
@Transactional
123140
@PreAuthorize("isAuthenticated()")

src/main/java/ru/mystamps/web/feature/site/ResourceUrl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public final class ResourceUrl {
3232
public static final String STATIC_RESOURCES_URL = "https://stamps.filezz.ru";
3333

3434
// MUST be updated when any of our resources were modified
35-
public static final String RESOURCES_VERSION = "v0.4.3.8";
35+
public static final String RESOURCES_VERSION = "v0.4.3.9";
3636

3737
// CheckStyle: ignore LineLength for next 15 lines
3838
private static final String CATALOG_UTILS_JS = "/public/js/" + RESOURCES_VERSION + "/CatalogUtils.min.js";
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (C) 2009-2020 Slava Semushin <slava.semushin@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17+
*/
18+
19+
package ru.mystamps.web.support.spring.mvc;
20+
21+
import lombok.Getter;
22+
import lombok.Setter;
23+
import lombok.ToString;
24+
25+
import javax.validation.constraints.NotEmpty;
26+
import javax.validation.constraints.NotNull;
27+
28+
// See for details: http://jsonpatch.com
29+
@Getter
30+
@Setter
31+
@ToString
32+
public class PatchRequest {
33+
34+
public enum Operation {
35+
add, copy, move, remove, replace, test;
36+
}
37+
38+
// @todo #785 Update series: add integration test for required "op" field
39+
@NotNull
40+
private Operation op;
41+
42+
// @todo #785 Update series: add integration test for non-empty "path" field
43+
@NotEmpty
44+
private String path;
45+
46+
// @todo #785 Update series: add integration test for non-empty "value" field
47+
@NotEmpty
48+
private String value;
49+
50+
}

0 commit comments

Comments
 (0)