Skip to content

Commit 4bb279b

Browse files
committed
fix(series import): price and currency aren't required anymore when there is no seller information.
This is needed in order to handle cases when we were able to parse a seller but without price, so we empty seller info and want to proceed. Fix #1256
1 parent 0da58c7 commit 4bb279b

File tree

5 files changed

+208
-0
lines changed

5 files changed

+208
-0
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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.feature.series.importing;
20+
21+
import org.apache.commons.lang3.StringUtils;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
import org.springframework.http.HttpMethod;
25+
import org.springframework.web.filter.OncePerRequestFilter;
26+
27+
import javax.servlet.FilterChain;
28+
import javax.servlet.ServletException;
29+
import javax.servlet.http.HttpServletRequest;
30+
import javax.servlet.http.HttpServletRequestWrapper;
31+
import javax.servlet.http.HttpServletResponse;
32+
import java.io.IOException;
33+
import java.util.Collections;
34+
import java.util.Enumeration;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.stream.Collectors;
38+
39+
/**
40+
* Filter that trims an empty object into null by removing corresponding fields.
41+
*
42+
* In order to prevent validation of an empty {@code ImportSeriesSalesForm},
43+
* this filter turns it into {@code null}.
44+
*
45+
* @see ImportSeriesForm
46+
* @see ImportSeriesSalesForm
47+
* @see SeriesImportController#processImportSeriesForm
48+
*/
49+
public class ImportSeriesFormTrimmerFilter extends OncePerRequestFilter {
50+
51+
private static final Logger LOG = LoggerFactory.getLogger(ImportSeriesFormTrimmerFilter.class);
52+
53+
@Override
54+
protected void doFilterInternal(
55+
HttpServletRequest request,
56+
HttpServletResponse response,
57+
FilterChain chain)
58+
throws ServletException, IOException {
59+
60+
HttpServletRequest wrappedRequest = request;
61+
try {
62+
if (HttpMethod.POST.matches(request.getMethod())) {
63+
LOG.debug("Handling request {} {}", request.getMethod(), request.getRequestURI());
64+
65+
String sellerId = request.getParameter("seriesSale.sellerId");
66+
String price = request.getParameter("seriesSale.price");
67+
String currency = request.getParameter("seriesSale.currency");
68+
69+
LOG.debug(
70+
"seriesSale: sellerId = '{}', price = '{}', currency = '{}'",
71+
sellerId,
72+
price,
73+
currency
74+
);
75+
76+
boolean atLeastOneIsSet = sellerId != null
77+
|| price != null
78+
|| currency != null;
79+
80+
boolean allEmpty = StringUtils.isEmpty(sellerId)
81+
&& StringUtils.isEmpty(price)
82+
&& StringUtils.isEmpty(currency);
83+
84+
if (atLeastOneIsSet && allEmpty) {
85+
LOG.debug("Remove empty seriesSale.* parameters from request");
86+
wrappedRequest = new ErasePrefixedParametersWrapper(request, "seriesSale.");
87+
}
88+
}
89+
90+
} finally {
91+
chain.doFilter(wrappedRequest, response);
92+
}
93+
94+
}
95+
96+
public static class ErasePrefixedParametersWrapper extends HttpServletRequestWrapper {
97+
private final String prefix;
98+
99+
/* default */ ErasePrefixedParametersWrapper(HttpServletRequest request, String prefix) {
100+
super(request);
101+
this.prefix = prefix;
102+
}
103+
104+
@Override
105+
public String getParameter(String name) {
106+
return name.startsWith(prefix) ? null : super.getParameter(name);
107+
}
108+
109+
@Override
110+
public Map<String, String[]> getParameterMap() {
111+
return super.getParameterMap()
112+
.entrySet()
113+
.stream()
114+
.filter(entry -> !entry.getKey().startsWith(prefix))
115+
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
116+
}
117+
118+
@Override
119+
public Enumeration<String> getParameterNames() {
120+
List<String> result = Collections.list(super.getParameterNames());
121+
122+
result.removeIf(name -> name.startsWith(prefix));
123+
124+
return Collections.enumeration(result);
125+
}
126+
127+
@Override
128+
public String[] getParameterValues(String name) {
129+
return name.startsWith(prefix) ? null : super.getParameterValues(name);
130+
}
131+
}
132+
133+
}

