1 package org.imageconverter.application;
2
3 import static java.nio.charset.StandardCharsets.UTF_8;
4 import static java.text.MessageFormat.format;
5 import static org.apache.commons.csv.QuoteMode.NONE;
6 import static org.apache.commons.lang3.StringUtils.EMPTY;
7 import static org.apache.commons.lang3.StringUtils.substringBetween;
8 import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage;
9
10 import java.io.ByteArrayOutputStream;
11 import java.io.IOException;
12 import java.io.OutputStreamWriter;
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import javax.validation.Valid;
17 import javax.validation.constraints.NotEmpty;
18 import javax.validation.constraints.NotNull;
19
20 import org.apache.commons.csv.CSVFormat;
21 import org.apache.commons.csv.CSVPrinter;
22 import org.imageconverter.domain.conversion.ImageConversion;
23 import org.imageconverter.domain.conversion.ImageConversionRepository;
24 import org.imageconverter.infra.exception.CsvFileGenerationException;
25 import org.imageconverter.infra.exception.CsvFileNoDataException;
26 import org.imageconverter.infra.exception.ElementAlreadyExistsException;
27 import org.imageconverter.infra.exception.ElementInvalidException;
28 import org.imageconverter.infra.exception.ElementNotFoundException;
29 import org.imageconverter.infra.exception.ElementWithIdNotFoundException;
30 import org.imageconverter.util.controllers.imageconverter.ImageConversionResponse;
31 import org.imageconverter.util.controllers.imageconverter.ImageConverterRequestInterface;
32 import org.imageconverter.util.logging.Loggable;
33 import org.springframework.dao.InvalidDataAccessApiUsageException;
34 import org.springframework.data.domain.Page;
35 import org.springframework.data.domain.Pageable;
36 import org.springframework.data.jpa.domain.Specification;
37 import org.springframework.stereotype.Service;
38 import org.springframework.transaction.annotation.Transactional;
39
40
41
42
43
44
45 @Service
46 @Loggable
47 public class ImageConversionService {
48
49 public static final String [] HEADER_FILE = { "id", "file-name", "result" };
50
51 private final ImageConversionRepository repository;
52
53
54
55
56
57
58 ImageConversionService(final ImageConversionRepository newRepository) {
59 super();
60 this.repository = newRepository;
61 }
62
63
64
65
66
67
68
69
70
71 @Transactional
72 public ImageConversionResponse convert(@NotNull @Valid final ImageConverterRequestInterface request) {
73
74 final var imageConversionNew = new ImageConversion.Builder()
75 .with(request)
76 .build();
77
78 final var fileName = imageConversionNew.getFileName();
79
80 repository.findByFileName(fileName).ifPresent(imageConversionResult -> {
81
82 throw new ElementAlreadyExistsException(
83 ImageConversion.class,
84 new Object[] { format("fileName ''{0}''", fileName) }
85 );
86 });
87
88 final var imageConversion = repository.save(imageConversionNew);
89
90 return new ImageConversionResponse(imageConversion.getId(), imageConversion.getFileName(), imageConversion.getText());
91 }
92
93
94
95
96
97
98
99
100
101 @Transactional
102 public List<ImageConversionResponse> convert(@NotNull @NotEmpty @Valid final List<ImageConverterRequestInterface> requests) {
103
104 final var imageList = new ArrayList<ImageConversion>();
105
106 for (final var request : requests) {
107 final var imageConversionNew = new ImageConversion.Builder()
108 .with(request)
109 .build();
110
111 repository.findByFileName(imageConversionNew.getFileName()).ifPresent(imageConversionResult -> {
112
113 throw new ElementAlreadyExistsException(
114 ImageConversion.class,
115 createParams(imageConversionResult)
116 );
117 });
118
119 imageList.add(imageConversionNew);
120 }
121
122 final var imagesConversion = repository.saveAll(imageList);
123
124 return imagesConversion
125 .stream()
126 .map(img -> new ImageConversionResponse(img.getId(), img.getFileName(), img.getText()))
127 .toList();
128
129 }
130
131 private Object[] createParams(final ImageConversion imageConversionResult) {
132 return new Object[] { format("fileName '{0}', id '{1}'", imageConversionResult.getFileName(), imageConversionResult.getId()) };
133 }
134
135
136
137
138
139
140
141 @Transactional
142 public void deleteImageConversion(@NotNull final Long id) {
143
144 final var imageConversion = repository.findById(id)
145 .orElseThrow(() -> new ElementWithIdNotFoundException(ImageConversion.class, id));
146
147 repository.delete(imageConversion);
148
149 repository.flush();
150 }
151
152
153
154
155
156
157 @Transactional(readOnly = true)
158 public List<ImageConversionResponse> findAll() {
159
160 return repository.findAll()
161 .stream()
162 .map(icr -> new ImageConversionResponse(icr.getId(), icr.getFileName(), icr.getText()))
163 .toList();
164 }
165
166
167
168
169
170
171
172
173 @Transactional(readOnly = true)
174 public ImageConversionResponse findById(@NotNull final Long id) {
175
176 final var imageConversion = repository.findById(id)
177 .orElseThrow(() -> new ElementWithIdNotFoundException(ImageConversion.class, id));
178
179 return new ImageConversionResponse(imageConversion.getId(), imageConversion.getFileName(), imageConversion.getText());
180 }
181
182
183
184
185
186
187
188
189
190 @Transactional(readOnly = true)
191 public Page<ImageConversionResponse> findBySpecification(final Specification<ImageConversion> spec, final Pageable page) {
192
193 try {
194
195 return repository.findAll(spec, page)
196 .map(icr -> new ImageConversionResponse(icr.getId(), icr.getFileName(), icr.getText()));
197
198 } catch (final InvalidDataAccessApiUsageException ex) {
199
200 final var msgException = getRootCauseMessage(ex);
201
202 final Object[] params = { substringBetween(msgException, "[", "]"), ImageConversion.class.getSimpleName() };
203
204 throw new ElementInvalidException("{exception.ElementInvalidDataSpecification}", ex, params);
205 }
206 }
207
208
209
210
211
212
213
214 @Transactional(readOnly = true)
215 public byte[] findBySpecificationToCsv(final Specification<ImageConversion> spec) {
216
217 final var pageable = Pageable.ofSize(1000);
218
219
220 var page = repository.findAll(spec, pageable);
221
222 if (page.isEmpty()) {
223 throw new CsvFileNoDataException("{exception.csvEmpyResult}", spec);
224 }
225
226 final var csvFormat = CSVFormat.Builder.create()
227 .setHeader(HEADER_FILE)
228 .setAllowMissingColumnNames(true)
229 .setNullString(EMPTY)
230 .setQuoteMode(NONE)
231 .setDelimiter(';')
232 .setEscape(' ')
233 .build();
234
235 try (final var byteArrayOutputStream = new ByteArrayOutputStream();
236 final var outStreamWriter = new OutputStreamWriter(byteArrayOutputStream, UTF_8);
237 final var csvPrinter = new CSVPrinter(outStreamWriter, csvFormat);) {
238
239 do {
240
241 var imageConversionList = page.getContent();
242
243 for (final var imageConversion : imageConversionList) {
244 csvPrinter.printRecord(imageConversion.getId().toString(), imageConversion.getFileName(), imageConversion.getText());
245 }
246
247 csvPrinter.flush();
248
249 page = repository.findAll(spec, page.nextOrLastPageable());
250 imageConversionList = page.getContent();
251
252 } while (page.hasNext());
253
254 return byteArrayOutputStream.toByteArray();
255
256 } catch (final IOException ex) {
257 throw new CsvFileGenerationException("{exception.csvUnexpectedError}", ex);
258 }
259 }
260 }