com.vmware.photon.controller.apife.backends.TaskDcpBackend.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.photon.controller.apife.backends.TaskDcpBackend.java

Source

/*
 * Copyright 2015 VMware, Inc. All Rights Reserved.
 *
 * Licensed 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 com.vmware.photon.controller.apife.backends;

import com.vmware.photon.controller.api.Operation;
import com.vmware.photon.controller.api.ResourceList;
import com.vmware.photon.controller.api.Step;
import com.vmware.photon.controller.api.Task;
import com.vmware.photon.controller.api.common.entities.base.BaseEntity;
import com.vmware.photon.controller.api.common.exceptions.external.ExternalException;
import com.vmware.photon.controller.api.common.exceptions.external.PageExpiredException;
import com.vmware.photon.controller.api.common.exceptions.external.TaskNotFoundException;
import com.vmware.photon.controller.apife.backends.clients.ApiFeXenonRestClient;
import com.vmware.photon.controller.apife.entities.StepEntity;
import com.vmware.photon.controller.apife.entities.StepErrorBaseEntity;
import com.vmware.photon.controller.apife.entities.StepErrorEntity;
import com.vmware.photon.controller.apife.entities.StepResourceEntity;
import com.vmware.photon.controller.apife.entities.StepWarningEntity;
import com.vmware.photon.controller.apife.entities.TaskEntity;
import com.vmware.photon.controller.apife.entities.base.InfrastructureEntity;
import com.vmware.photon.controller.apife.exceptions.external.InvalidQueryParamsException;
import com.vmware.photon.controller.apife.utils.PaginationUtils;
import com.vmware.photon.controller.cloudstore.dcp.entity.TaskService;
import com.vmware.photon.controller.cloudstore.dcp.entity.TaskServiceFactory;
import com.vmware.photon.controller.common.xenon.ServiceUtils;
import com.vmware.photon.controller.common.xenon.exceptions.DocumentNotFoundException;
import com.vmware.xenon.common.ServiceDocumentQueryResult;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Common task operations using DCP cloud store.
 */
@Singleton
public class TaskDcpBackend implements TaskBackend, StepBackend {

    private static final Logger logger = LoggerFactory.getLogger(TaskDcpBackend.class);

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private final ApiFeXenonRestClient dcpClient;

    private final EntityLockBackend entityLockBackend;

    @Inject
    public TaskDcpBackend(ApiFeXenonRestClient dcpClient, EntityLockBackend entityLockBackend) {
        this.dcpClient = dcpClient;
        this.entityLockBackend = entityLockBackend;

        dcpClient.start();
    }

    @Override
    public StepBackend getStepBackend() {
        return this;
    }

    @Override
    public Task getApiRepresentation(String id) throws TaskNotFoundException {
        return toApiRepresentation(findById(id));
    }

    @Override
    public Task getApiRepresentation(TaskEntity task) throws TaskNotFoundException {
        return toApiRepresentation(task);
    }

    @Override
    public ResourceList<Task> filter(String entityId, String entityKind, Optional<String> state,
            Optional<Integer> pageSize) throws ExternalException {

        return this.filter(Optional.of(entityId), Optional.of(entityKind), state, pageSize);
    }

    @Override
    public ResourceList<Task> filter(Optional<String> entityId, Optional<String> entityKind, Optional<String> state,
            Optional<Integer> pageSize) throws ExternalException {
        ResourceList<TaskEntity> taskEntities = getEntityTasks(entityId, entityKind, state, pageSize);
        return toApiRepresentation(taskEntities);
    }

    @Override
    public ResourceList<Task> getTasksPage(String pageLink) throws PageExpiredException {
        ResourceList<TaskEntity> taskEntities = getEntityTasksPage(pageLink);
        return toApiRepresentation(taskEntities);
    }