src/main/java/ru/mystamps/web/feature/series/importing/SeriesImportConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import lombok.RequiredArgsConstructor;
2121
import org.slf4j.LoggerFactory;
22+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
2223
import org.springframework.context.ApplicationEventPublisher;
2324
import org.springframework.context.annotation.Bean;
2425
import org.springframework.context.annotation.Configuration;
@@ -30,6 +31,8 @@
3031
import ru.mystamps.web.feature.series.importing.sale.SeriesSalesImportService;
3132
import ru.mystamps.web.feature.series.sale.SeriesSalesService;
3233

34+
import java.util.Collections;
35+
3336
/**
3437
* Spring configuration that is required for importing series in an application.
3538
*
@@ -59,6 +62,17 @@ public SeriesImportController seriesImportController() {
5962
);
6063
}
6164

65+
@Bean
66+
public FilterRegistrationBean importSeriesFormTrimmerFilter() {
67+
FilterRegistrationBean bean =
68+
new FilterRegistrationBean<>(new ImportSeriesFormTrimmerFilter());
69+
70+
String pattern = SeriesImportUrl.REQUEST_IMPORT_PAGE.replace("{id}", "*");
71+
bean.setUrlPatterns(Collections.singletonList(pattern));
72+
73+
return bean;
74+
}
75+
6276
}
6377

6478
@RequiredArgsConstructor

src/main/resources/liquibase/version/0.4.2.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
<include file="0.4.2/2019-11-17--drop_unique_series_from_collection.xml" relativeToChangelogFile="true" />
99
<include file="0.4.2/2019-11-27--add_collections_series_id.xml" relativeToChangelogFile="true" />
1010
<include file="0.4.2/2020-02-11--downloading_failed_import_requests.xml" relativeToChangelogFile="true" />
11+
<include file="0.4.2/2020-02-12--empty_price_import_request.xml" relativeToChangelogFile="true" />
1112

1213
</databaseChangeLog>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<databaseChangeLog
3+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
6+
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
7+
8+
<changeSet id="add-import-request-without-price" author="php-coder" context="test-data">
9+
10+
<insert tableName="series_import_requests">
11+
<column name="url" value="http://example.com/issue/1256" />
12+
<column name="status_id" valueComputed="(SELECT id FROM series_import_request_statuses WHERE name = 'ParsingSucceeded')" />
13+
<column name="requested_at" valueComputed="${NOW}" />
14+
<column name="requested_by" valueComputed="(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1)" />
15+
<column name="updated_at" valueComputed="${NOW}" />
16+
</insert>
17+
18+
<insert tableName="series_import_parsed_data">
19+
<column name="request_id" valueComputed="(SELECT id FROM series_import_requests WHERE url = 'http://example.com/issue/1256')" />
20+
<column name="category_id" valueComputed="(SELECT id FROM categories WHERE slug = 'prehistoric-animals')" />
21+
<column name="image_url" value="http://127.0.0.1:8080/image/1" />
22+
<column name="quantity" valueNumeric="3" />
23+
<column name="created_at" valueComputed="${NOW}" />
24+
<column name="updated_at" valueComputed="${NOW}" />
25+
</insert>
26+
27+
<insert tableName="series_sales_import_parsed_data">
28+
<column name="request_id" valueComputed="(SELECT id FROM series_import_requests WHERE url = 'http://example.com/issue/1256')" />
29+
<column name="seller_name" value="Issue 1256" />
30+
<column name="seller_url" value="http://example.com/issue/1256" />
31+
<column name="created_at" valueComputed="${NOW}" />
32+
<column name="updated_at" valueComputed="${NOW}" />
33+
</insert>
34+
35+
</changeSet>
36+
37+
</databaseChangeLog>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
*** Settings ***
2+
Documentation Validation of a series import
3+
Library SeleniumLibrary
4+
Resource ../../auth.steps.robot
5+
Suite Setup Before Test Suite
6+
Suite Teardown Close Browser
7+
Force Tags series import-series validation
8+
9+
*** Test Cases ***
10+
Price and currency should be optional when seller information isn't provided
11+
Go To ${SITE_URL}/series/import/request/2
12+
Textfield Value Should Be id:seller-name Issue 1256
13+
Input Text id:seller-name ${EMPTY}
14+
Textfield Value Should Be id:seller-url http://example.com/issue/1256
15+
Input Text id:seller-url ${EMPTY}
16+
Submit Form id:create-series-form
17+
Element Text Should Be id:category_name Prehistoric animals
18+
19+
*** Keywords ***
20+
Before Test Suite
21+
Open Browser ${SITE_URL}/account/auth ${BROWSER}
22+
Register Keyword To Run On Failure Log Source
23+
Log In As login=admin password=test

0 commit comments

Comments
 (0)