blob: 6fceface41128d14641b1db925851f16892a0eb3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.audit.service.controller;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.PostConstruct;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.audit.service.catalog.AuditManager;
import org.apache.logging.log4j.catalog.api.Attribute;
import org.apache.logging.log4j.catalog.api.Category;
import org.apache.logging.log4j.catalog.api.Event;
import org.apache.logging.log4j.catalog.api.Product;
import org.apache.logging.log4j.catalog.api.Versions;
import org.apache.logging.log4j.catalog.jpa.converter.AttributeConverter;
import org.apache.logging.log4j.catalog.jpa.converter.AttributeModelConverter;
import org.apache.logging.log4j.catalog.jpa.converter.CategoryConverter;
import org.apache.logging.log4j.catalog.jpa.converter.CategoryModelConverter;
import org.apache.logging.log4j.catalog.jpa.converter.EventConverter;
import org.apache.logging.log4j.catalog.jpa.converter.EventModelConverter;
import org.apache.logging.log4j.catalog.jpa.converter.ProductConverter;
import org.apache.logging.log4j.catalog.jpa.converter.ProductModelConverter;
import org.apache.logging.log4j.catalog.jpa.model.AttributeModel;
import org.apache.logging.log4j.catalog.jpa.model.CategoryModel;
import org.apache.logging.log4j.catalog.jpa.model.EventModel;
import org.apache.logging.log4j.catalog.jpa.model.ProductModel;
import org.apache.logging.log4j.catalog.jpa.service.AttributeService;
import org.apache.logging.log4j.catalog.jpa.service.CategoryService;
import org.apache.logging.log4j.catalog.jpa.service.EventService;
import org.apache.logging.log4j.catalog.jpa.service.ProductService;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static org.apache.logging.log4j.catalog.api.constant.Constants.DEFAULT_CATALOG;
@RestController
@RequestMapping(value = "/catalog")
public class CatalogController {
private static final Logger LOGGER = LogManager.getLogger(CatalogController.class);
private final ModelMapper attributeModelMapper = new ModelMapper();
private final ModelMapper eventModelMapper = new ModelMapper();
private final ModelMapper productModelMapper = new ModelMapper();
private final ModelMapper categoryModelMapper = new ModelMapper();
@Autowired
private AttributeService attributeService;
@Autowired
private AttributeModelConverter attributeModelConverter;
@Autowired
private AttributeConverter attributeConverter;
@Autowired
private EventService eventService;
@Autowired
private EventModelConverter eventModelConverter;
@Autowired
private EventConverter eventConverter;
@Autowired
private ProductService productService;
@Autowired
private ProductModelConverter productModelConverter;
@Autowired
private ProductConverter productConverter;
@Autowired
private CategoryService categoryService;
@Autowired
private AuditManager auditManager;
@Autowired
private CategoryModelConverter categoryModelConverter;
@Autowired
private CategoryConverter categoryConverter;
@PostConstruct
public void init() {
attributeModelMapper.addConverter(attributeModelConverter);
eventModelMapper.addConverter(eventModelConverter);
productModelMapper.addConverter(productModelConverter);
categoryModelMapper.addConverter(categoryModelConverter);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "List catalog Attributes", notes = "List catalog attributes for a catalog id", tags = {"Catalog"})
@GetMapping(value = "{catalogId}/attributes")
public ResponseEntity<List<Attribute>> getAttributes(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
@RequestParam(value = "startIndex", required = false) Integer startIndex,
@RequestParam(value = "pageSize", required = false) Integer pageSize,
@RequestParam(value = "sortCol", required= false) String sortColumn,
@RequestParam(value = "sortDir", required = false) String sortDirection) {
Type listType = new TypeToken<List<Attribute>>() {
}.getType();
List<Attribute> attributes;
if (startIndex == null || pageSize == null) {
attributes = attributeModelMapper.map(attributeService.getAttributes(catalogId), listType);
} else {
sortDirection = validateSortDirection(sortDirection);
if (sortColumn == null || sortColumn.length() == 0) {
sortColumn = "name";
}
int startPage = 0;
if (startIndex > 0) {
startPage = startIndex / pageSize;
}
attributes = attributeModelMapper.map(attributeService.getAttributes(startPage, pageSize, sortColumn,
sortDirection), listType);
}
if (attributes == null) {
attributes = new ArrayList<>();
}
return new ResponseEntity<>(attributes, HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Create a catalog Attribute", notes = "Returns a catalog attribute", tags = {"Catalog"})
@GetMapping(value = "{catalogId}/attribute/{name}")
public ResponseEntity<Attribute> getAttribute(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
@ApiParam(value = "attribute name", required = true) @PathVariable String name) {
Optional<AttributeModel> optional = attributeService.getAttribute(catalogId, name);
if (!optional.isPresent()) {
LOGGER.warn("Unable to locate attribute {} in catalog {}", name, catalogId);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Attribute attribute = attributeModelConverter.convert(optional.get());
return new ResponseEntity<>(attribute, HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Create a catalog Attribute", notes = "Creates a catalog attribute", tags = {"Catalog"})
@PostMapping(value = "/attribute", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Attribute> createAttribute(@ApiParam(value = "attribute", required = true) @RequestBody Attribute attribute) {
if (attribute.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required.");
}
if (DEFAULT_CATALOG.equals(attribute.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
AttributeModel model;
synchronized(this) {
Optional<AttributeModel> opt = attributeService.getAttribute(attribute.getCatalogId(), attribute.getName());
if (opt != null && opt.isPresent()) {
throw new IllegalStateException(
"An attribute named " + attribute.getName() + " in catalog " + attribute.getCatalogId() + " already exists");
}
model = attributeService.saveAttribute(attributeConverter.convert(attribute));
auditManager.saveAttribute(attribute);
}
return new ResponseEntity<>(attributeModelConverter.convert(model), HttpStatus.CREATED);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Update a catalog Attribute", notes = "Updates a catalog attribute", tags = {"Catalog"})
@PutMapping(value = "/attribute", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Attribute> updateAttribute(@ApiParam(value = "attribute", required = true) @RequestBody Attribute attribute) {
if (attribute.getId() == null) {
throw new IllegalArgumentException("An Attribute must have an id to be updated.");
}
if (attribute.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required in the Attribute.");
}
if (DEFAULT_CATALOG.equals(attribute.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
AttributeModel model = attributeConverter.convert(attribute);
model = attributeService.saveAttribute(model);
auditManager.saveAttribute(attribute);
return new ResponseEntity<>(attributeModelConverter.convert(model), HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Deletes a catalog Attribute", notes = "Deletes a catalog attribute", tags = {"Catalog"})
@DeleteMapping(value = "/attribute/{id}")
public ResponseEntity<?> deleteAttribute(@RequestParam("id") Long attributeId) {
synchronized (this) {
Optional<AttributeModel> opt = attributeService.getAttribute(attributeId);
if (opt.isPresent()) {
attributeService.deleteAttribute(attributeId);
}
}
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "List catalog Events", notes = "Lists catalog events for a catalog id", tags = {"Catalog"})
@GetMapping(value = "{catalogId}/events")
public ResponseEntity<List<Event>> getEvents(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
@RequestParam(value = "startIndex", required = false) Integer startIndex,
@RequestParam(value = "pageSize", required = false) Integer pageSize,
@RequestParam(value = "sortCol", required= false) String sortColumn,
@RequestParam(value = "sortDir", required = false) String sortDirection) {
Type listType = new TypeToken<List<Event>>() {}.getType();
List<Event> events;
if (startIndex == null || pageSize == null) {
events = eventModelMapper.map(eventService.getEvents(catalogId), listType);
} else {
sortDirection = validateSortDirection(sortDirection);
if (sortColumn == null || sortColumn.length() == 0) {
sortColumn = "name";
}
int startPage = 0;
if (startIndex > 0) {
startPage = startIndex / pageSize;
}
events = eventModelMapper.map(eventService.getEvents(startPage, pageSize, sortColumn,
sortDirection), listType);
}
if (events == null) {
events = new ArrayList<>();
}
return new ResponseEntity<>(events, HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Create a catalog Event", notes = "Creates a catalog event", tags = {"Catalog"})
@PostMapping(value = "/event", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Event> createEvent(@ApiParam(value = "event", required = true) @RequestBody Event event) {
if (event.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to create an event.");
}
if (DEFAULT_CATALOG.equals(event.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
EventModel model;
synchronized(this) {
Optional<EventModel> opt = eventService.getEvent(event.getCatalogId(), event.getName());
if (opt != null && opt.isPresent()) {
throw new IllegalStateException(
"An event named " + event.getName() + " in catalog " + event.getCatalogId() + " already exists");
}
model = auditManager.saveEvent(event);
}
return new ResponseEntity<>(eventModelConverter.convert(model), HttpStatus.CREATED);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Update a catalog Event", notes = "Updates a catalog event", tags = {"Catalog"})
@PutMapping(value = "/event", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Event> updateEvent(@ApiParam(value = "event", required = true) @RequestBody Event event) {
if (event.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to update an event.");
}
if (DEFAULT_CATALOG.equals(event.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
EventModel model;
synchronized(this) {
model = eventConverter.convert(event);
model = eventService.saveEvent(model);
}
return new ResponseEntity<>(eventModelConverter.convert(model), HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Deletes a catalog event", notes = "Deletes a catalog event", tags = {"Catalog"})
@DeleteMapping(value = "/event/{id}")
public ResponseEntity<?> deleteEvent(@RequestParam("id") Long eventId) {
eventService.deleteEvent(eventId);
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "List catalog Products", notes = "Lists catalog products for a catalog id", tags = {"Catalog"})
@GetMapping(value = "{catalogId}/products")
public ResponseEntity<List<Product>> getProducts(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
@RequestParam(value = "startIndex", required = false) Integer startIndex,
@RequestParam(value = "pageSize", required = false) Integer pageSize,
@RequestParam(value = "sortCol", required= false) String sortColumn,
@RequestParam(value = "sortDir", required = false) String sortDirection) {
Type listType = new TypeToken<List<Product>>() {}.getType();
List<Product> products;
if (startIndex == null || pageSize == null) {
products = productModelMapper.map(productService.getProducts(catalogId), listType);
} else {
sortDirection = validateSortDirection(sortDirection);
if (sortColumn == null || sortColumn.length() == 0) {
sortColumn = "name";
}
int startPage = 0;
if (startIndex > 0) {
startPage = startIndex / pageSize;
}
products = productModelMapper.map(productService.getProducts(startPage, pageSize, sortColumn,
sortDirection), listType);
}
if (products == null) {
products = new ArrayList<>();
}
return new ResponseEntity<>(products, HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Create a catalog Product", notes = "Creates a catalog product", tags = {"Catalog"})
@PostMapping(value = "/product", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Product> createProduct(@ApiParam(value = "product", required = true) @RequestBody Product product) {
if (product.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to create a product.");
}
if (DEFAULT_CATALOG.equals(product.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
Optional<ProductModel> opt = productService.getProduct(product.getCatalogId(), product.getName());
if (opt != null && opt.isPresent()) {
throw new IllegalStateException("A product named "+ product.getName() + " in catalog " +
product.getCatalogId() + " already exists");
}
ProductModel model = productConverter.convert(product);
model = productService.saveProduct(model);
return new ResponseEntity<>(productModelConverter.convert(model), HttpStatus.CREATED);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Update a catalog Product", notes = "Updates a catalog event", tags = {"Catalog"})
@PutMapping(value = "/product", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Product> updateProduct(@ApiParam(value = "product", required = true) @RequestBody Product product) {
if (product.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to update a product.");
}
if (DEFAULT_CATALOG.equals(product.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
ProductModel model = productConverter.convert(product);
model = productService.saveProduct(model);
return new ResponseEntity<>(productModelConverter.convert(model), HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Deletes a catalog product", notes = "Deletes a catalog product", tags = {"Catalog"})
@DeleteMapping(value = "/product/{id}")
public ResponseEntity<?> deleteProduct(@RequestParam("id") Long productId) {
productService.deleteProduct(productId);
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "List catalog Categories", notes = "Lists catalog categories for a catalog id", tags = {"Catalog"})
@GetMapping(value = "{catalogId}/categories")
public ResponseEntity<List<Category>> getCategories(@ApiParam(value = "catalog id", required = true) @PathVariable String catalogId,
@RequestParam(value = "startIndex", required = false) Integer startIndex,
@RequestParam(value = "pageSize", required = false) Integer pageSize,
@RequestParam(value = "sortCol", required= false) String sortColumn,
@RequestParam(value = "sortDir", required = false) String sortDirection) {
Type listType = new TypeToken<List<Category>>() {}.getType();
List<Category> categories;
if (startIndex == null || pageSize == null) {
categories = categoryModelMapper.map(categoryService.getCategories(catalogId), listType);
} else {
sortDirection = validateSortDirection(sortDirection);
if (sortColumn == null || sortColumn.length() == 0) {
sortColumn = "name";
}
int startPage = 0;
if (startIndex > 0) {
startPage = startIndex / pageSize;
}
categories = categoryModelMapper.map(categoryService.getCategories(startPage, pageSize, sortColumn,
sortDirection), listType);
}
if (categories == null) {
categories = new ArrayList<>();
}
return new ResponseEntity<>(categories, HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Create a catalog Category", notes = "Creates a catalog category", tags = {"Catalog"})
@PostMapping(value = "/category", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Category> createCategory(@ApiParam(value = "category", required = true) @RequestBody Category category) {
if (category.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to create a category.");
}
if (DEFAULT_CATALOG.equals(category.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
Optional<CategoryModel> opt = categoryService.getCategory(category.getCatalogId(), category.getName());
if (opt != null && opt.isPresent()) {
throw new IllegalStateException("A category named "+ category.getName() + " in catalog " +
category.getCatalogId() + " already exists");
}
CategoryModel model = categoryConverter.convert(category);
model = categoryService.saveCategory(model);
return new ResponseEntity<>(categoryModelConverter.convert(model), HttpStatus.CREATED);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Update a catalog Category", notes = "Updates a catalog category", tags = {"Catalog"})
@PutMapping(value = "/category", consumes=Versions.V1_0, produces=Versions.V1_0)
public ResponseEntity<Category> updateCategory(@ApiParam(value = "category", required = true) @RequestBody Category category) {
if (category.getCatalogId() == null) {
throw new IllegalArgumentException("A catalog id is required to create a category.");
}
if (DEFAULT_CATALOG.equals(category.getCatalogId())) {
throw new IllegalArgumentException("The default catalog cannot be modified at run time.");
}
CategoryModel model = categoryConverter.convert(category);
model = categoryService.saveCategory(model);
return new ResponseEntity<>(categoryModelConverter.convert(model), HttpStatus.OK);
}
@ApiImplicitParams( {@ApiImplicitParam(dataType = "String", name = "Authorization", paramType = "header")})
@ApiOperation(value = "Deletes a catalog category", notes = "Deletes a catalog category", tags = {"Catalog"})
@DeleteMapping(value = "/category/{id}")
public ResponseEntity<?> deleteCategory(@RequestParam("id") Long categoryId) {
categoryService.deleteCategory(categoryId);
return new ResponseEntity<>(HttpStatus.OK);
}
private String validateSortDirection(String sortDirection) {
if (sortDirection == null) {
sortDirection = "ASC";
} else if (!sortDirection.equals("ASC") && !sortDirection.equals("DESC")) {
LOGGER.warn("Invalid sort direction {}, defaulting to ascending", sortDirection);
sortDirection = "ASC";
}
return sortDirection;
}
}