    @Override
    public TaskEntity createQueuedTask(BaseEntity entity, Operation operation) {

        TaskService.State taskServiceState = new TaskService.State();

        //currently creation of kubernetes and mesos cluster, their resize and delete pass null entity
        //putting this null check temporarily to allow the switch to dcp backend to work
        //
        if (entity != null) {
            taskServiceState.entityId = entity.getId();
            taskServiceState.entityKind = entity.getKind();

            // auto-link infrastructure tasks to their project
            if (entity instanceof InfrastructureEntity) {
                InfrastructureEntity infrastructureEntity = (InfrastructureEntity) entity;
                String projectId = infrastructureEntity.getProjectId();
                taskServiceState.projectId = projectId;
            }
        }

        taskServiceState.state = TaskService.State.TaskState.QUEUED;
        taskServiceState.operation = operation;
        taskServiceState.queuedTime = DateTime.now().toDate();

        com.vmware.xenon.common.Operation result = dcpClient.post(TaskServiceFactory.SELF_LINK, taskServiceState);
        TaskService.State createdState = result.getBody(TaskService.State.class);
        TaskEntity task = convertToTaskEntity(createdState);
        logger.info("created task: {}", task);
        return task;
    }

    @Override
    public TaskEntity createCompletedTask(BaseEntity entity, Operation operation) {
        TaskService.State taskServiceState = new TaskService.State();
        if (entity != null) {
            taskServiceState.entityId = entity.getId();
            taskServiceState.entityKind = entity.getKind();

            // auto-link infrastructure tasks to their project
            if (entity instanceof InfrastructureEntity) {
                InfrastructureEntity infrastructureEntity = (InfrastructureEntity) entity;
                String projectId = infrastructureEntity.getProjectId();
                taskServiceState.projectId = projectId;
            }
        }

        taskServiceState.state = TaskService.State.TaskState.COMPLETED;
        taskServiceState.operation = operation;
        taskServiceState.startedTime = DateTime.now().toDate();
        taskServiceState.endTime = taskServiceState.startedTime;
        taskServiceState.queuedTime = taskServiceState.startedTime;

        com.vmware.xenon.common.Operation result = dcpClient.post(TaskServiceFactory.SELF_LINK, taskServiceState);
        TaskService.State createdState = result.getBody(TaskService.State.class);
        TaskEntity task = convertToTaskEntity(createdState);
        logger.info("created task: {}", task);
        return task;
    }

    @Override
    public TaskEntity createTaskWithSteps(BaseEntity entity, Operation operation, Boolean isCompleted,
            List<StepEntity> stepEntities) {
        Date currentTime = DateTime.now().toDate();
        TaskService.State taskServiceState = new TaskService.State();

        //currently creation of kubernetes and mesos cluster, their resize and delete pass null entity
        //putting this null check temporarily to allow the switch to dcp backend to work
        //
        if (entity != null) {
            taskServiceState.entityId = entity.getId();
            taskServiceState.entityKind = entity.getKind();

            // auto-link infrastructure tasks to their project
            if (entity instanceof InfrastructureEntity) {
                InfrastructureEntity infrastructureEntity = (InfrastructureEntity) entity;
                String projectId = infrastructureEntity.getProjectId();
                taskServiceState.projectId = projectId;
            }
        }

        if (isCompleted) {
            taskServiceState.state = TaskService.State.TaskState.COMPLETED;
            taskServiceState.startedTime = currentTime;
            taskServiceState.endTime = currentTime;
            taskServiceState.queuedTime = currentTime;
        } else {
            taskServiceState.state = TaskService.State.TaskState.QUEUED;
            taskServiceState.queuedTime = currentTime;
        }

        taskServiceState.operation = operation;

        if (stepEntities != null) {
            taskServiceState.steps = new ArrayList<>();
            Integer nextStepSequence = 0;
            for (StepEntity stepEntity : stepEntities) {
                stepEntity.setQueuedTime(currentTime);
                if (isCompleted) {
                    stepEntity.setState(StepEntity.State.COMPLETED);
                    stepEntity.setStartedTime(currentTime);
                    stepEntity.setEndTime(currentTime);
                } else {
                    stepEntity.setState(StepEntity.State.QUEUED);
                }
                stepEntity.setSequence(nextStepSequence);
                nextStepSequence++;
                TaskService.State.Step step = new TaskService.State.Step();
                fillStep(step, stepEntity);
                taskServiceState.steps.add(step);
            }
        }

        com.vmware.xenon.common.Operation result = dcpClient.post(TaskServiceFactory.SELF_LINK, taskServiceState);
        TaskService.State createdState = result.getBody(TaskService.State.class);
        TaskEntity task = convertToTaskEntity(createdState);
        task.setSteps(stepEntities); // replacing steps to retain the transient properties
        logger.info("created task: {}", task);
        return task;
    }

