com.centurylink.mdw.service.rest.Tasks Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mdw-services Show documentation
Show all versions of mdw-services Show documentation
MDW is a workflow framework specializing in microservice orchestration
/*
* Copyright (C) 2017 CenturyLink, Inc.
*
* 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.centurylink.mdw.service.rest;
import com.centurylink.mdw.cache.asset.PackageCache;
import com.centurylink.mdw.common.service.Query;
import com.centurylink.mdw.common.service.ServiceException;
import com.centurylink.mdw.common.service.types.StatusMessage;
import com.centurylink.mdw.dataaccess.DataAccessException;
import com.centurylink.mdw.model.*;
import com.centurylink.mdw.model.event.Event;
import com.centurylink.mdw.model.event.EventLog;
import com.centurylink.mdw.model.listener.Listener;
import com.centurylink.mdw.model.task.*;
import com.centurylink.mdw.model.user.Role;
import com.centurylink.mdw.model.user.User;
import com.centurylink.mdw.model.user.UserAction.Entity;
import com.centurylink.mdw.model.user.UserList;
import com.centurylink.mdw.model.workflow.Package;
import com.centurylink.mdw.model.workflow.ProcessInstance;
import com.centurylink.mdw.service.data.task.TaskDataAccess;
import com.centurylink.mdw.service.data.user.UserGroupCache;
import com.centurylink.mdw.services.ServiceLocator;
import com.centurylink.mdw.services.TaskServices;
import com.centurylink.mdw.services.WorkflowServices;
import com.centurylink.mdw.services.rest.JsonRestService;
import com.centurylink.mdw.services.task.AllowableTaskActions;
import com.centurylink.mdw.task.types.TaskList;
import com.centurylink.mdw.util.JsonUtil;
import com.centurylink.mdw.util.log.LoggerUtil;
import com.centurylink.mdw.util.log.StandardLogger;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.xmlbeans.XmlException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.ws.rs.Path;
import java.io.IOException;
import java.text.ParseException;
import java.time.Instant;
import java.util.*;
@Path("/Tasks")
@Api("Task instances")
public class Tasks extends JsonRestService implements JsonExportable {
private static StandardLogger logger = LoggerUtil.getStandardLogger();
private TaskServices getTaskServices() {
return ServiceLocator.getTaskServices();
}
/**
* Retrieve a task or list of tasks, or subData for a task instance.
*/
@Override
@Path("/{instanceId}/{subData}")
@ApiOperation(value = "Retrieve a task instance or a page of task instances", notes = "If taskInstanceId is not present, returns a page of task instances. "
+ "If subData is not present, returns task summary info. "
+ "Options for subData: 'values', 'indexes', 'history', 'actions', 'subtasks'", response = Value.class, responseContainer = "List")
public JSONObject get(String path, Map headers)
throws ServiceException, JSONException {
TaskServices taskServices = ServiceLocator.getTaskServices();
try {
Query query = getQuery(path, headers);
String userCuid = headers.get(Listener.AUTHENTICATED_USER_HEADER);
String segOne = getSegment(path, 1);
if (segOne == null) {
// task list query
TaskList tasks = taskServices.getTasks(query, userCuid);
return tasks.getJson();
}
else {
if (segOne.equals("templates")) {
boolean grouped = query.getBooleanFilter("grouped");
if (grouped) {
return getPackageTemplatesJson(getTaskServices().getTaskTemplatesByPackage(query));
}
return getTemplates(query).getJson();
}
else if (segOne.equals("categories")) {
return getCategories(query).getJson();
}
else if (segOne.equals("bulkActions")) {
return getBulkActions(query).getJson();
}
else if (segOne.equals("assignees")) {
return getPotentialAssignees(query, userCuid).getJson();
}
else if (segOne.equals("tops")) {
return getTops(query).getJson();
}
else if (segOne.equals("breakdown")) {
return getBreakdown(query).getJson();
}
else {
// must be instance id
try {
Long instanceId = Long.parseLong(segOne);
TaskInstance taskInstance;
String extra = getSegment(path, 2);
if (extra == null) {
taskInstance = taskServices.getInstance(instanceId);
if (taskInstance == null)
throw new ServiceException(HTTP_404_NOT_FOUND,
"Task instance not found: " + instanceId);
if (taskInstance.isProcessOwned()) {
ProcessInstance procInst = ServiceLocator.getWorkflowServices()
.getProcess(taskInstance.getOwnerId());
taskInstance.setProcessName(procInst.getProcessName());
taskInstance.setPackageName(procInst.getPackageName());
}
return taskInstance.getJson();
}
else if (extra.equals("values")) {
Map values = taskServices.getValues(instanceId);
JSONObject valuesJson = new JsonObject();
for (String name : values.keySet()) {
valuesJson.put(name, values.get(name).getJson());
}
return valuesJson;
}
else if (extra.equals("indexes")) {
Map indexes = taskServices.getIndexes(instanceId);
return JsonUtil.getJson(indexes);
}
else if (extra.equals("history")) {
List eventLogs = taskServices.getHistory(instanceId);
JSONObject json = new JsonObject();
if (eventLogs != null && eventLogs.size() > 0) {
JSONArray historyJson = new JSONArray();
for (EventLog log : eventLogs) {
historyJson.put(log.getJson());
}
json.put("taskHistory", historyJson);
}
return json;
}
else if (extra.equals("actions")) {
JSONArray jsonTaskActions = new JSONArray();
for (TaskAction taskAction : taskServices.getActions(instanceId, userCuid, query)) {
jsonTaskActions.put(taskAction.getJson());
}
return new JsonArray(jsonTaskActions).getJson();
}
else if (extra.equals("subtasks")) {
TaskList subtasks = taskServices.getSubtasks(instanceId);
return subtasks.getJson();
}
else {
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Invalid subpath: " + extra);
}
}
catch (NumberFormatException ex) {
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Invalid task instance id: " + segOne);
}
}
}
}
catch (ServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new ServiceException(ex.getMessage(), ex);
}
}
@Override
@Path("/{instanceId}")
@ApiOperation(value = "Update a task instance, update an instance's index values, or register to wait for an event", response = StatusMessage.class, notes = "If indexes is present, body is TaskIndexes; if regEvent is present, body is Event; otherwise body is a Task."
+ "If subData is not present, updates task summary info. Options for subData: values, indexes, regEvent")
@ApiImplicitParams({
@ApiImplicitParam(name = "Task", paramType = "body", dataType = "com.centurylink.mdw.model.task.TaskInstance", value = "When no subData is specified"),
@ApiImplicitParam(name = "indexes", paramType = "body", dataType = "com.centurylink.mdw.model.task.TaskIndexes", value = "When {subData}=indexes"),
@ApiImplicitParam(name = "regEvent", paramType = "body", dataType = "com.centurylink.mdw.model.event.Event", value = "When {subData}=regEvent. Only the id (event name) field is mandatory in Event object. Optionally, a completionCode can specified - Default is FINISHED"),
@ApiImplicitParam(name = "values", paramType = "body", dataType = "java.lang.Object", value = "When {subData}=values. JSON object parseable into a key/value Map.") })
public JSONObject put(String path, JSONObject content, Map headers)
throws ServiceException, JSONException {
String id = getSegment(path, 1);
if (id == null)
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Missing path segment: {taskInstanceId}");
try {
Long instanceId = Long.parseLong(id);
String extra = getSegment(path, 2);
if (extra == null) {
// update task summary info
TaskInstance taskInstJson = new TaskInstance(content);
if (!content.has("id"))
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content is missing required field: id");
long contentInstanceId = content.getLong("id");
if (instanceId != contentInstanceId)
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content/path mismatch (instanceId): " + contentInstanceId + " is not: "
+ instanceId);
ServiceLocator.getTaskServices().updateTask(getAuthUser(headers), taskInstJson);
return null;
}
else if (extra.equals("values")) {
Map values = JsonUtil.getMap(content);
TaskServices taskServices = ServiceLocator.getTaskServices();
taskServices.applyValues(instanceId, values);
}
else if (extra.equals("indexes")) {
// update task indexes
TaskIndexes taskIndexes = new TaskIndexes(content);
if (instanceId != taskIndexes.getTaskInstanceId())
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content/path mismatch (instanceId): " + taskIndexes.getTaskInstanceId()
+ " is not: " + instanceId);
ServiceLocator.getTaskServices().updateIndexes(taskIndexes.getTaskInstanceId(),
taskIndexes.getIndexes());
if (logger.isDebugEnabled())
logger.debug("Updated task indexes for instance ID: "
+ taskIndexes.getTaskInstanceId());
return null;
}
else if (extra.equals("regEvent")) {
Event event = new Event(content);
WorkflowServices workflowServices = ServiceLocator.getWorkflowServices();
workflowServices.registerTaskWaitEvent(instanceId, event);
if (logger.isDebugEnabled())
logger.debug("Registered Event : [" + event.getId() + "]Task Instance Id = "
+ instanceId);
}
}
catch (NumberFormatException ex) {
throw new ServiceException(HTTP_400_BAD_REQUEST, "Invalid task instance id: " + id);
}
catch (ServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new ServiceException(ex.getMessage(), ex);
}
return null;
}
/**
* For creating a new task or performing task action(s). When performing
* actions: old way = path='Tasks/{instanceId}/{action}', new way =
* path='Tasks/{action}'. Where {action} is an actual valid task action like
* 'Claim'.
*/
@Override
@Path("/action")
@ApiOperation(value = "Create a task instance or perform an action on existing instance(s)",
notes = "If {action} is 'Create', then the body contains a task template logical Id; otherwise it contains a TaskAction to be performed.",
response = StatusMessage.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "TaskAction", paramType = "body", dataType = "com.centurylink.mdw.model.task.UserTaskAction") })
public JSONObject post(String path, JSONObject content, Map headers)
throws ServiceException, JSONException {
String segOne = getSegment(path, 1);
try {
TaskServices taskServices = ServiceLocator.getTaskServices();
if (segOne == null || segOne.equalsIgnoreCase("create")) {
// TODO: create a new task (asset path should drive template)
// TODO: title, dueDate
String title = null;
String comments = null;
Instant due = null;
if (content.has("masterTaskInstanceId")) {
// TODO: subtask instance
return null;
}
else {
// TODO: top-level task instance
return null;
}
}
else {
try {
// handle taskInstanceId as segOne and actual action as segTwo
long taskInstanceId = Long.parseLong(segOne);
TaskInstance taskInstance = taskServices.getInstance(taskInstanceId);
if (taskInstance == null)
throw new ServiceException(ServiceException.NOT_FOUND, "Task instance not found: " + taskInstanceId);
String segTwo = getSegment(path, 2);
if (segTwo == null)
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Missing {action} on request path, should be eg: /Tasks/{instanceId}/Claim");
try {
if (content.has("taskAction")
&& !content.getString("taskAction").equals(segTwo))
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content/path mismatch (action): '"
+ content.getString("taskAction") + "' is not: '"
+ segTwo + "'");
UserTaskAction taskAction = new UserTaskAction(content, segTwo);
if (taskAction.getTaskInstanceId() == null
|| taskInstanceId != taskAction.getTaskInstanceId())
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content/path mismatch (instanceId): "
+ taskAction.getTaskInstanceId() + " is not: "
+ taskInstanceId);
taskServices.performTaskAction(taskAction);
return null;
}
catch (IllegalArgumentException ex2) {
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Invalid task action: '" + segTwo + "'", ex2);
}
}
catch (NumberFormatException ex) {
// segOne must be the action
try {
UserTaskAction taskAction = new UserTaskAction(content, segOne);
if (!segOne.equals(taskAction.getTaskAction()))
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Content/path mismatch (action): '" + taskAction.getTaskAction()
+ "' is not: '" + segOne + "'");
taskServices.performTaskAction(taskAction);
return null;
}
catch (IllegalArgumentException ex2) {
throw new ServiceException(HTTP_400_BAD_REQUEST,
"Invalid task action: '" + segOne + "'", ex2);
}
}
}
}
catch (JSONException e) {
logger.error(e.getMessage(), e);
throw new ServiceException(e.getMessage(), e);
}
}
@Override
public Jsonable toExportJson(Query query, JSONObject json) throws JSONException {
try {
if (json.has(TaskList.TASKS))
return new TaskList(TaskList.TASKS, json);
else if ("Tasks/breakdown".equals(query.getPath()))
return new JsonListMap<>(json, TaskAggregate.class);
else
throw new JSONException("Unsupported export type for query: " + query);
}
catch (ParseException ex) {
throw new JSONException(ex);
}
}
@Override
public String getExportName() { return "Tasks"; }
@Override
public List getRoles(String path) {
List roles = super.getRoles(path);
roles.add(Role.TASK_EXECUTION);
return roles;
}
@Override
protected Entity getEntity(String path, Object content, Map headers) {
return Entity.Task;
}
private JSONObject getPackageTemplatesJson(Map> templates) {
JSONObject templatesJson = new JSONObject();
JSONArray pkgsArray = new JSONArray();
for (Map.Entry> entry : templates.entrySet()) {
JSONObject json = new JSONObject();
json.put("name", entry.getKey());
List taskVOs = entry.getValue();
Package taskPkg = PackageCache.getPackage(taskVOs.get(0).getPackageName());
json.put("version", taskPkg.getVersion().toString());
JSONArray jsonTasks = new JSONArray();
for (TaskTemplate taskVO : taskVOs) {
JSONObject jsonTask = new JsonObject();
jsonTask.put("taskId", taskVO.getId());
jsonTask.put("name", taskVO.getName());
jsonTask.put("version", taskVO.getVersionString());
jsonTask.put("logicalId", taskVO.getLogicalId());
jsonTasks.put(jsonTask);
}
json.put("templates", jsonTasks);
pkgsArray.put(json);
}
templatesJson.put("packages", pkgsArray);
return templatesJson;
}
@Path("/templates")
public JsonArray getTemplates(Query query) throws ServiceException {
List taskVOs = getTaskServices().getTaskTemplates(query);
JSONArray jsonTasks = new JSONArray();
for (TaskTemplate taskVO : taskVOs) {
JSONObject jsonTask = new JsonObject();
jsonTask.put("packageName", taskVO.getPackageName());
jsonTask.put("taskId", taskVO.getId());
jsonTask.put("name", taskVO.getTaskName());
jsonTask.put("version", taskVO.getVersionString());
jsonTask.put("logicalId", taskVO.getLogicalId());
jsonTasks.put(jsonTask);
}
return new JsonArray(jsonTasks);
}
@Path("/categories")
public JsonArray getCategories(Query query) {
Map categories = TaskDataAccess.getTaskRefData().getCategories();
List list = new ArrayList<>();
list.addAll(categories.values());
Collections.sort(list);
return JsonUtil.getJsonArray(list);
}
@Path("/bulkActions")
public JsonArray getBulkActions(Query query) throws IOException, XmlException {
boolean myTasks = query.getBooleanFilter("myTasks");
List taskActions;
if (myTasks)
taskActions = AllowableTaskActions.getMyTasksBulkActions();
else
taskActions = AllowableTaskActions.getWorkgroupTasksBulkActions();
JSONArray jsonTaskActions = new JSONArray();
for (TaskAction taskAction : taskActions) {
jsonTaskActions.put(taskAction.getJson());
}
return new JsonArray(jsonTaskActions);
}
@Path("/assignees")
public UserList getPotentialAssignees(Query query, String userCuid) throws DataAccessException {
// return potential assignees for all this user's workgroups
User user = UserGroupCache.getUser(userCuid);
return ServiceLocator.getUserServices()
.findWorkgroupUsers(user.getGroupNames(), query.getFind());
}
@Path("/tops")
public JsonArray getTops(Query query) throws ServiceException {
// dashboard top throughput query
List taskAggregates = getTaskServices().getTopTasks(query);
JSONArray taskArr = new JSONArray();
int ct = 0;
TaskAggregate other = null;
long otherTot = 0;
for (TaskAggregate taskAggregate : taskAggregates) {
if (ct >= query.getMax()) {
if (other == null) {
other = new TaskAggregate(0);
other.setName("Other");
}
otherTot += taskAggregate.getCount();
}
else {
taskArr.put(taskAggregate.getJson());
}
ct++;
}
if (other != null) {
other.setCount(otherTot);
taskArr.put(other.getJson());
}
return new JsonArray(taskArr);
}
@Path("/breakdown")
public JsonListMap getBreakdown(Query query) throws ServiceException {
TreeMap> instMap = getTaskServices().getTaskBreakdown(query);
LinkedHashMap> listMap = new LinkedHashMap<>();
for (Instant instant : instMap.keySet()) {
List taskAggregates = instMap.get(instant);
listMap.put(Query.getString(instant), taskAggregates);
}
return new JsonListMap<>(listMap);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy