1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.audit.service.controller;
18
19 import java.lang.reflect.Type;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Optional;
23 import javax.annotation.PostConstruct;
24
25 import io.swagger.annotations.ApiImplicitParam;
26 import io.swagger.annotations.ApiImplicitParams;
27 import io.swagger.annotations.ApiOperation;
28 import io.swagger.annotations.ApiParam;
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.Logger;
31 import org.apache.logging.log4j.audit.service.catalog.AuditManager;
32 import org.apache.logging.log4j.catalog.api.Attribute;
33 import org.apache.logging.log4j.catalog.api.Category;
34 import org.apache.logging.log4j.catalog.api.Event;
35 import org.apache.logging.log4j.catalog.api.Product;
36 import org.apache.logging.log4j.catalog.api.Versions;
37 import org.apache.logging.log4j.catalog.jpa.converter.AttributeConverter;
38 import org.apache.logging.log4j.catalog.jpa.converter.AttributeModelConverter;
39 import org.apache.logging.log4j.catalog.jpa.converter.CategoryConverter;
40 import org.apache.logging.log4j.catalog.jpa.converter.CategoryModelConverter;
41 import org.apache.logging.log4j.catalog.jpa.converter.EventConverter;
42 import org.apache.logging.log4j.catalog.jpa.converter.EventModelConverter;
43 import org.apache.logging.log4j.catalog.jpa.converter.ProductConverter;
44 import org.apache.logging.log4j.catalog.jpa.converter.ProductModelConverter;
45 import org.apache.logging.log4j.catalog.jpa.model.AttributeModel;
46 import org.apache.logging.log4j.catalog.jpa.model.CategoryModel;
47 import org.apache.logging.log4j.catalog.jpa.model.EventModel;
48 import org.apache.logging.log4j.catalog.jpa.model.ProductModel;
49 import org.apache.logging.log4j.catalog.jpa.service.AttributeService;
50 import org.apache.logging.log4j.catalog.jpa.service.CategoryService;
51 import org.apache.logging.log4j.catalog.jpa.service.EventService;
52 import org.apache.logging.log4j.catalog.jpa.service.ProductService;
53 import org.modelmapper.ModelMapper;
54 import org.modelmapper.TypeToken;
55 import org.springframework.beans.factory.annotation.Autowired;
56 import org.springframework.http.HttpStatus;
57 import org.springframework.http.ResponseEntity;
58 import org.springframework.web.bind.annotation.DeleteMapping;
59 import org.springframework.web.bind.annotation.GetMapping;
60 import org.springframework.web.bind.annotation.PathVariable;
61 import org.springframework.web.bind.annotation.PostMapping;
62 import org.springframework.web.bind.annotation.PutMapping;
63 import org.springframework.web.bind.annotation.RequestBody;
64 import org.springframework.web.bind.annotation.RequestMapping;
65 import org.springframework.web.bind.annotation.RequestParam;
66 import org.springframework.web.bind.annotation.RestController;
67
68 import static org.apache.logging.log4j.catalog.api.constant.Constants.DEFAULT_CATALOG;
69
70 @RestController
71 @RequestMapping(value = "/catalog")
72 public class CatalogController {
73
74 private static final Logger LOGGER = LogManager.getLogger(CatalogController.class);
75
76 private ModelMapper attributeModelMapper = new ModelMapper();
77 private ModelMapper eventModelMapper = new ModelMapper();
78 private ModelMapper productModelMapper = new ModelMapper();
79 private ModelMapper categoryModelMapper = new ModelMapper();
80
81 @Autowired
82 private AttributeService attributeService;
83
84 @Autowired
85 private AttributeModelConverter attributeModelConverter;
86
87 @Autowired
88 private AttributeConverter attributeConverter;
89
90 @Autowired
91 private EventService eventService;
92
93 @Autowired
94 private EventModelConverter eventModelConverter;
95
96 @Autowired
97 private EventConverter eventConverter;
98
99 @Autowired
100 private ProductService productService;
101
102 @Autowired
103 private ProductModelConverter productModelConverter;
104
105 @Autowired
106 private ProductConverter productConverter;
107
108 @Autowired
109 private CategoryService categoryService;
110
111 @Autowired
112 private AuditManager auditManager;
113
114 @Autowired
115 private CategoryModelConverter categoryModelConverter;
116
117 @Autowired
118 private CategoryConverter categoryConverter;
119
120 @PostConstruct
121 public void init() {
122 attributeModelMapper.addConverter(attributeModelConverter);
123 eventModelMapper.addConverter(eventModelConverter);
124 productModelMapper.addConverter(productModelConverter);
125 categoryModelMapper.addConverter(categoryModelConverter);
126 }
127
128 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
129 @ApiOperation(value = "List catalog Attributes", notes = "List catalog attributes for a catalog id", tags = {"Catalog"})
130 @GetMapping(value = "{catalogId}/attributes")
131 public ResponseEntity<List<Attribute>> getAttributes(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
132 @RequestParam(value = "startIndex", required = false) Integer startIndex,
133 @RequestParam(value = "pageSize", required = false) Integer pageSize,
134 @RequestParam(value = "sortCol", required= false) String sortColumn,
135 @RequestParam(value = "sortDir", required = false) String sortDirection) {
136 Type listType = new TypeToken<List<Attribute>>() {
137 }.getType();
138 List<Attribute> attributes = null;
139 if (startIndex == null || pageSize == null) {
140 attributes = attributeModelMapper.map(attributeService.getAttributes(catalogId), listType);
141 } else {
142 sortDirection = validateSortDirection(sortDirection);
143 if (sortColumn == null || sortColumn.length() == 0) {
144 sortColumn = "name";
145 }
146 int startPage = 0;
147 if (startIndex > 0) {
148 startPage = startIndex / pageSize;
149 }
150 attributes = attributeModelMapper.map(attributeService.getAttributes(startPage, pageSize, sortColumn,
151 sortDirection), listType);
152 }
153 if (attributes == null) {
154 attributes = new ArrayList<>();
155 }
156 return new ResponseEntity<>(attributes, HttpStatus.OK);
157 }
158
159 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
160 @ApiOperation(value = "Create a catalog Attribute", notes = "Returns a catalog attribute", tags = {"Catalog"})
161 @GetMapping(value = "{catalogId}/attribute/{name}")
162 public ResponseEntity<Attribute> getAttribute(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
163 @ApiParam(value = "attribute name", required = true) @PathVariable String name) {
164 Optional<AttributeModel> optional = attributeService.getAttribute(catalogId, name);
165 if (!optional.isPresent()) {
166 LOGGER.warn("Unable to locate attribute {} in catalog {}", name, catalogId);
167 return new ResponseEntity<>(HttpStatus.NOT_FOUND);
168 }
169 Attribute attribute = attributeModelConverter.convert(optional.get());
170 return new ResponseEntity<>(attribute, HttpStatus.OK);
171 }
172
173
174
175 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
176 @ApiOperation(value = "Create a catalog Attribute", notes = "Creates a catalog attribute", tags = {"Catalog"})
177 @PostMapping(value = "/attribute", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
178 public ResponseEntity<Attribute> createAttribute(@ApiParam(value = "attribute", required = true) @RequestBody Attribute attribute) {
179 if (attribute.getCatalogId() == null) {
180 throw new IllegalArgumentException("A catalog id is required.");
181 }
182 if (DEFAULT_CATALOG.equals(attribute.getCatalogId())) {
183 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
184 }
185 AttributeModel model;
186 synchronized(this) {
187 Optional<AttributeModel> opt = attributeService.getAttribute(attribute.getCatalogId(), attribute.getName());
188 if (opt != null && opt.isPresent()) {
189 throw new IllegalStateException(
190 "An attribute named " + attribute.getName() + " in catalog " + attribute.getCatalogId() + " already exists");
191 }
192 model = attributeService.saveAttribute(attributeConverter.convert(attribute));
193 auditManager.saveAttribute(attribute);
194 }
195 return new ResponseEntity<>(attributeModelConverter.convert(model), HttpStatus.CREATED);
196 }
197
198 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
199 @ApiOperation(value = "Update a catalog Attribute", notes = "Updates a catalog attribute", tags = {"Catalog"})
200 @PutMapping(value = "/attribute", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
201 public ResponseEntity<Attribute> updateAttribute(@ApiParam(value = "attribute", required = true) @RequestBody Attribute attribute) {
202 if (attribute.getId() == null) {
203 throw new IllegalArgumentException("An Attribute must have an id to be updated.");
204 }
205 if (attribute.getCatalogId() == null) {
206 throw new IllegalArgumentException("A catalog id is required in the Attribute.");
207 }
208 if (DEFAULT_CATALOG.equals(attribute.getCatalogId())) {
209 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
210 }
211 AttributeModel model = attributeConverter.convert(attribute);
212 model = attributeService.saveAttribute(model);
213 auditManager.saveAttribute(attribute);
214 return new ResponseEntity<>(attributeModelConverter.convert(model), HttpStatus.OK);
215 }
216
217 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
218 @ApiOperation(value = "Deletes a catalog Attribute", notes = "Deletes a catalog attribute", tags = {"Catalog"})
219 @DeleteMapping(value = "/attribute/{id}")
220 public ResponseEntity<?> deleteAttribute(@RequestParam("id") Long attributeId) {
221 synchronized (this) {
222 Optional<AttributeModel> opt = attributeService.getAttribute(attributeId);
223 if (opt.isPresent()) {
224 attributeService.deleteAttribute(attributeId);
225 }
226 }
227 return new ResponseEntity<>(HttpStatus.OK);
228 }
229
230 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
231 @ApiOperation(value = "List catalog Events", notes = "Lists catalog events for a catalog id", tags = {"Catalog"})
232 @GetMapping(value = "{catalogId}/events")
233 public ResponseEntity<List<Event>> getEvents(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
234 @RequestParam(value = "startIndex", required = false) Integer startIndex,
235 @RequestParam(value = "pageSize", required = false) Integer pageSize,
236 @RequestParam(value = "sortCol", required= false) String sortColumn,
237 @RequestParam(value = "sortDir", required = false) String sortDirection) {
238 Type listType = new TypeToken<List<Event>>() {}.getType();
239 List<Event> events = null;
240 if (startIndex == null || pageSize == null) {
241 events = eventModelMapper.map(eventService.getEvents(catalogId), listType);
242 } else {
243 sortDirection = validateSortDirection(sortDirection);
244 if (sortColumn == null || sortColumn.length() == 0) {
245 sortColumn = "name";
246 }
247 int startPage = 0;
248 if (startIndex > 0) {
249 startPage = startIndex / pageSize;
250 }
251 events = eventModelMapper.map(eventService.getEvents(startPage, pageSize, sortColumn,
252 sortDirection), listType);
253 }
254 if (events == null) {
255 events = new ArrayList<>();
256 }
257 return new ResponseEntity<>(events, HttpStatus.OK);
258 }
259
260 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
261 @ApiOperation(value = "Create a catalog Event", notes = "Creates a catalog event", tags = {"Catalog"})
262 @PostMapping(value = "/event", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
263 public ResponseEntity<Event> createEvent(@ApiParam(value = "event", required = true) @RequestBody Event event) {
264 if (event.getCatalogId() == null) {
265 throw new IllegalArgumentException("A catalog id is required to create an event.");
266 }
267 if (DEFAULT_CATALOG.equals(event.getCatalogId())) {
268 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
269 }
270 EventModel model;
271 synchronized(this) {
272 Optional<EventModel> opt = eventService.getEvent(event.getCatalogId(), event.getName());
273 if (opt != null && opt.isPresent()) {
274 throw new IllegalStateException(
275 "An event named " + event.getName() + " in catalog " + event.getCatalogId() + " already exists");
276 }
277 model = auditManager.saveEvent(event);
278 }
279 return new ResponseEntity<>(eventModelConverter.convert(model), HttpStatus.CREATED);
280 }
281
282 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
283 @ApiOperation(value = "Update a catalog Event", notes = "Updates a catalog event", tags = {"Catalog"})
284 @PutMapping(value = "/event", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
285 public ResponseEntity<Event> updateEvent(@ApiParam(value = "event", required = true) @RequestBody Event event) {
286 if (event.getCatalogId() == null) {
287 throw new IllegalArgumentException("A catalog id is required to update an event.");
288 }
289 if (DEFAULT_CATALOG.equals(event.getCatalogId())) {
290 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
291 }
292 EventModel model;
293 synchronized(this) {
294 model = eventConverter.convert(event);
295 model = eventService.saveEvent(model);
296 }
297 return new ResponseEntity<>(eventModelConverter.convert(model), HttpStatus.OK);
298 }
299
300 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
301 @ApiOperation(value = "Deletes a catalog event", notes = "Deletes a catalog event", tags = {"Catalog"})
302 @DeleteMapping(value = "/event/{id}")
303 public ResponseEntity<?> deleteEvent(@RequestParam("id") Long eventId) {
304 eventService.deleteEvent(eventId);
305 return new ResponseEntity<>(HttpStatus.OK);
306 }
307
308 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
309 @ApiOperation(value = "List catalog Products", notes = "Lists catalog products for a catalog id", tags = {"Catalog"})
310 @GetMapping(value = "{catalogId}/products")
311 public ResponseEntity<List<Product>> getProducts(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
312 @RequestParam(value = "startIndex", required = false) Integer startIndex,
313 @RequestParam(value = "pageSize", required = false) Integer pageSize,
314 @RequestParam(value = "sortCol", required= false) String sortColumn,
315 @RequestParam(value = "sortDir", required = false) String sortDirection) {
316 Type listType = new TypeToken<List<Product>>() {}.getType();
317 List<Product> products = null;
318 if (startIndex == null || pageSize == null) {
319 products = productModelMapper.map(productService.getProducts(catalogId), listType);
320 } else {
321 sortDirection = validateSortDirection(sortDirection);
322 if (sortColumn == null || sortColumn.length() == 0) {
323 sortColumn = "name";
324 }
325 int startPage = 0;
326 if (startIndex > 0) {
327 startPage = startIndex / pageSize;
328 }
329 products = productModelMapper.map(productService.getProducts(startPage, pageSize, sortColumn,
330 sortDirection), listType);
331 }
332 if (products == null) {
333 products = new ArrayList<>();
334 }
335 return new ResponseEntity<>(products, HttpStatus.OK);
336 }
337
338 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
339 @ApiOperation(value = "Create a catalog Product", notes = "Creates a catalog product", tags = {"Catalog"})
340 @PostMapping(value = "/product", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
341 public ResponseEntity<Product> createProduct(@ApiParam(value = "product", required = true) @RequestBody Product product) {
342 if (product.getCatalogId() == null) {
343 throw new IllegalArgumentException("A catalog id is required to create a product.");
344 }
345 if (DEFAULT_CATALOG.equals(product.getCatalogId())) {
346 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
347 }
348 Optional<ProductModel> opt = productService.getProduct(product.getCatalogId(), product.getName());
349 if (opt != null && opt.isPresent()) {
350 throw new IllegalStateException("A product named "+ product.getName() + " in catalog " +
351 product.getCatalogId() + " already exists");
352 }
353 ProductModel model = productConverter.convert(product);
354 model = productService.saveProduct(model);
355 return new ResponseEntity<>(productModelConverter.convert(model), HttpStatus.CREATED);
356 }
357
358 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
359 @ApiOperation(value = "Update a catalog Product", notes = "Updates a catalog event", tags = {"Catalog"})
360 @PutMapping(value = "/product", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
361 public ResponseEntity<Product> updateProduct(@ApiParam(value = "product", required = true) @RequestBody Product product) {
362 if (product.getCatalogId() == null) {
363 throw new IllegalArgumentException("A catalog id is required to update a product.");
364 }
365 if (DEFAULT_CATALOG.equals(product.getCatalogId())) {
366 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
367 }
368 ProductModel model = productConverter.convert(product);
369 model = productService.saveProduct(model);
370 return new ResponseEntity<>(productModelConverter.convert(model), HttpStatus.OK);
371 }
372
373 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
374 @ApiOperation(value = "Deletes a catalog product", notes = "Deletes a catalog product", tags = {"Catalog"})
375 @DeleteMapping(value = "/product/{id}")
376 public ResponseEntity<?> deleteProduct(@RequestParam("id") Long productId) {
377 productService.deleteProduct(productId);
378 return new ResponseEntity<>(HttpStatus.OK);
379 }
380
381 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
382 @ApiOperation(value = "List catalog Categories", notes = "Lists catalog categories for a catalog id", tags = {"Catalog"})
383 @GetMapping(value = "{catalogId}/categories")
384 public ResponseEntity<List<Category>> getCategories(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
385 @RequestParam(value = "startIndex", required = false) Integer startIndex,
386 @RequestParam(value = "pageSize", required = false) Integer pageSize,
387 @RequestParam(value = "sortCol", required= false) String sortColumn,
388 @RequestParam(value = "sortDir", required = false) String sortDirection) {
389 Type listType = new TypeToken<List<Category>>() {}.getType();
390 List<Category> categories = null;
391 if (startIndex == null || pageSize == null) {
392 categories = categoryModelMapper.map(categoryService.getCategories(catalogId), listType);
393 } else {
394 sortDirection = validateSortDirection(sortDirection);
395 if (sortColumn == null || sortColumn.length() == 0) {
396 sortColumn = "name";
397 }
398 int startPage = 0;
399 if (startIndex > 0) {
400 startPage = startIndex / pageSize;
401 }
402 categories = categoryModelMapper.map(categoryService.getCategories(startPage, pageSize, sortColumn,
403 sortDirection), listType);
404 }
405 if (categories == null) {
406 categories = new ArrayList<>();
407 }
408 return new ResponseEntity<>(categories, HttpStatus.OK);
409 }
410
411 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
412 @ApiOperation(value = "Create a catalog Category", notes = "Creates a catalog category", tags = {"Catalog"})
413 @PostMapping(value = "/category", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
414 public ResponseEntity<Category> createCategory(@ApiParam(value = "category", required = true) @RequestBody Category category) {
415 if (category.getCatalogId() == null) {
416 throw new IllegalArgumentException("A catalog id is required to create a category.");
417 }
418 if (DEFAULT_CATALOG.equals(category.getCatalogId())) {
419 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
420 }
421 Optional<CategoryModel> opt = categoryService.getCategory(category.getCatalogId(), category.getName());
422 if (opt != null && opt.isPresent()) {
423 throw new IllegalStateException("A category named "+ category.getName() + " in catalog " +
424 category.getCatalogId() + " already exists");
425 }
426 CategoryModel model = categoryConverter.convert(category);
427 model = categoryService.saveCategory(model);
428 return new ResponseEntity<>(categoryModelConverter.convert(model), HttpStatus.CREATED);
429 }
430
431 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
432 @ApiOperation(value = "Update a catalog Category", notes = "Updates a catalog category", tags = {"Catalog"})
433 @PutMapping(value = "/category", consumes=Versions.V1_0_VALUE, produces=Versions.V1_0_VALUE)
434 public ResponseEntity<Category> updateCategory(@ApiParam(value = "category", required = true) @RequestBody Category category) {
435 if (category.getCatalogId() == null) {
436 throw new IllegalArgumentException("A catalog id is required to create a category.");
437 }
438 if (DEFAULT_CATALOG.equals(category.getCatalogId())) {
439 throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
440 }
441 CategoryModel model = categoryConverter.convert(category);
442 model = categoryService.saveCategory(model);
443 return new ResponseEntity<>(categoryModelConverter.convert(model), HttpStatus.OK);
444 }
445
446 @ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
447 @ApiOperation(value = "Deletes a catalog category", notes = "Deletes a catalog category", tags = {"Catalog"})
448 @DeleteMapping(value = "/category/{id}")
449 public ResponseEntity<?> deleteCategory(@RequestParam("id") Long categoryId) {
450 categoryService.deleteCategory(categoryId);
451 return new ResponseEntity<>(HttpStatus.OK);
452 }
453
454 private String validateSortDirection(String sortDirection) {
455 if (sortDirection == null) {
456 sortDirection = "ASC";
457 } else if (sortDirection != "ASC" && sortDirection != "DESC") {
458 LOGGER.warn("Invalid sort direction {}, defaulting to ascending", sortDirection);
459 sortDirection = "ASC";
460 }
461 return sortDirection;
462 }
463 }