cancel
Showing results for 
Search instead for 
Did you mean: 

How to update task property of type NodeRef using REST API?

eugen_p
Champ on-the-rise
Champ on-the-rise

Note: This is a clone of my original post located in the wrong forum: How to update task property of type NodeRef using REST API? 

Hi,

 

I'm working on a project which integrates with Alfresco v4.2.e and Activiti v5.13.

I use the REST API to query for tasks through the task-instance service.

When a property is a (collection of) NodeRef, It returns a (collection of) string ("workspace://....").
When I try to update this property, Alfresco throws a ClassCastException saying that String cannot be cast to NodeRef.

I found this bug on your jira https://community.alfresco.com/external-link.jspa?url=https%3A%2F%2Fissues.alfresco.com%2Fjira%2Fbro...   which seems to be related to my problem but in the other direction. Is my problem a bug? Is this a bug fixed in the v4.2.f? Did I do something wrong?

 

Anyway I decided to try the activiti-rest webapp to query directly Activiti. So I searched the war for the 5.13.

Unfortunately the Activiti website doesn't provide a long history of binaries. I had to build the war using the sources from github. When I deployed the compiled war to a Tomcat and tried to get variables through the runtime/tasks service, It throws a ActivitiException "unknown variable type name alfrescoScriptNodeList". Indeed in some Maven repo I've seen some Activiti jars suffixed by "alf". Did I built the wrong war? Is there a fork of the project?

 

Finally I could write a webscript in Alfresco using the Alfresco Java API but it would be the last option...

 

Sorry for the length of this text and thanks in advance for your answers.

4 REPLIES 4

eugen_p
Champ on-the-rise
Champ on-the-rise

No one has an answer? I can't imagine I'm the only one who would like to update a nodeRef inside a task using REST...

resplin
Elite Collaborator
Elite Collaborator

It isn't really clear what you are trying to do. 

  • What is your goal? I'm not clear on which REST API you are trying to access, Activiti or Alfresco Community Edition.
  • What did you try? A small section of code is often helpful here.
  • What happened? What is the exact error, and where was it thrown?

That might help people assist you.

eugen_p
Champ on-the-rise
Champ on-the-rise

You're right I missed some crucial information. My apologies

I use the Workflow repository from the Alfresco Community Edition 4.2.e Rest API, documented here: Workflow | Alfresco Documentation

To get a specific workflow instance, I use the endpoint documented here: Get workflow instance | Alfresco Documentation. Some task instances of a workflow instance have a property of type NodeRef.

In the JSON response, a NodeRef property is represented as follow:

"taskPropertyName":
         [
            "workspace:\/\/SpacesStore\/5f654948-8a55-4b46-8bb6-9b8e5db7cdec"
         ]

Now I want to update some properties of a task instance. For that I use the endpoint documented here: Updates workflow task instance | Alfresco Documentation.

The task instance is well updated except if I include a property of type NodeRef.

In that case I receive a status code 500 from the Alfresco REST API. When I check the logs of Alfresco, I see this stacktrace:

Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to org.alfresco.service.cmr.repository.NodeRef
        at org.alfresco.repo.workflow.activiti.ActivitiNodeConverter.convertNodes(ActivitiNodeConverter.java:59)
        at org.alfresco.repo.workflow.AbstractWorkflowNodeConverter.convertNodes(AbstractWorkflowNodeConverter.java:71)
        at org.alfresco.repo.workflow.AbstractWorkflowNodeConverter.convertNodes(AbstractWorkflowNodeConverter.java:47)
        at org.alfresco.repo.workflow.AbstractWorkflowPropertyHandler.convertAssociationValue(AbstractWorkflowPropertyHandler.java:106)
        at org.alfresco.repo.workflow.AbstractWorkflowPropertyHandler.handleAssociation(AbstractWorkflowPropertyHandler.java:60)
        at org.alfresco.repo.workflow.AbstractWorkflowPropertyHandler.handleDefaultProperty(AbstractWorkflowPropertyHandler.java:166)
        at org.alfresco.repo.workflow.DefaultWorkflowPropertyHandler.handleProperty(DefaultWorkflowPropertyHandler.java:38)
        at org.alfresco.repo.workflow.WorkflowPropertyHandlerRegistry.handleVariablesToSet(WorkflowPropertyHandlerRegistry.java:75)
        at org.alfresco.repo.workflow.activiti.properties.ActivitiPropertyConverter.setTaskProperties(ActivitiPropertyConverter.java:789)
        at org.alfresco.repo.workflow.activiti.properties.ActivitiPropertyConverter.updateTask(ActivitiPropertyConverter.java:1050)
        at org.alfresco.repo.workflow.activiti.ActivitiWorkflowEngine.updateTask(ActivitiWorkflowEngine.java:2167)
        at org.alfresco.repo.workflow.WorkflowServiceImpl.updateTask(WorkflowServiceImpl.java:951)

