org.eclipse.che.api.workspace.server.stack.StackService.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.api.workspace.server.stack.StackService.java

Source

/*******************************************************************************
 * 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.workspace.server.stack;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

import org.apache.commons.fileupload.FileItem;
import org.eclipse.che.api.core.ApiException;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
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.workspace.server.model.impl.stack.StackImpl;
import org.eclipse.che.api.workspace.server.spi.StackDao;
import org.eclipse.che.api.workspace.server.stack.image.StackIcon;
import org.eclipse.che.api.workspace.shared.dto.stack.StackDto;
import org.eclipse.che.commons.env.EnvironmentContext;

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.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.MULTIPART_FORM_DATA;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.Response.Status.CREATED;
import static org.eclipse.che.api.workspace.server.DtoConverter.asDto;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_CREATE_STACK;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_DELETE_ICON;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_GET_ICON;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_GET_STACK_BY_ID;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_REMOVE_STACK;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_SEARCH_STACKS;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_UPDATE_STACK;
import static org.eclipse.che.api.workspace.shared.Constants.LINK_REL_UPLOAD_ICON;

/**
 * Defines Stack REST API
 *
 * @author Alexander Andrienko
 */
@Api(value = "/stack", description = "Stack REST API")
@Path("/stack")
public class StackService extends Service {

    private final StackDao stackDao;
    private final StackValidator stackValidator;

    @Inject
    public StackService(StackDao stackDao, StackValidator stackValidator) {
        this.stackDao = stackDao;
        this.stackValidator = stackValidator;
    }

