View Javadoc
1   package org.imageconverter.controller;
2   
3   import static java.util.stream.Collectors.joining;
4   import static org.apache.commons.lang3.StringUtils.EMPTY;
5   import static org.imageconverter.util.controllers.imageconverter.ImageConverterConst.REST_URL;
6   import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
7   import static org.springframework.http.HttpStatus.CREATED;
8   import static org.springframework.http.HttpStatus.NO_CONTENT;
9   import static org.springframework.http.HttpStatus.OK;
10  import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
11  import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
12  
13  import java.io.ByteArrayInputStream;
14  import java.io.IOException;
15  import java.util.ArrayList;
16  import java.util.List;
17  import java.util.Optional;
18  
19  import javax.servlet.http.HttpServletRequest;
20  import javax.servlet.http.HttpServletResponse;
21  
22  import org.imageconverter.application.ImageConversionService;
23  import org.imageconverter.domain.conversion.ExecutionType;
24  import org.imageconverter.domain.conversion.ImageConversion;
25  import org.imageconverter.util.controllers.imageconverter.ImageConversionPostResponse;
26  import org.imageconverter.util.controllers.imageconverter.ImageConversionResponse;
27  import org.imageconverter.util.controllers.imageconverter.ImageConverterRequest;
28  import org.imageconverter.util.controllers.imageconverter.ImageConverterRequestArea;
29  import org.imageconverter.util.controllers.imageconverter.ImageConverterRequestInterface;
30  import org.imageconverter.util.logging.Loggable;
31  import org.imageconverter.util.openapi.imageconverter.ImageConverterRestGetByIdOpenApi;
32  import org.imageconverter.util.openapi.imageconverter.ImageConverterRestGetOpenApi;
33  import org.imageconverter.util.openapi.imageconverter.ImageConverterRestPostAreaOpenApi;
34  import org.imageconverter.util.openapi.imageconverter.ImageConverterRestPostOpenApi;
35  import org.imageconverter.util.openapi.imagetype.ImageTypeRestDeleteOpenApi;
36  import org.springdoc.core.converters.models.PageableAsQueryParam;
37  import org.springframework.context.annotation.Description;
38  import org.springframework.core.io.InputStreamResource;
39  import org.springframework.data.domain.Page;
40  import org.springframework.data.domain.Pageable;
41  import org.springframework.data.jpa.domain.Specification;
42  import org.springframework.data.web.PageableDefault;
43  import org.springframework.http.MediaType;
44  import org.springframework.http.ResponseEntity;
45  import org.springframework.web.bind.annotation.DeleteMapping;
46  import org.springframework.web.bind.annotation.GetMapping;
47  import org.springframework.web.bind.annotation.PathVariable;
48  import org.springframework.web.bind.annotation.PostMapping;
49  import org.springframework.web.bind.annotation.RequestMapping;
50  import org.springframework.web.bind.annotation.RequestParam;
51  import org.springframework.web.bind.annotation.RequestPart;
52  import org.springframework.web.bind.annotation.ResponseStatus;
53  import org.springframework.web.bind.annotation.RestController;
54  import org.springframework.web.multipart.MultipartFile;
55  
56  import com.turkraft.springfilter.boot.Filter;
57  
58  import io.swagger.v3.oas.annotations.Parameter;
59  import io.swagger.v3.oas.annotations.media.Content;
60  import io.swagger.v3.oas.annotations.security.SecurityRequirement;
61  import io.swagger.v3.oas.annotations.tags.Tag;
62  
63  /**
64   * Image converter http rest.
65   * 
66   * @author Fernando Romulo da Silva
67   */
68  @SecurityRequirement(name = "BASIC")
69  @Tag( //
70  		name = "Image Convert", //
71  		description = """
72  				Image Convert API - If something went wrong, please put 'trace' (for all Http methods)
73  				at the end of the call to receive the stackStrace.
74  				Ex: http://127.0.0.1:8080/image-converter/rest/images/convert?trace=true
75  				     """ //
76  )
77  //
78  @Loggable
79  @RestController
80  @Description("Controller for image converstion API")
81  @RequestMapping(REST_URL)
82  public class ImageConversionRestController {
83  
84      private final ImageConversionService imageConversionService;
85  
86      /**
87       * Default constructor.
88       * 
89       * @param imageConversionService The image convert service
90       */
91      ImageConversionRestController(final ImageConversionService imageConversionService) {
92  	super();
93  	this.imageConversionService = imageConversionService;
94      }
95  
96      /**
97       * Get a conversion already done.
98       * 
99       * @param id The image conversion's id
100      * @return A {@link ImageConversionResponse} object
101      * @exception ElementNotFoundException if a element with id not found
102      */
103     @ImageConverterRestGetByIdOpenApi
104     //
105     @ResponseStatus(OK)
106     @GetMapping(value = "/{id:[\\d]*}", produces = APPLICATION_JSON_VALUE)
107     public ImageConversionResponse getById( //
108 		    @Parameter(name = "id", description = "The image conversion id's", required = true, example = "3") //
109 		    @PathVariable(name = "id", required = true) //
110 		    final Long id) {
111 
112 	return imageConversionService.findById(id);
113     }
114 
115     /**
116      * Get conversions by filter.
117      * 
118      * @param filter A object {@link Specification} that specific the filter the search
119      * @param page   A object {@link Pageable} that page the result
120      * @return A {@link List} or a empty list
121      */
122     @PageableAsQueryParam
123     @ImageConverterRestGetOpenApi
124     //
125     @ResponseStatus(OK)
126     @GetMapping(produces = APPLICATION_JSON_VALUE)
127     public Page<ImageConversionResponse> getByFilter( //
128 		    @Parameter(name = "filter", description = "Search's filter", required = true, example = "?filter=fileName:'image.png'") //
129 		    @Filter //
130 		    final Specification<ImageConversion> filter, // 
131 		    //
132 		    @PageableDefault(value = 10, page = 0) //
133 		    final Pageable page) {
134 
135 	return imageConversionService.findBySpecification(filter, page);
136     }
137 
138     /**
139      * Convert a image file on text.
140      * 
141      * @param file The image to convert
142      * @return A {@link ImageConversionResponse} object with response.
143      */
144     @ImageConverterRestPostOpenApi
145     //
146     @ResponseStatus(CREATED)
147     @PostMapping(consumes = { MULTIPART_FORM_DATA_VALUE }, produces = APPLICATION_JSON_VALUE)
148     public ImageConversionPostResponse convert( //
149 		    @Parameter(description = "The Image to be uploaded", content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE), required = true, example = "image.bmp") //
150 		    @RequestPart(name = "file", required = true) //
151 		    final MultipartFile file,
152 		    //
153 		    final HttpServletRequest request, final HttpServletResponse response) {
154 
155 	final var bytes = extractBytes(file);
156 
157 	final var executionType = extractExecutionType(request);
158 
159 	final var result = imageConversionService.convert(new ImageConverterRequest(file.getOriginalFilename(), bytes, executionType));
160 
161 	response.addHeader("Location", REST_URL + "/" + result.id());
162 
163 	return new ImageConversionPostResponse(result.text());
164     }
165 
166     /**
167      * Convert a image file with area on text.
168      * 
169      * @param file   The image to convert
170      * @param xAxis  The image's x coordinate
171      * @param yAxis  The image's y coordinate
172      * @param width  The image's width in pixels
173      * @param height The image's height in pixels
174      * @return A {@link ImageConversionResponse} object with response.
175      */
176     @ImageConverterRestPostAreaOpenApi
177     //
178     @ResponseStatus(CREATED)
179     @PostMapping(value = "/area", consumes = { MULTIPART_FORM_DATA_VALUE }, produces = APPLICATION_JSON_VALUE)
180     public ImageConversionPostResponse convertWithArea( //
181 		    @Parameter(description = "The Image to be uploaded", content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE), required = true, example = "image.bmp") //
182 		    @RequestPart(name = "file", required = true) //
183 		    final MultipartFile file, //
184 		    //
185 		    @Parameter(description = "The vertical position", required = true, example = "3") //
186 		    @RequestParam(required = true) //
187 		    final Integer xAxis, //
188 		    //
189 		    @Parameter(description = "The horizontal position", required = true, example = "4") //
190 		    @RequestParam(required = true) //
191 		    final Integer yAxis, //
192 		    //
193 		    @Parameter(description = "The width size's area", required = true, example = "56") //
194 		    @RequestParam(required = true) //
195 		    final Integer width, //
196 		    //
197 		    @Parameter(description = "The height's size area ", required = true, example = "345") //
198 		    @RequestParam(required = true) //
199 		    final Integer height,
200 		    //
201 		    final HttpServletRequest request, final HttpServletResponse response) {
202 
203 	final var executionType = extractExecutionType(request);
204 
205 	final byte[] bytes = extractBytes(file);
206 
207 	final var result = imageConversionService.convert(new ImageConverterRequestArea(file.getOriginalFilename(), bytes, executionType, xAxis, yAxis, width, height));
208 
209 	response.addHeader("Location", REST_URL + "/" + result.id());
210 
211 	return new ImageConversionPostResponse(result.text());
212     }
213 
214     /**
215      * Convert multiple images file on text.
216      * 
217      * @param files The images' collection to convert
218      * @return A list of {@link ImageConversionResponse} with each response.
219      */
220     @ImageConverterRestPostOpenApi
221     //
222     @ResponseStatus(CREATED)
223     @PostMapping(value = "/multiple", consumes = { MULTIPART_FORM_DATA_VALUE }, produces = APPLICATION_JSON_VALUE)
224     public List<ImageConversionPostResponse> convert( //
225 		    @Parameter(description = "The Images to be uploaded", content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE), required = true, example = "image1.bmp, image2.png") //
226 		    @RequestPart(name = "files", required = true) //
227 		    final MultipartFile[] files, //
228 		    //
229 		    final HttpServletRequest request, //
230 		    final HttpServletResponse response) {
231 
232 	final var executionType = extractExecutionType(request);
233 
234 	final var listRequest = new ArrayList<ImageConverterRequestInterface>();
235 
236 	for (final var file : files) {
237 	    listRequest.add(new ImageConverterRequest(file.getOriginalFilename(), extractBytes(file), executionType));
238 	}
239 
240 	final var result = imageConversionService.convert(listRequest);
241 
242 	response.addHeader("Location", result.stream().map(rst -> REST_URL + "/" + rst.id()).collect(joining(";")));
243 
244 	return result.stream().map(rst -> new ImageConversionPostResponse(rst.text())).toList();
245     }
246 
247     /**
248      * Delete a image conversion.
249      * 
250      * @param id The conversion id
251      */
252     @ImageTypeRestDeleteOpenApi
253     //
254     @ResponseStatus(NO_CONTENT)
255     @DeleteMapping("/{id:[\\d]*}")
256     public void delete( //
257 		    //
258 		    @Parameter(description = "The conversion id's", example = "1000") //
259 		    @PathVariable(name = "id", required = true) //
260 		    final Long id) {
261 
262 	imageConversionService.deleteImageConversion(id);
263     }
264     
265     
266     /**
267      * Create a CSV file using filter.
268      * 
269      * @param filter A object {@link Specification} that specific the filter the search
270      * @return A Csv file
271      */
272     @ResponseStatus(OK)
273     @GetMapping(value = "/export", produces = "txt/csv")
274     public ResponseEntity<InputStreamResource> downloadImageConversionCsv( //		    
275 		    @Parameter(name = "filter", description = "Search's filter", required = true, example = "?filter=fileName:'image.png'") //
276 		    @Filter //
277 		    final Specification<ImageConversion> filter ) {
278 	
279 	final var bytes = imageConversionService.findBySpecificationToCsv(filter);
280 	
281 	final var body = new InputStreamResource(new ByteArrayInputStream(bytes));
282 	
283 	return ResponseEntity.ok()
284 			.header(CONTENT_DISPOSITION, "attachment; filename=convertions.csv")
285 			.contentType(MediaType.parseMediaType("txt/csv"))
286 			.body(body);
287     }
288     
289 
290     private byte[] extractBytes(final MultipartFile file) {
291 	byte[] bytes;
292 	try {
293 	    bytes = file.getBytes();
294 	} catch (final IOException e) {
295 	    bytes = new byte[0];
296 	}
297 	return bytes;
298     }
299 
300     private ExecutionType extractExecutionType(final HttpServletRequest request) {
301 	final var executionTypeHeader = Optional.ofNullable(request.getHeader("Execution-Type")).orElse(EMPTY);
302 
303 	return ExecutionType.from(executionTypeHeader);
304     }
305 }