    @Override
    public void markTaskAsStarted(TaskEntity task) throws TaskNotFoundException {
        logger.info("Task {} has been marked as STARTED", task.getId());

        TaskService.State taskServiceState = new TaskService.State();
        taskServiceState.state = TaskService.State.TaskState.STARTED;
        taskServiceState.startedTime = DateTime.now().toDate();

        patchTaskService(task.getId(), taskServiceState);
    }

    @Override
    public void markTaskAsDone(TaskEntity task) throws TaskNotFoundException {
        TaskService.State taskServiceState = new TaskService.State();
        taskServiceState.state = TaskService.State.TaskState.COMPLETED;
        taskServiceState.endTime = DateTime.now().toDate();
        patchTaskService(task.getId(), taskServiceState);
        logger.info("Task {} has been marked as COMPLETED", task.getId());
    }

    @Override
    public void markTaskAsFailed(TaskEntity task) throws TaskNotFoundException {
        TaskService.State taskServiceState = new TaskService.State();
        taskServiceState.state = TaskService.State.TaskState.ERROR;
        taskServiceState.endTime = DateTime.now().toDate();
        patchTaskService(task.getId(), taskServiceState);
        logger.info("Task {} has been marked as ERROR", task);
    }

    @Override
    public void markAllStepsAsFailed(TaskEntity taskEntity, Throwable t) throws TaskNotFoundException {
        taskEntity.setState(TaskEntity.State.ERROR);
        taskEntity.setEndTime(DateTime.now().toDate());

        if (taskEntity.getSteps() != null) {
            for (StepEntity stepEntity : taskEntity.getSteps()) {
                stepEntity.setState(StepEntity.State.ERROR);
                stepEntity.setEndTime(taskEntity.getEndTime());
                stepEntity.addException(t);
            }
        }

        TaskService.State task = convertToTask(taskEntity);
        patchTaskService(taskEntity.getId(), task);
    }

    @Override
    public void update(TaskEntity task) throws TaskNotFoundException {
        TaskService.State taskState = convertToTask(task);
        patchTaskService(task.getId(), taskState);
    }

    @Override
    public ResourceList<TaskEntity> getEntityTasks(Optional<String> entityId, Optional<String> entityKind,
            Optional<String> state, Optional<Integer> pageSize) throws InvalidQueryParamsException {

        ResourceList<TaskService.State> tasksDocuments = getEntityDocuments(entityId, entityKind, state, pageSize);
        return getTaskEntitiesFromDocuments(tasksDocuments);
    }

    @Override
    public ResourceList<TaskEntity> getEntityTasksPage(String pageLink) throws PageExpiredException {
        ServiceDocumentQueryResult queryResult = null;
        try {
            queryResult = dcpClient.queryDocumentPage(pageLink);
        } catch (DocumentNotFoundException e) {
            throw new PageExpiredException(pageLink);
        }

        ResourceList<TaskService.State> taskStates = PaginationUtils
                .xenonQueryResultToResourceList(TaskService.State.class, queryResult);

        return getTaskEntitiesFromDocuments(taskStates);
    }

    private void patchTaskService(String taskId, TaskService.State taskServiceState) throws TaskNotFoundException {
        try {
            dcpClient.patch(TaskServiceFactory.SELF_LINK + "/" + taskId, taskServiceState);
        } catch (DocumentNotFoundException e) {
            throw new TaskNotFoundException(taskId);
        }
    }

    private void patchTaskServiceWithStepUpdate(String taskId, TaskService.StepUpdate stepUpdate)
            throws TaskNotFoundException {
        try {
            dcpClient.patch(TaskServiceFactory.SELF_LINK + "/" + taskId, stepUpdate);
        } catch (DocumentNotFoundException e) {
            throw new TaskNotFoundException(taskId);
        }
    }