    @POST
    @Consumes(APPLICATION_JSON)
    @Produces(APPLICATION_JSON)
    @GenerateLink(rel = LINK_REL_CREATE_STACK)
    @ApiOperation(value = "Create a new stack", notes = "This operation can be performed only by authorized user", response = StackDto.class)
    @ApiResponses({ @ApiResponse(code = 201, message = "The stack successfully created"),
            @ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
            @ApiResponse(code = 403, message = "The user does not have access to create a new stack"),
            @ApiResponse(code = 409, message = "Conflict error occurred during the stack creation"
                    + "(e.g. The stack with such name already exists)"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public Response createStack(@ApiParam("The new stack") final StackDto stackDto) throws ApiException {
        stackValidator.check(stackDto);
        final String userId = EnvironmentContext.getCurrent().getSubject().getUserId();
        final StackImpl newStack = StackImpl.builder().generateId().setName(stackDto.getName())
                .setDescription(stackDto.getDescription()).setScope(stackDto.getScope()).setCreator(userId)
                .setTags(stackDto.getTags()).setWorkspaceConfig(stackDto.getWorkspaceConfig())
                .setSource(stackDto.getSource()).setComponents(stackDto.getComponents()).build();
        stackDao.create(newStack);

        return Response.status(CREATED).entity(asStackDto(newStack)).build();
    }

    @GET
    @Path("/{id}")
    @Produces(APPLICATION_JSON)
    @GenerateLink(rel = LINK_REL_GET_STACK_BY_ID)
    @ApiOperation(value = "Get the stack by id", notes = "This operation can be performed for stack owner, or for predefined stacks")
    @ApiResponses({ @ApiResponse(code = 200, message = "The response contains requested stack entity"),
            @ApiResponse(code = 404, message = "The requested stack was not found"),
            @ApiResponse(code = 403, message = "The user has not permission get requested stack"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public StackDto getStack(@ApiParam("The stack id") @PathParam("id") final String id) throws ApiException {
        return asStackDto(stackDao.getById(id));
    }

    @PUT
    @Path("/{id}")
    @Produces(APPLICATION_JSON)
    @Consumes(APPLICATION_JSON)
    @GenerateLink(rel = LINK_REL_UPDATE_STACK)
    @ApiOperation(value = "Update the stack by replacing all the existing data (exclude field \"creator\") with update")
    @ApiResponses({ @ApiResponse(code = 200, message = "The stack successfully updated"),
            @ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
            @ApiResponse(code = 403, message = "The user does not have access to update the stack"),
            @ApiResponse(code = 409, message = "Conflict error occurred during stack update"
                    + "(e.g. Stack with such name already exists)"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public StackDto updateStack(@ApiParam(value = "The stack update", required = true) final StackDto updateDto,
            @ApiParam(value = "The stack id", required = true) @PathParam("id") final String id)
            throws ApiException {
        stackValidator.check(updateDto);
        final StackImpl stack = stackDao.getById(id);

        StackImpl stackForUpdate = StackImpl.builder().setId(id).setName(updateDto.getName())
                .setDescription(updateDto.getDescription()).setScope(updateDto.getScope())
                //user can't edit creator
                .setCreator(stack.getCreator()).setTags(updateDto.getTags())
                .setWorkspaceConfig(updateDto.getWorkspaceConfig()).setSource(updateDto.getSource())
                .setComponents(updateDto.getComponents()).build();

        return asStackDto(stackDao.update(stackForUpdate));
    }

    @DELETE
    @Path("/{id}")
    @GenerateLink(rel = LINK_REL_REMOVE_STACK)
    @ApiOperation(value = "Removes the stack")
    @ApiResponses({ @ApiResponse(code = 204, message = "The stack successfully removed"),
            @ApiResponse(code = 403, message = "The user does not have access to remove the stack"),
            @ApiResponse(code = 404, message = "The stack doesn't exist"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public void removeStack(@ApiParam("The stack id") @PathParam("id") final String id) throws ApiException {
        stackDao.remove(id);
    }

    @GET
    @Produces(APPLICATION_JSON)
    @GenerateLink(rel = LINK_REL_SEARCH_STACKS)
    @ApiOperation(value = "Get the list stacks with required tags", notes = "This operation can be performed only by authorized user", response = StackDto.class, responseContainer = "List")
    @ApiResponses({
            @ApiResponse(code = 200, message = "The response contains requested list stack entity with required tags"),
            @ApiResponse(code = 403, message = "The user does not have access to get stack entity list with required tags"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public List<StackDto> searchStacks(
            @ApiParam("List tags for search") @QueryParam("tags") final List<String> tags,
            @ApiParam(value = "The number of the items to skip") @DefaultValue("0") @QueryParam("skipCount") final Integer skipCount,
            @ApiParam("The limit of the items in the response, default is 30") @DefaultValue("30") @QueryParam("maxItems") final Integer maxItems)
            throws ServerException {
        final String currentUser = EnvironmentContext.getCurrent().getSubject().getUserId();
        return stackDao.searchStacks(currentUser, tags, skipCount, maxItems).stream().map(this::asStackDto)
                .collect(Collectors.toList());
    }

    @GET
    @Path("/{id}/icon")
    @Produces("image/*")
    @GenerateLink(rel = LINK_REL_GET_ICON)
    @ApiOperation(value = "Get icon by stack id", notes = "This operation can be performed only by authorized user", response = byte[].class)
    @ApiResponses({ @ApiResponse(code = 200, message = "The response contains requested image entity"),
            @ApiResponse(code = 403, message = "The user does not have access to get image entity"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public Response getIcon(@ApiParam("The stack id") @PathParam("id") final String id)
            throws NotFoundException, ServerException, BadRequestException {
        StackImpl stack = stackDao.getById(id);

        if (stack == null) {
            throw new NotFoundException("Stack with id '" + id + "' was not found.");
        }

        StackIcon image = stack.getStackIcon();

        if (image == null) {
            throw new NotFoundException("Image for stack with id '" + id + "' was not found.");
        }
        return Response.ok(image.getData(), image.getMediaType()).build();
    }

    @POST
    @Path("/{id}/icon")
    @Consumes(MULTIPART_FORM_DATA)
    @Produces(TEXT_PLAIN)
    @GenerateLink(rel = LINK_REL_UPLOAD_ICON)
    @ApiOperation(value = "Upload icon for required stack", notes = "This operation can be performed only by authorized stack owner")
    @ApiResponses({ @ApiResponse(code = 200, message = "Image was successfully uploaded"),
            @ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
            @ApiResponse(code = 403, message = "The user does not have access upload image for stack with required id"),
            @ApiResponse(code = 404, message = "The stack doesn't exist"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public Response uploadIcon(@ApiParam("The image for stack") final Iterator<FileItem> formData,
            @ApiParam("The stack id") @PathParam("id") final String id)
            throws NotFoundException, ServerException, BadRequestException, ForbiddenException, ConflictException {
        if (formData.hasNext()) {
            FileItem fileItem = formData.next();
            StackIcon stackIcon = new StackIcon(fileItem.getName(), fileItem.getContentType(), fileItem.get());

            StackImpl stack = stackDao.getById(id);

            stack.setStackIcon(stackIcon);
            stackDao.update(stack);
        }
        return Response.ok().build();
    }

    @DELETE
    @Path("/{id}/icon")
    @GenerateLink(rel = LINK_REL_DELETE_ICON)
    @ApiOperation(value = "Delete icon for required stack", notes = "This operation can be performed only by authorized stack owner")
    @ApiResponses({ @ApiResponse(code = 204, message = "Icon was successfully removed"),
            @ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
            @ApiResponse(code = 403, message = "The user does not have access upload image for stack with required id"),
            @ApiResponse(code = 404, message = "The stack or icon doesn't exist"),
            @ApiResponse(code = 409, message = "Conflict error occurred during stack update"
                    + "(e.g. Stack with such name already exists)"),
            @ApiResponse(code = 500, message = "Internal server error occurred") })
    public void removeIcon(@ApiParam("The stack Id") @PathParam("id") final String id)
            throws NotFoundException, ServerException, ConflictException, ForbiddenException, BadRequestException {
        StackImpl stack = stackDao.getById(id);
        stack.setStackIcon(null);
        stackDao.update(stack);
    }

    private StackDto asStackDto(StackImpl stack) {
        final UriBuilder builder = getServiceContext().getServiceUriBuilder();

        List<Link> links = new ArrayList<>();
        final Link removeLink = LinksHelper.createLink("DELETE",
                builder.clone().path(getClass(), "removeStack").build(stack.getId()).toString(),
                LINK_REL_REMOVE_STACK);
        final Link getLink = LinksHelper.createLink("GET",
                builder.clone().path(getClass(), "getStack").build(stack.getId()).toString(), APPLICATION_JSON,
                LINK_REL_GET_STACK_BY_ID);
        links.add(removeLink);
        links.add(getLink);

        StackIcon stackIcon = stack.getStackIcon();
        if (stackIcon != null) {
            Link deleteIcon = LinksHelper.createLink("DELETE",
                    builder.clone().path(getClass(), "removeIcon").build(stack.getId()).toString(),
                    stackIcon.getMediaType(), LINK_REL_DELETE_ICON);
            Link getIconLink = LinksHelper.createLink("GET",
                    builder.clone().path(getClass(), "getIcon").build(stack.getId()).toString(),
                    stackIcon.getMediaType(), LINK_REL_GET_ICON);
            links.add(deleteIcon);
            links.add(getIconLink);
        }
        return asDto(stack).withLinks(links);
    }
}