/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.machine.server.recipe;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.core.rest.annotations.GenerateLink;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.core.util.LinksHelper;
import org.eclipse.che.api.machine.server.spi.RecipeDao;
import org.eclipse.che.api.machine.shared.ManagedRecipe;
import org.eclipse.che.api.machine.shared.dto.recipe.NewRecipe;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeDescriptor;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeUpdate;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.dto.server.DtoFactory;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Arrays.asList;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.Response.Status.CREATED;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_CREATE_RECIPE;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_GET_RECIPE_SCRIPT;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_REMOVE_RECIPE;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_SEARCH_RECIPES;
import static org.eclipse.che.api.machine.shared.Constants.LINK_REL_UPDATE_RECIPE;
/**
* Recipe API
*
* @author Eugene Voevodin
*/
@Path("/recipe")
public class RecipeService extends Service {
private final RecipeDao recipeDao;
@Inject
public RecipeService(RecipeDao recipeDao) {
this.recipeDao = recipeDao;
}
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@GenerateLink(rel = LINK_REL_CREATE_RECIPE)
public Response createRecipe(NewRecipe newRecipe) throws ApiException {
if (newRecipe == null) {
throw new BadRequestException("Recipe required");
}
if (isNullOrEmpty(newRecipe.getType())) {
throw new BadRequestException("Recipe type required");
}
if (isNullOrEmpty(newRecipe.getScript())) {
throw new BadRequestException("Recipe script required");
}
if (isNullOrEmpty(newRecipe.getName())) {
throw new BadRequestException("Recipe name required");
}
String userId = EnvironmentContext.getCurrent().getSubject().getUserId();
final RecipeImpl recipe = new RecipeImpl().withId(NameGenerator.generate("recipe", 16))
.withName(newRecipe.getName())
.withCreator(userId)
.withType(newRecipe.getType())
.withScript(newRecipe.getScript())
.withTags(newRecipe.getTags());
recipeDao.create(recipe);
return Response.status(CREATED)
.entity(asRecipeDescriptor(recipe))
.build();
}
@GET
@Path("/{id}/script")
public Response getRecipeScript(@PathParam("id") String id) throws ApiException, UnsupportedEncodingException {
// Do not remove!
// Docker can not use dockerfile in some cases without content-length header.
final ManagedRecipe recipe = recipeDao.getById(id);
byte[] script = recipe.getScript().getBytes("UTF-8");
return Response.ok(script, MediaType.TEXT_PLAIN)
.header(HttpHeaders.CONTENT_LENGTH, script.length)
.build();
}
@GET
@Path("/{id}")
@Produces(APPLICATION_JSON)
public RecipeDescriptor getRecipe(@PathParam("id") String id) throws ApiException {
return asRecipeDescriptor(recipeDao.getById(id));
}
@GET
@Produces(APPLICATION_JSON)
@GenerateLink(rel = LINK_REL_SEARCH_RECIPES)
public List<RecipeDescriptor> searchRecipes(@QueryParam("tags") List<String> tags,
@QueryParam("type") String type,
@DefaultValue("0") @QueryParam("skipCount") Integer skipCount,
@DefaultValue("30") @QueryParam("maxItems") Integer maxItems) throws ApiException {
final String currentUser = EnvironmentContext.getCurrent().getSubject().getUserId();
return recipeDao.search(currentUser, tags, type, skipCount, maxItems)
.stream()
.map(this::asRecipeDescriptor)
.collect(Collectors.toList());
}
@PUT
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@GenerateLink(rel = LINK_REL_UPDATE_RECIPE)
public RecipeDescriptor updateRecipe(RecipeUpdate update) throws ApiException {
if (update == null) {
throw new BadRequestException("Update required");
}
if (update.getId() == null) {
throw new BadRequestException("Recipe id required");
}
return asRecipeDescriptor(recipeDao.update(new RecipeImpl(update)));
}
@DELETE
@Path("/{id}")
public void removeRecipe(@PathParam("id") String id) throws ApiException {
recipeDao.remove(id);
}
/**
* Transforms {@link ManagedRecipe} to {@link RecipeDescriptor}.
*/
private RecipeDescriptor asRecipeDescriptor(ManagedRecipe recipe) {
final RecipeDescriptor descriptor = DtoFactory.getInstance()
.createDto(RecipeDescriptor.class)
.withId(recipe.getId())
.withName(recipe.getName())
.withType(recipe.getType())
.withScript(recipe.getScript())
.withCreator(recipe.getCreator())
.withTags(recipe.getTags());
final UriBuilder builder = getServiceContext().getServiceUriBuilder();
final Link removeLink = LinksHelper.createLink("DELETE",
builder.clone()
.path(getClass(), "removeRecipe")
.build(recipe.getId())
.toString(),
LINK_REL_REMOVE_RECIPE);
final Link scriptLink = LinksHelper.createLink("GET",
builder.clone()
.path(getClass(), "getRecipeScript")
.build(recipe.getId())
.toString(),
TEXT_PLAIN,
LINK_REL_GET_RECIPE_SCRIPT);
descriptor.setLinks(asList(scriptLink, removeLink));
return descriptor;
}
}