    private ResourceList<TaskEntity> getTaskEntitiesFromDocuments(ResourceList<TaskService.State> tasksDocuments) {

        ResourceList<TaskEntity> taskEntityList = new ResourceList<>();
        taskEntityList.setItems(
                tasksDocuments.getItems().stream().map(d -> convertToTaskEntity(d)).collect(Collectors.toList()));
        taskEntityList.setNextPageLink(tasksDocuments.getNextPageLink());
        taskEntityList.setPreviousPageLink(tasksDocuments.getPreviousPageLink());

        return taskEntityList;
    }

    private ResourceList<TaskService.State> getEntityDocuments(Optional<String> entityId,
            Optional<String> entityKind, Optional<String> state, Optional<Integer> pageSize)
            throws InvalidQueryParamsException {

        final ImmutableMap.Builder<String, String> termsBuilder = new ImmutableMap.Builder<>();

        if (entityId.isPresent() && !entityKind.isPresent()) {
            throw new InvalidQueryParamsException("Both entityId and entityKind params need to be specified.");
        }

        if (!entityId.isPresent() && entityKind.isPresent()) {
            throw new InvalidQueryParamsException("Both entityId and entityKind params need to be specified.");
        }

        if (entityId.isPresent()) {
            termsBuilder.put("entityId", entityId.get());
        }

        if (entityKind.isPresent()) {
            termsBuilder.put("entityKind", entityKind.get().toLowerCase());
        }

        if (state.isPresent()) {
            termsBuilder.put("state", state.get().toUpperCase());
        }

        ServiceDocumentQueryResult queryResult = dcpClient.queryDocuments(TaskService.State.class,
                termsBuilder.build(), pageSize, true);

        return PaginationUtils.xenonQueryResultToResourceList(TaskService.State.class, queryResult);
    }

    @Override
    public void delete(TaskEntity task) {
        dcpClient.delete(TaskServiceFactory.SELF_LINK + "/" + task.getId(), new TaskService.State());
    }

    @Override
    public TaskEntity findById(String id) throws TaskNotFoundException {
        TaskEntity task = getById(id);

        if (task == null) {
            throw new TaskNotFoundException(id);
        }

        return task;
    }

    @Override
    public Step toApiRepresentation(StepEntity stepEntity) {
        Step step = new Step();

        step.setSequence(stepEntity.getSequence());
        step.setQueuedTime(stepEntity.getQueuedTime());
        step.setStartedTime(stepEntity.getStartedTime());
        step.setEndTime(stepEntity.getEndTime());
        step.setOperation(stepEntity.getOperation().toString());
        step.setState(stepEntity.getState().toString());

        if (StringUtils.isNotBlank(stepEntity.getOptions())) {
            step.setOptions(getStepOptions(stepEntity));
        }

        for (StepErrorEntity error : stepEntity.getErrors()) {
            step.addError(error.toApiError());
        }

        for (StepWarningEntity warning : stepEntity.getWarnings()) {
            step.addWarning(warning.toApiError());
        }

        return step;
    }

    @Override
    public void update(StepEntity stepEntity) throws TaskNotFoundException {
        TaskService.State.Step step = new TaskService.State.Step();
        fillStep(step, stepEntity);
        TaskService.StepUpdate stepUpdate = new TaskService.StepUpdate(step);
        patchTaskServiceWithStepUpdate(stepEntity.getTask().getId(), stepUpdate);
    }

    @Override
    public StepEntity createQueuedStep(TaskEntity task, Operation operation) throws TaskNotFoundException {
        return createQueuedStep(task, new ArrayList<BaseEntity>(), operation, null);
    }

    @Override
    public StepEntity createQueuedStep(TaskEntity task, BaseEntity entity, Operation operation)
            throws TaskNotFoundException {
        return createQueuedStep(task, entity, operation, null);
    }

    @Override
    public StepEntity createQueuedStep(TaskEntity task, BaseEntity entity, Operation operation,
            Map<String, String> stepOptions) throws TaskNotFoundException {
        List<BaseEntity> entities = new ArrayList<>();
        if (entity != null) {
            entities.add(entity);
        }
        return createQueuedStep(task, entities, operation, stepOptions);
    }

    @Override
    public StepEntity createQueuedStep(TaskEntity task, List<BaseEntity> entities, Operation operation)
            throws TaskNotFoundException {
        return createQueuedStep(task, entities, operation, null);
    }