In the PUT HTTP method, I set the property of type NodeRef as follow:

"taskPropertyName":["workspace://SpacesStore/f7be95e4-3b0b-4519-bdb6-06c2dd9685cf"]

I talked about the Activiti REST API as a workaround: I installed it and configured with the same database as the Activiti bundled with Alfresco to see if I could update task instances by this way. But it's not a good idea.

I hope I have been clearer and thanks for your help.

eugen_p
Champ on-the-rise
Champ on-the-rise

As a workaround I ended up writing a Webscript which do the same as the Alfresco one but with NodeRef handling.
I'ts a bit dirty as I had to parse the string to see if it starts with "workspace://.."

Anyway I'm always interested if someone has the solution or a better workaround.

Here's the code :

public class UpdateTaskProperties extends AbstractWebScript {

    private static final String TASK_ID_PARAM = "taskId";
    private static Logger LOGGER = LoggerFactory.getLogger(UpdateTaskProperties.class);

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final NodeRefObjectMapper nodeRefObjectMapper = new NodeRefObjectMapper();

    public UpdateTaskProperties() {
    }

    @Override
    public void execute(WebScriptRequest request, WebScriptResponse response) throws IOException {
        String taskId = getTaskIdIn(request);

        Map<String, Object> properties = objectMapper.readValue(request.getContent().getReader(), Map.class);

        Map<String, Object> finalProperties = collectCastedPropertiesFrom(properties);

        LOGGER.debug("Updating " + finalProperties.size() + " properties of task " + taskId + "...");

        taskService.setVariablesLocal(taskId, finalProperties);

        LOGGER.info("Updated " + finalProperties.size() + " properties of task " + taskId);
    }

    private String getTaskIdIn(WebScriptRequest request) {
        String taskId = request.getParameter(TASK_ID_PARAM);
        isTrue(isNotBlank(taskId), "Invalid task id");
        return taskId;
    }

    private Map<String, Object> collectCastedPropertiesFrom(Map<String, Object> properties) {
        Map<String, Object> finalProperties = newHashMap();
        for (String propertyName : properties.keySet()) {
            Object value = properties.get(propertyName);
            if (nodeRefObjectMapper.isNodeRef(value)) {
                finalProperties.put(propertyName, nodeRefObjectMapper.readValue(value));
            } else if (nodeRefObjectMapper.isNodeRefCollection(value)) {
                finalProperties.put(propertyName, nodeRefObjectMapper.readValues(value));
            } else {
                finalProperties.put(propertyName, value);
            }
        }
        return finalProperties;
    }
}

public class NodeRefObjectMapper {

    private static final String WORKSPACE_PREFIX = "workspace://SpacesStore/";
    private static final String NOTHING = "";

    public NodeRef readValue(Object value) {
        isTrue(isNodeRef(value), "Not a NodeRef object");
        return toNodeRef((String) value);
    }

    public Collection<NodeRef> readValues(Object value) {
        isTrue(isNodeRefCollection(value), "Not a NodeRef collection object");
        return from((Collection<String>) value)
                .transform(s -> toNodeRef(s)).toList();
    }

    public boolean isNodeRef(Object value) {
        return value instanceof String && normalize((String) value).startsWith(WORKSPACE_PREFIX);
    }

    public boolean isNodeRefCollection(Object value) {
        return value instanceof Collection
                && !((Collection) value).isEmpty()
                && isNodeRef(((Collection) value).iterator().next());
    }

    private NodeRef toNodeRef(String value) {
        return new NodeRef(STORE_REF_WORKSPACE_SPACESSTORE, toNodeRefId(value));
    }

    private String toNodeRefId(String nodeRef) {
        return normalize(nodeRef).replace(WORKSPACE_PREFIX, NOTHING);
    }

    private String normalize(String value) {
        return value.replace("\\", "");
    }
}