Skip to content

Commit 8319fba

Browse files
CCheminmehdimaaref7
authored andcommitted
[BUG] 🐛 Export should return less than maxSizeOutput (#506)
Signed-off-by: CChemin <cecile.chemin@insee.fr>
1 parent 2b5aec8 commit 8319fba

File tree

5 files changed

+167
-5
lines changed

5 files changed

+167
-5
lines changed

docs/configuration.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [Password configuration](#password-configuration)
1111
- [WebHooks configuration](#webhooks-configuration)
1212
- [Spring actuator configuration](#spring-actuator-configuration)
13+
- [Export configuration](#export-configuration)
1314
- [Other info configuration](#other-info-configuration)
1415
- [Old endpoints configuration](#old-endpoints-configuration)
1516

@@ -186,6 +187,15 @@ fr.insee.sugoi.security.monitor-user-password=monitor
186187

187188
This user only has rights on `/actuator` endpoints.
188189

190+
### Export configuration
191+
192+
Configure the /export.csv endpoint with :
193+
194+
| Properties | Description | Default value |
195+
| ---------------------------------------------| :---------: | ------------: |
196+
| fr.insee.sugoi.export.maxSizeOutput | The export function will stop when the number of entities reach this limit | 10000 |
197+
| fr.insee.sugoi.export.pagesize | The maximum size of pages to request on the store provider when exporting. With a pagesize of 10 the store will be requested for pages 10 by 10 before aggregating the results | 2000 |
198+
189199
### Other info configuration
190200

191201
You can add all other spring properties for example :

sugoi-api-ldap-store-provider/src/test/java/fr/insee/sugoi/ldap/LdapReaderStoreTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ public void testSearchAllUsers() {
197197
users.stream().anyMatch(user -> user.getUsername().equals("nogroup")));
198198
}
199199

200+
@Test
201+
@DisplayName(
202+
"When the size of the pagerequest is less than the total result, the pageresult should have isHasMoreResult")
203+
public void testAllUsersHasMoreResult() {
204+
PageableResult pageableResult = new PageableResult();
205+
pageableResult.setSize(1);
206+
PageResult<User> result = ldapReaderStore.searchUsers(new User(), pageableResult, "AND");
207+
assertThat("Should get one result", 1, is(result.getResults().size()));
208+
assertThat("Page size should still be 1", 1, is(result.getResults().size()));
209+
assertThat("Should be tagged with more results", result.isHasMoreResult());
210+
}
211+
200212
@Test
201213
public void testSearchUserWithMatchingMail() {
202214
PageableResult pageableResult = new PageableResult();

sugoi-api-rest-services/src/main/java/fr/insee/sugoi/services/controller/ExportController.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
3535
import io.swagger.v3.oas.annotations.tags.Tag;
3636
import java.io.IOException;
37+
import java.lang.reflect.Field;
3738
import java.util.ArrayList;
39+
import java.util.Arrays;
3840
import java.util.List;
3941
import java.util.stream.Collectors;
4042
import javax.servlet.http.HttpServletResponse;
@@ -193,6 +195,7 @@ private void getExportUsersWithStorages(
193195
String realm,
194196
SearchType typeRecherche,
195197
CSVPrinter csvPrinter) {
198+
int allResultsSize = 0;
196199
try {
197200

198201
// set the page to maintain the search request pagination
@@ -230,7 +233,10 @@ private void getExportUsersWithStorages(
230233
}
231234

232235
if (foundUsers.isHasMoreResult()) {
233-
csvPrinter.close();
236+
if (allResultsSize + pageSize >= maxSizeOutput) {
237+
break;
238+
}
239+
allResultsSize += foundUsers.getResults().size();
234240
pageable = new PageableResult(pageSize, 0, foundUsers.getSearchToken());
235241
} else {
236242
break;

sugoi-api-rest-services/src/test/java/fr/insee/sugoi/services/controller/ExportControllerTest.java

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,36 @@
1414
package fr.insee.sugoi.services.controller;
1515

1616
import static org.hamcrest.MatcherAssert.assertThat;
17+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
1718
import static org.hamcrest.Matchers.is;
19+
import static org.hamcrest.Matchers.lessThan;
1820
import static org.mockito.ArgumentMatchers.any;
1921
import static org.mockito.ArgumentMatchers.eq;
2022

23+
import com.fasterxml.jackson.databind.ObjectMapper;
2124
import fr.insee.sugoi.commons.services.controller.technics.SugoiAdviceController;
2225
import fr.insee.sugoi.core.service.ConfigService;
2326
import fr.insee.sugoi.core.service.UserService;
24-
import fr.insee.sugoi.model.*;
27+
import fr.insee.sugoi.model.Group;
28+
import fr.insee.sugoi.model.Habilitation;
29+
import fr.insee.sugoi.model.Organization;
30+
import fr.insee.sugoi.model.PostalAddress;
31+
import fr.insee.sugoi.model.Realm;
32+
import fr.insee.sugoi.model.User;
33+
import fr.insee.sugoi.model.UserStorage;
2534
import fr.insee.sugoi.model.paging.PageResult;
35+
import fr.insee.sugoi.model.paging.PageableResult;
36+
import fr.insee.sugoi.model.paging.SearchType;
2637
import fr.insee.sugoi.model.technics.ModelType;
2738
import fr.insee.sugoi.model.technics.StoreMapping;
39+
import java.io.UnsupportedEncodingException;
40+
import java.util.ArrayList;
41+
import java.util.Arrays;
2842
import java.util.List;
2943
import java.util.Map;
3044
import java.util.stream.Collectors;
3145
import org.junit.jupiter.api.BeforeEach;
46+
import org.junit.jupiter.api.DisplayName;
3247
import org.junit.jupiter.api.Test;
3348
import org.mockito.Mockito;
3449
import org.springframework.beans.factory.annotation.Autowired;
@@ -56,6 +71,8 @@ public class ExportControllerTest {
5671

5772
@MockBean private ConfigService configService;
5873

74+
ObjectMapper objectMapper = new ObjectMapper();
75+
5976
private Realm reamlOneUS;
6077
private Realm realmTwoUS;
6178

@@ -148,6 +165,7 @@ public void headerTestMultipleUserStorages() throws Exception {
148165
@Test
149166
@WithMockUser
150167
public void bodyTestString() throws Exception {
168+
String response = getOneUsResponse().getContentAsString();
151169
List<String> lines =
152170
getOneUsResponse().getContentAsString().lines().collect(Collectors.toList());
153171
assertThat(
@@ -236,6 +254,120 @@ private String retrieveAttribute(String header, String userLine, String attribut
236254
return s[attributePosition];
237255
}
238256

257+
@Test
258+
@WithMockUser
259+
@DisplayName("When there are only two users in userstorage all users should be returned")
260+
public void getAllUsersWhenTotalIs2() throws Exception {
261+
Mockito.when(
262+
userService.findByProperties(
263+
Mockito.eq("realmOneUS"),
264+
Mockito.eq("storage"),
265+
Mockito.isA(User.class),
266+
Mockito.isA(PageableResult.class),
267+
Mockito.isA(SearchType.class)))
268+
.thenReturn(createPageResult("toto", "tutu", false));
269+
270+
RequestBuilder requestBuilder =
271+
MockMvcRequestBuilders.get("/realms/realmOneUS/storages/storage/export/users/export.csv");
272+
MockHttpServletResponse response = mockMvc.perform(requestBuilder).andReturn().getResponse();
273+
274+
assertThat(
275+
"HttpServletResponse type should be a csv",
276+
"text/csv;charset=UTF-8",
277+
is(response.getContentType()));
278+
List<String> usernames = convertCsvToUsernames(response.getContentAsString());
279+
assertThat("Should return 2 users", 2, is(usernames.size()));
280+
assertThat(
281+
"Should have a user tutu",
282+
usernames.stream().anyMatch(username -> username.equals("tutu")));
283+
}
284+
285+
@Test
286+
@WithMockUser
287+
@DisplayName(
288+
"When there are more users than the pagesize, "
289+
+ "but less than the maximum value then "
290+
+ "users should be requested several times and all users should be returned")
291+
public void getTotalUsersWhenMoreThanPageSize() throws Exception {
292+
293+
Mockito.when(
294+
userService.findByProperties(
295+
Mockito.eq("realmOneUS"),
296+
Mockito.eq("storage"),
297+
Mockito.isA(User.class),
298+
Mockito.isA(PageableResult.class),
299+
Mockito.isA(SearchType.class)))
300+
.thenReturn(createPageResult("toto", "tata", true))
301+
.thenReturn(createPageResult("tutu", null, false));
302+
303+
RequestBuilder requestBuilder =
304+
MockMvcRequestBuilders.get("/realms/realmOneUS/storages/storage/export/users/export.csv");
305+
List<String> usernames =
306+
convertCsvToUsernames(
307+
mockMvc.perform(requestBuilder).andReturn().getResponse().getContentAsString());
308+
309+
assertThat("Should return 3 users even if page size is 2", 3, is(usernames.size()));
310+
}
311+
312+
@Test
313+
@WithMockUser
314+
@DisplayName(
315+
"When there are more users than the maximum a user can fetch, max exception should be returned")
316+
public void exceptionIsReturnedWhenMoreThanMax() throws Exception {
317+
318+
Mockito.when(
319+
userService.findByProperties(
320+
Mockito.eq("realmOneUS"),
321+
Mockito.eq("storage"),
322+
Mockito.isA(User.class),
323+
Mockito.isA(PageableResult.class),
324+
Mockito.isA(SearchType.class)))
325+
.thenReturn(createPageResult("toto", "tata", true))
326+
.thenReturn(createPageResult("tutu", "toto2", true))
327+
.thenReturn(createPageResult("toto4", "toto4", true))
328+
.thenReturn(createPageResult("toto5", "toto6", true))
329+
.thenReturn(createPageResult("toto7", "toto8", true))
330+
.thenReturn(createPageResult("toto9", "toto10", true))
331+
.thenReturn(createPageResult("toto11", "toto12", false));
332+
333+
RequestBuilder requestBuilder =
334+
MockMvcRequestBuilders.get("/realms/realmOneUS/storages/storage/export/users/export.csv");
335+
List<String> usernames =
336+
convertCsvToUsernames(
337+
mockMvc.perform(requestBuilder).andReturn().getResponse().getContentAsString());
338+
339+
assertThat("Should return a value under the max", 10, greaterThanOrEqualTo(usernames.size()));
340+
assertThat(
341+
"Should return a value above the max plus the pagesize",
342+
10 - 2,
343+
lessThan(usernames.size()));
344+
}
345+
346+
private List<String> convertCsvToUsernames(String usersCsv) throws UnsupportedEncodingException {
347+
List<String> usernames =
348+
Arrays.stream(usersCsv.split("\n"))
349+
.skip(1)
350+
.map(line -> line.split(",")[0])
351+
.collect(Collectors.toList());
352+
return usernames;
353+
}
354+
355+
private PageResult<User> createPageResult(
356+
String firstName, String secondName, boolean hasMoreResult) {
357+
PageResult<User> usersResult = new PageResult<>();
358+
usersResult.setPageSize(2);
359+
usersResult.setHasMoreResult(hasMoreResult);
360+
List<User> users = new ArrayList<>();
361+
if (firstName != null) {
362+
users.add(new User(firstName));
363+
}
364+
if (secondName != null) {
365+
users.add(new User(secondName));
366+
}
367+
usersResult.setResults(users);
368+
return usersResult;
369+
}
370+
239371
private MockHttpServletResponse getOneUsResponse() throws Exception {
240372
return getResponse("/realms/realmOneUS/storages/storage/export/users/export.csv");
241373
}
@@ -247,6 +379,7 @@ private MockHttpServletResponse getTwoUsResponse() throws Exception {
247379
private MockHttpServletResponse getResponse(String url) throws Exception {
248380
RequestBuilder requestBuilder =
249381
MockMvcRequestBuilders.get(url).accept(MediaType.APPLICATION_JSON);
250-
return mockMvc.perform(requestBuilder).andReturn().getResponse();
382+
MockHttpServletResponse response = mockMvc.perform(requestBuilder).andReturn().getResponse();
383+
return response;
251384
}
252385
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
2-
logging.level.fr.insee.sugoi=debug
1+
logging.level.fr.insee.sugoi=debug
2+
fr.insee.sugoi.export.maxSizeOutput=10
3+
fr.insee.sugoi.export.pagesize=2

0 commit comments

Comments
 (0)