    @Override
    public StepEntity createQueuedStep(TaskEntity task, List<BaseEntity> entities, Operation operation,
            Map<String, String> stepOptions) throws TaskNotFoundException {
        return createStep(task, StepEntity.State.QUEUED, entities, operation, stepOptions);
    }

    @Override
    public StepEntity createCompletedStep(TaskEntity task, BaseEntity entity, Operation operation)
            throws TaskNotFoundException {
        List<BaseEntity> entities = new ArrayList<>();
        if (entity != null) {
            entities.add(entity);
        }
        return createStep(task, StepEntity.State.COMPLETED, entities, operation, null);
    }

    @Override
    public void markStepAsStarted(StepEntity stepEntity) throws TaskNotFoundException {
        stepEntity.setState(StepEntity.State.STARTED);
        stepEntity.setStartedTime(DateTime.now().toDate());
        update(stepEntity);
    }

    @Override
    public void markStepAsDone(StepEntity stepEntity) throws TaskNotFoundException {
        stepEntity.setState(StepEntity.State.COMPLETED);
        stepEntity.setEndTime(DateTime.now().toDate());
        update(stepEntity);
    }

    @Override
    public void markStepAsFailed(StepEntity stepEntity, Throwable t) throws TaskNotFoundException {
        stepEntity.setState(StepEntity.State.ERROR);
        stepEntity.setEndTime(DateTime.now().toDate());
        stepEntity.addException(t);
        update(stepEntity);
    }

    @Override
    public void addWarning(StepEntity stepEntity, Throwable t) throws TaskNotFoundException {
        logger.warn("Step {} has warning", stepEntity, t);

        stepEntity.addWarning(t);
        stepEntity.setEndTime(DateTime.now().toDate());
        update(stepEntity);
    }

    @Override
    public void addWarnings(StepEntity stepEntity, List<Throwable> warningList) throws TaskNotFoundException {
        for (Throwable t : warningList) {
            logger.warn("Step {} has warning", stepEntity, t);
            stepEntity.addWarning(t);
        }

        stepEntity.setEndTime(DateTime.now().toDate());
        update(stepEntity);
    }

    @Override
    public StepEntity getStepByTaskIdAndOperation(String taskId, Operation operation) throws TaskNotFoundException {
        TaskEntity taskEntity = findById(taskId);

        for (StepEntity stepEntity : taskEntity.getSteps()) {
            if (stepEntity.getOperation() == operation) {
                return stepEntity;
            }
        }
        return null;
    }

    @Override
    public TaskEntity getById(String id) {
        TaskService.State taskState = getTaskStateById(id);
        if (taskState == null) {
            return null;
        }
        return convertToTaskEntity(taskState);
    }

    @Override
    public void setTaskResourceProperties(TaskEntity task, String properties) throws TaskNotFoundException {
        TaskService.State taskServiceState = new TaskService.State();
        taskServiceState.resourceProperties = properties;

        patchTaskService(task.getId(), taskServiceState);
    }

    private TaskService.State getTaskStateById(String taskId) {
        com.vmware.xenon.common.Operation result;
        try {
            result = dcpClient.get(TaskServiceFactory.SELF_LINK + "/" + taskId);
        } catch (DocumentNotFoundException documentNotFoundException) {
            return null;
        }

        if (result == null) {
            return null;
        }

        return result.getBody(TaskService.State.class);
    }

    private TaskEntity convertToTaskEntity(TaskService.State taskState) {
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setId(ServiceUtils.getIDFromDocumentSelfLink(taskState.documentSelfLink));
        taskEntity.setEntityId(taskState.entityId);
        taskEntity.setEntityKind(taskState.entityKind);
        taskEntity.setQueuedTime(taskState.queuedTime);
        taskEntity.setStartedTime(taskState.startedTime);
        taskEntity.setEndTime(taskState.endTime);
        taskEntity.setProjectId(taskState.projectId);
        taskEntity.setOperation(taskState.operation);
        taskEntity.setResourceProperties(taskState.resourceProperties);

        switch (taskState.state) {
        case COMPLETED:
            taskEntity.setState(TaskEntity.State.COMPLETED);
            break;
        case STARTED:
            taskEntity.setState(TaskEntity.State.STARTED);
            break;
        case QUEUED:
            taskEntity.setState(TaskEntity.State.QUEUED);
            break;
        case ERROR:
            taskEntity.setState(TaskEntity.State.ERROR);
            break;
        default:
            String errorMessage = String.format("Unknown task state {%s} found for task link {%s}", taskState.state,
                    taskState.documentSelfLink);
            logger.error(errorMessage);
            throw new IllegalStateException(errorMessage);
        }

        taskEntity.setSteps(new ArrayList<StepEntity>());

        if (taskState.steps != null) {
            for (TaskService.State.Step step : taskState.steps) {
                StepEntity stepEntity = convertToStepEntity(taskEntity, step);
                taskEntity.addStep(stepEntity);
            }
        }
        return taskEntity;
    }

