/**
* Copyright (C) 2015 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.web.designer.controller;
import com.google.common.base.Optional;
import org.bonitasoft.web.designer.controller.asset.AssetService;
import org.bonitasoft.web.designer.controller.utils.HttpFile;
import org.bonitasoft.web.designer.model.Identifiable;
import org.bonitasoft.web.designer.model.JacksonObjectMapper;
import org.bonitasoft.web.designer.model.JsonViewLight;
import org.bonitasoft.web.designer.model.WidgetContainerRepository;
import org.bonitasoft.web.designer.model.widget.Property;
import org.bonitasoft.web.designer.model.widget.Widget;
import org.bonitasoft.web.designer.repository.WidgetRepository;
import org.bonitasoft.web.designer.repository.exception.InUseException;
import org.bonitasoft.web.designer.repository.exception.NotAllowedException;
import org.bonitasoft.web.designer.repository.exception.NotFoundException;
import org.bonitasoft.web.designer.repository.exception.RepositoryException;
import org.bonitasoft.web.designer.service.WidgetService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.*;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
@RestController
@RequestMapping("/rest/widgets")
public class WidgetResource extends AssetResource<Widget>{
private JacksonObjectMapper objectMapper;
private WidgetRepository widgetRepository;
private WidgetService widgetService;
private Path widgetPath;
private List<WidgetContainerRepository> widgetContainerRepositories;
@Inject
public WidgetResource(JacksonObjectMapper objectMapper,
WidgetRepository widgetRepository,
WidgetService widgetService,
AssetService<Widget> widgetAssetService,
@Named("widgetPath") Path widgetPath,
List<WidgetContainerRepository> widgetContainerRepositories) {
super(widgetAssetService, widgetRepository, null, Optional.<SimpMessagingTemplate>absent());
this.widgetRepository = widgetRepository;
this.objectMapper = objectMapper;
this.widgetService = widgetService;
this.widgetPath = widgetPath;
this.widgetContainerRepositories = widgetContainerRepositories;
}
@Override
protected void checkArtifactId(String artifactId) {
checkWidgetIdIsNotAPbWidget(artifactId);
}
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> getAll(@RequestParam(value = "view", defaultValue = "full") String view) throws RepositoryException, IOException {
byte[] json;
List<Widget> widgets = widgetRepository.getAll();
if ("light".equals(view)) {
for (Widget widget : widgets) {
fillWithUsedBy(widget);
}
json = objectMapper.toJson(widgets, JsonViewLight.class);
} else {
json = objectMapper.toJson(widgets);
}
//In our case we don't know the view asked outside this method. So like we can't know which JsonView used, I
//build the json manually but in the return I must specify the mime-type in the header
//{@link ResourceControllerAdvice#getHttpHeaders()}
return new ResponseEntity<>(new String(json), ResourceControllerAdvice.getHttpHeaders(), HttpStatus.OK);
}
@SuppressWarnings("unchecked")
private void fillWithUsedBy(Widget widget) {
for (WidgetContainerRepository<Identifiable> repository : widgetContainerRepositories) {
widget.addUsedBy(repository.getComponentName(), repository.getArtifactsUsingWidget(widget.getId()));
}
}
@RequestMapping(value = "/{widgetId}", method = RequestMethod.GET)
public Widget get(@PathVariable("widgetId") String widgetId) throws RepositoryException, NotAllowedException {
return widgetRepository.get(widgetId);
}
@RequestMapping(method = RequestMethod.POST)
public Widget create(@RequestBody Widget widget, @RequestParam(value = "duplicata", required = false) String sourceWidgetId) throws IllegalArgumentException {
Widget newWidget = widgetRepository.create(widget);
if(isNotEmpty(sourceWidgetId)) {
assetService.duplicateAsset(widgetPath, widgetRepository.resolvePath(sourceWidgetId), sourceWidgetId, newWidget.getId());
}
return newWidget;
}
@RequestMapping(value = "/{widgetId}", method = RequestMethod.PUT)
public void save(@PathVariable("widgetId") String widgetId, @RequestBody Widget widget) throws RepositoryException, NotAllowedException {
checkWidgetIdIsNotAPbWidget(widgetId);
if (!widget.isCustom()) {
throw new NotAllowedException("We can only save a custom widget");
}
widget.setId(widgetId);
widgetRepository.updateLastUpdateAndSave(widget);
}
@RequestMapping(value = "/{widgetId}", method = RequestMethod.DELETE)
public void delete(@PathVariable("widgetId") String widgetId) throws RepositoryException, NotFoundException, NotAllowedException {
Widget widget = widgetRepository.get(widgetId);
if (!widget.isCustom()) {
throw new NotAllowedException("We can only delete a custom widget");
}
fillWithUsedBy(widget);
//if this widget is used elsewhere we prevent the deletion.
if (widget.isUsed()) {
throw new InUseException(buildErrorMessage(widget));
}
widgetRepository.delete(widgetId);
}
private String buildErrorMessage(Widget widget) {
//if an error occurred it's useful for user to know which components use this widget
StringBuilder msg = new StringBuilder("The widget cannot be deleted because it is used in");
for (Entry<String, List<Identifiable>> entry : widget.getUsedBy().entrySet()) {
List<? extends Identifiable> elements = entry.getValue();
if (!elements.isEmpty()) {
msg.append(" ").append(elements.size()).append(" " + entry.getKey()).append(elements.size() > 1 ? "s" : "");
for (Identifiable element : elements) {
msg.append(", <").append(element.getName()).append(">");
}
}
}
return msg.toString();
}
@RequestMapping(value = "/{widgetId}/properties", method = RequestMethod.POST)
public List<Property> addProperty(@PathVariable("widgetId") String widgetId, @RequestBody Property property) throws RepositoryException, NotFoundException, NotAllowedException {
checkWidgetIdIsNotAPbWidget(widgetId);
return widgetRepository.addProperty(widgetId, property);
}
@RequestMapping(value = "/{widgetId}/properties/{propertyName}", method = RequestMethod.PUT)
public List<Property> updateProperty(@PathVariable("widgetId") String widgetId, @PathVariable("propertyName") String propertyName, @RequestBody Property property) throws RepositoryException, NotFoundException, NotAllowedException {
checkWidgetIdIsNotAPbWidget(widgetId);
return widgetService.updateProperty(widgetId, propertyName, property);
}
@RequestMapping(value = "/{widgetId}/properties/{propertyName}", method = RequestMethod.DELETE)
public List<Property> deleteProperty(@PathVariable("widgetId") String widgetId, @PathVariable("propertyName") String propertyName) throws RepositoryException, NotFoundException, NotAllowedException {
checkWidgetIdIsNotAPbWidget(widgetId);
return widgetRepository.deleteProperty(widgetId, propertyName);
}
@RequestMapping(value = "/{widgetId}/favorite", method = RequestMethod.PUT)
public void favorite(@PathVariable("widgetId") String pageId, @RequestBody Boolean favorite) throws RepositoryException {
if (favorite) {
widgetRepository.markAsFavorite(pageId);
} else {
widgetRepository.unmarkAsFavorite(pageId);
}
}
@RequestMapping(value = "/{widgetId}/help", method = RequestMethod.GET, produces = "text/html; charset=UTF-8")
public void serveWidgetFiles(HttpServletRequest request, HttpServletResponse response, @PathVariable("widgetId") String widgetId) throws IOException {
HttpFile.writeFileInResponse(request, response, widgetPath.resolve(widgetId+"/help.html"));
}
private void checkWidgetIdIsNotAPbWidget(String widgetId) {
if (isPbWidgetId(widgetId)) {
throw new NotAllowedException("Not allowed to modify a non custom widgets");
}
}
private boolean isPbWidgetId(String widgetId) {
return widgetId.startsWith("pb");
}
}