    private StepEntity convertToStepEntity(TaskEntity taskEntity, TaskService.State.Step step) {
        StepEntity stepEntity = new StepEntity();
        stepEntity.setTask(taskEntity);
        stepEntity.setSequence(step.sequence);
        stepEntity.setOperation(step.operation);

        switch (step.state) {
        case QUEUED:
            stepEntity.setState(StepEntity.State.QUEUED);
            break;
        case STARTED:
            stepEntity.setState(StepEntity.State.STARTED);
            break;
        case ERROR:
            stepEntity.setState(StepEntity.State.ERROR);
            break;
        case COMPLETED:
            stepEntity.setState(StepEntity.State.COMPLETED);
            break;
        default:
            String errorMessage = String.format("Unknown step state {%s} found for task id {%s}", step.state,
                    taskEntity.getId());
            logger.error(errorMessage);
            throw new IllegalStateException(errorMessage);
        }

        stepEntity.setStartedTime(step.startedTime);
        stepEntity.setQueuedTime(step.queuedTime);
        stepEntity.setEndTime(step.endTime);
        stepEntity.setOptions(step.options);

        if (step.errors != null) {
            for (TaskService.State.StepError error : step.errors) {
                StepErrorEntity stepErrorEntity = new StepErrorEntity();
                fillStepErrorBaseEntity(stepErrorEntity, error);
                stepEntity.getErrors().add(stepErrorEntity);
            }
        }

        if (step.warnings != null) {
            for (TaskService.State.StepError warning : step.warnings) {
                StepWarningEntity stepErrorWarning = new StepWarningEntity();
                fillStepErrorBaseEntity(stepErrorWarning, warning);
                stepEntity.getWarnings().add(stepErrorWarning);
            }
        }

        if (step.resources != null) {
            for (TaskService.State.StepResource stepResource : step.resources) {
                stepEntity.getResources().add(convertToStepResourceEntity(stepResource));
            }
        }

        return stepEntity;
    }

    private void fillStepErrorBaseEntity(StepErrorBaseEntity stepErrorBaseEntity,
            TaskService.State.StepError stepError) {
        stepErrorBaseEntity.setCode(stepError.code);
        stepErrorBaseEntity.setMessage(stepError.message);
        stepErrorBaseEntity.setData(stepError.data);
    }

    private StepResourceEntity convertToStepResourceEntity(TaskService.State.StepResource stepResource) {
        StepResourceEntity stepResourceEntity = new StepResourceEntity();
        stepResourceEntity.setEntityId(stepResource.resourceId);
        stepResourceEntity.setEntityKind(stepResource.resourceKind);
        return stepResourceEntity;
    }

    private TaskService.State convertToTask(TaskEntity taskEntity) {

        TaskService.State taskState = new TaskService.State();
        taskState.entityId = taskEntity.getEntityId();
        taskState.entityKind = taskEntity.getEntityKind();
        taskState.queuedTime = taskEntity.getQueuedTime();
        taskState.startedTime = taskEntity.getStartedTime();
        taskState.endTime = taskEntity.getEndTime();
        taskState.projectId = taskEntity.getProjectId();
        taskState.operation = taskEntity.getOperation();

        switch (taskEntity.getState()) {
        case COMPLETED:
            taskState.state = TaskService.State.TaskState.COMPLETED;
            break;
        case STARTED:
            taskState.state = TaskService.State.TaskState.STARTED;
            break;
        case QUEUED:
            taskState.state = TaskService.State.TaskState.QUEUED;
            break;
        case ERROR:
            taskState.state = TaskService.State.TaskState.ERROR;
            break;
        default:
            String errorMessage = String.format("Unknown task state found in taskEntity {%s}", taskEntity);
            logger.error(errorMessage);
            throw new IllegalArgumentException(errorMessage);
        }

        if (taskEntity.getSteps() == null || taskEntity.getSteps().size() <= 0) {
            return taskState;
        }

        taskState.steps = new ArrayList<>();

        for (StepEntity stepEntity : taskEntity.getSteps()) {
            TaskService.State.Step step = new TaskService.State.Step();
            fillStep(step, stepEntity);
            taskState.steps.add(step);
        }

        return taskState;
    }

    private void fillStep(TaskService.State.Step step, StepEntity stepEntity) {
        step.operation = stepEntity.getOperation();
        step.sequence = stepEntity.getSequence();
        switch (stepEntity.getState()) {
        case QUEUED:
            step.state = TaskService.State.StepState.QUEUED;
            break;
        case STARTED:
            step.state = TaskService.State.StepState.STARTED;
            break;
        case ERROR:
            step.state = TaskService.State.StepState.ERROR;
            break;
        case COMPLETED:
            step.state = TaskService.State.StepState.COMPLETED;
            break;
        default:
            String errorMessage = String.format("Unknown step state {%s} found for task id {%s}", step.state,
                    stepEntity.getTask().getId());
            logger.error(errorMessage);
            throw new IllegalStateException(errorMessage);
        }

        step.startedTime = stepEntity.getStartedTime();
        step.queuedTime = stepEntity.getQueuedTime();
        step.endTime = stepEntity.getEndTime();
        step.options = stepEntity.getOptions();

        if (stepEntity.getErrors() != null) {
            step.errors = new ArrayList<>();
            for (StepErrorBaseEntity stepErrorBaseEntity : stepEntity.getErrors()) {
                step.errors.add(convertToStepError(stepErrorBaseEntity));
            }
        }

        if (stepEntity.getWarnings() != null) {
            step.warnings = new ArrayList<>();
            for (StepErrorBaseEntity stepErrorBaseEntity : stepEntity.getWarnings()) {
                step.warnings.add(convertToStepError(stepErrorBaseEntity));
            }
        }

        if (stepEntity.getResources() != null) {
            step.resources = new ArrayList<>();
            for (StepResourceEntity stepResourceEntity : stepEntity.getResources()) {
                step.resources.add(convertToStepResource(stepResourceEntity));
            }
        }
    }

    private TaskService.State.StepError convertToStepError(StepErrorBaseEntity stepErrorBaseEntity) {
        TaskService.State.StepError stepError = new TaskService.State.StepError();
        stepError.code = stepErrorBaseEntity.getCode();
        stepError.message = stepErrorBaseEntity.getMessage();
        stepError.data = stepErrorBaseEntity.getData();
        return stepError;
    }

    private TaskService.State.StepResource convertToStepResource(StepResourceEntity stepResourceEntity) {
        TaskService.State.StepResource stepResource = new TaskService.State.StepResource();
        stepResource.resourceId = stepResourceEntity.getEntityId();
        stepResource.resourceKind = stepResourceEntity.getEntityKind();
        return stepResource;
    }

    private ResourceList<Task> toApiRepresentation(ResourceList<TaskEntity> taskEntities) {
        ResourceList<Task> result = new ResourceList<>();
        result.setItems(
                taskEntities.getItems().stream().map(t -> toApiRepresentation(t)).collect(Collectors.toList()));
        result.setNextPageLink(taskEntities.getNextPageLink());
        result.setPreviousPageLink(taskEntities.getPreviousPageLink());

        return result;
    }

    private Task toApiRepresentation(TaskEntity taskEntity) {
        Task task = new Task();

        task.setId(taskEntity.getId());
        task.setQueuedTime(taskEntity.getQueuedTime());
        task.setStartedTime(taskEntity.getStartedTime());
        task.setEndTime(taskEntity.getEndTime());
        task.setOperation(taskEntity.getOperation().toString());
        task.setState(taskEntity.getState().toString());

        Task.Entity entity = new Task.Entity();
        entity.setId(taskEntity.getEntityId());
        entity.setKind(taskEntity.getEntityKind());
        task.setEntity(entity);

        if (StringUtils.isNotBlank(taskEntity.getResourceProperties())) {
            try {
                Object resourceProperties = objectMapper.readValue(taskEntity.getResourceProperties(),
                        Object.class);
                task.setResourceProperties(resourceProperties);
            } catch (IOException e) {
                logger.error("Error deserializing taskEntity resourceProperties {}",
                        taskEntity.getResourceProperties(), e);
                throw new IllegalArgumentException(
                        String.format("Error deserializing taskEntity resourceProperties %s, error %s",
                                taskEntity.getResourceProperties(), e.getMessage()));
            }
        }

        List<Step> steps = new ArrayList<>();
        Collections.sort(taskEntity.getSteps(), new Comparator<StepEntity>() {
            @Override
            public int compare(StepEntity s1, StepEntity s2) {
                return Integer.compare(s1.getSequence(), s2.getSequence());
            }
        });
        for (StepEntity stepEntity : taskEntity.getSteps()) {
            steps.add(toApiRepresentation(stepEntity));
        }
        task.setSteps(steps);

        return task;
    }

    private HashMap<String, String> getStepOptions(StepEntity stepEntity) {
        try {
            return objectMapper.readValue(stepEntity.getOptions(), HashMap.class);
        } catch (IOException e) {
            logger.error("Error deserializing step {} options", stepEntity.getId(), e);
            throw new IllegalArgumentException(
                    String.format("Error deserializing step %s options: %s", stepEntity.getId(), e.getMessage()));
        }
    }

    private String convertStepOptionsToString(Map<String, String> stepOptions, TaskEntity taskEntity) {
        if (stepOptions != null && !stepOptions.isEmpty()) {
            try {
                return objectMapper.writeValueAsString(stepOptions);
            } catch (JsonProcessingException e) {
                throw new IllegalArgumentException(
                        String.format("Error serializing step options: {%s} for task id {%s}", e.getMessage(),
                                taskEntity.getId()));
            }
        }

        return null;
    }

    private StepEntity createStep(TaskEntity taskEntity, StepEntity.State state, List<BaseEntity> entities,
            Operation operation, Map<String, String> stepOptions) throws TaskNotFoundException {
        TaskService.State task = getTaskStateById(taskEntity.getId());
        if (task.steps == null) {
            task.steps = new ArrayList<>();
        }

        TaskService.State.Step step = new TaskService.State.Step();

        step.sequence = taskEntity.getNextStepSequence();

        switch (state) {
        case QUEUED:
            step.state = TaskService.State.StepState.QUEUED;
            break;
        case STARTED:
            step.state = TaskService.State.StepState.STARTED;
            break;
        case ERROR:
            step.state = TaskService.State.StepState.ERROR;
            break;
        case COMPLETED:
            step.state = TaskService.State.StepState.COMPLETED;
            break;
        default:
            String errorMessage = String.format("Unknown step state {%s} passed in createStep for task id {%s}",
                    state, taskEntity.getId());
            logger.error(errorMessage);
            throw new IllegalArgumentException(errorMessage);
        }

        step.operation = operation;
        Date currentTime = DateTime.now().toDate();
        step.queuedTime = currentTime;
        step.options = convertStepOptionsToString(stepOptions, taskEntity);

        if (StepEntity.State.COMPLETED.equals(state)) {
            step.startedTime = currentTime;
            step.endTime = currentTime;
        }

        if (entities != null) {
            step.resources = new ArrayList<>();
            for (BaseEntity entity : entities) {
                TaskService.State.StepResource stepResource = new TaskService.State.StepResource();
                stepResource.resourceId = entity.getId();
                stepResource.resourceKind = entity.getKind();
                step.resources.add(stepResource);
            }
        }

        task.steps.add(step);

        patchTaskService(taskEntity.getId(), task);

        StepEntity stepEntity = convertToStepEntity(taskEntity, step);
        stepEntity.setTask(taskEntity);
        taskEntity.addStep(stepEntity);

        //populate entity with transient values that are not persisted in dcp document.

        if (entities != null) {
            for (BaseEntity entity : entities) {
                stepEntity.addTransientResourceEntity(entity);
            }
        }

        logger.info("created step: {}", stepEntity);
        return stepEntity;
    }
}