/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.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.gnu.org/licenses/agpl.txt
*
* 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 se.streamsource.streamflow.web.rest;
import static se.streamsource.dci.value.table.TableValue.BOOLEAN;
import static se.streamsource.dci.value.table.TableValue.DATETIME;
import static se.streamsource.dci.value.table.TableValue.STRING;
import java.util.Collections;
import java.util.Date;
import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.injection.scope.Service;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.query.Query;
import org.qi4j.api.service.ServiceReference;
import org.qi4j.api.specification.Specification;
import org.qi4j.api.structure.Module;
import org.qi4j.api.util.Function;
import org.qi4j.api.util.Iterables;
import org.qi4j.api.value.ValueBuilder;
import org.qi4j.spi.Qi4jSPI;
import org.restlet.Request;
import org.restlet.data.Form;
import org.slf4j.LoggerFactory;
import se.streamsource.dci.restlet.server.ResultConverter;
import se.streamsource.dci.value.StringValue;
import se.streamsource.dci.value.link.LinkValue;
import se.streamsource.dci.value.link.LinksValue;
import se.streamsource.dci.value.table.TableBuilderFactory;
import se.streamsource.dci.value.table.TableQuery;
import se.streamsource.dci.value.table.TableValue;
import se.streamsource.streamflow.api.administration.priority.PriorityDTO;
import se.streamsource.streamflow.api.administration.priority.PriorityValue;
import se.streamsource.streamflow.api.workspace.SearchResultDTO;
import se.streamsource.streamflow.api.workspace.cases.CaseDTO;
import se.streamsource.streamflow.api.workspace.cases.CaseStates;
import se.streamsource.streamflow.web.application.knowledgebase.KnowledgebaseService;
import se.streamsource.streamflow.web.context.LinksBuilder;
import se.streamsource.streamflow.web.context.workspace.CaseSearchResult;
import se.streamsource.streamflow.web.domain.Describable;
import se.streamsource.streamflow.web.domain.Removable;
import se.streamsource.streamflow.web.domain.entity.caselog.CaseLogEntity;
import se.streamsource.streamflow.web.domain.entity.caze.CaseEntity;
import se.streamsource.streamflow.web.domain.entity.form.FieldEntity;
import se.streamsource.streamflow.web.domain.interaction.gtd.Assignee;
import se.streamsource.streamflow.web.domain.interaction.gtd.CaseId;
import se.streamsource.streamflow.web.domain.interaction.gtd.Owner;
import se.streamsource.streamflow.web.domain.interaction.gtd.Status;
import se.streamsource.streamflow.web.domain.structure.SubmittedFieldValue;
import se.streamsource.streamflow.web.domain.structure.casetype.CaseType;
import se.streamsource.streamflow.web.domain.structure.casetype.Resolution;
import se.streamsource.streamflow.web.domain.structure.caze.Case;
import se.streamsource.streamflow.web.domain.structure.form.SubmittedFormValue;
import se.streamsource.streamflow.web.domain.structure.label.Label;
import se.streamsource.streamflow.web.domain.structure.organization.Priority;
import se.streamsource.streamflow.web.domain.structure.organization.PrioritySettings;
/**
* JAVADOC
*/
public class StreamflowResultConverter
implements ResultConverter
{
@Structure
Module module;
@Structure
Qi4jSPI spi;
@Service
ServiceReference<KnowledgebaseService> knowledgeBaseService;
static final String STANDARD_COLUMNS = ",description,created,creator,caseid,href,owner,status,casetype,resolution,assigned,hascontacts,hasconversations,hasattachments,hassubmittedforms,labels,subcases,parent,";
public Object convert(Object result, Request request, Object[] arguments)
{
if (result instanceof String)
{
ValueBuilder<StringValue> builder = module.valueBuilderFactory().newValueBuilder(StringValue.class);
builder.prototype().string().set((String) result);
return builder.newInstance();
} else if (result instanceof Form)
{
return result;
} else if (result instanceof Case)
{
if (request.getResourceRef().getPath().contains( "workspacev2" ))
{
return caseDTO((CaseEntity) result, module, request.getResourceRef().getBaseRef().getPath(), true);
}
if (arguments.length > 0 && arguments[0] instanceof TableQuery)
return caseTable(Collections.singleton((CaseEntity) result), module, request, arguments);
else
//needs to be relative path in case there is a proxy in front of streamflow server
return caseDTO((CaseEntity) result, module, request.getResourceRef().getBaseRef().getPath(), false);
} else if (result instanceof CaseSearchResult)
{
if (request.getResourceRef().getPath().contains( "workspacev2" )) {
return buildCaseList((CaseSearchResult) result, module, request.getResourceRef().getBaseRef().getPath(), true);
}
else if (arguments.length > 0 && arguments[0] instanceof TableQuery) {
Iterable iterableCases = ((CaseSearchResult) result).getResult();
return caseTable(iterableCases, module, request, arguments);
}
else {
//same here relative path needed
return buildCaseList((CaseSearchResult) result, module, request.getResourceRef().getBaseRef().getPath(), false);
}
}
if (result instanceof Iterable)
{
Iterable iterable = (Iterable) result;
return new LinksBuilder(module.valueBuilderFactory()).rel("resource").addDescribables(iterable).newLinks();
}
return result;
}
private SearchResultDTO buildCaseList(CaseSearchResult result, Module module, String basePath, boolean v2)
{
ValueBuilder<SearchResultDTO> builder = module.valueBuilderFactory().newValueBuilder(SearchResultDTO.class);
builder.prototype().unlimitedResultCount().set(result.getUnlimitedCount());
for (Case aCase : result.getResult()) {
builder.prototype().links().get().add(caseDTO((CaseEntity) aCase, module, basePath, v2));
}
return builder.newInstance();
}
private CaseDTO caseDTO(CaseEntity aCase, Module module, String basePath, boolean v2)
{
ValueBuilder<CaseDTO> builder = module.valueBuilderFactory().newValueBuilder(CaseDTO.class);
CaseDTO prototype = builder.prototype();
prototype.id().set(aCase.identity().get());
prototype.creationDate().set(aCase.createdOn().get());
if (aCase.createdBy().get() != null)
prototype.createdBy().set(((Describable) aCase.createdBy().get()).getDescription());
if (aCase.caseId().get() != null)
prototype.caseId().set(aCase.caseId().get());
// Not so fancy solution to the v2 problem...
if (v2)
{
prototype.href().set(basePath + "/workspacev2/cases/" + aCase.identity().get() + "/");
}
else
{
prototype.href().set(basePath + "/workspace/cases/" + aCase.identity().get() + "/");
}
prototype.rel().set("case");
try
{
if (aCase.owner().get() != null) {
prototype.owner().set(((Describable) aCase.owner().get()).getDescription());
prototype.ownerId().set( aCase.owner().toString());
}
} catch (Exception e)
{
// Ignore
}
prototype.status().set(aCase.status().get());
prototype.text().set(aCase.description().get());
if (aCase.caseType().get() != null)
{
ValueBuilder<LinkValue> linkBuilder = module.valueBuilderFactory().newValueBuilder(LinkValue.class);
linkBuilder.prototype().text().set(aCase.caseType().get().getDescription());
linkBuilder.prototype().id().set(aCase.caseType().get().toString());
if (knowledgeBaseService.isAvailable())
{
try
{
linkBuilder.prototype().href().set(knowledgeBaseService.get().createURL((EntityComposite) aCase.caseType().get()));
} catch (Exception e)
{
LoggerFactory.getLogger(getClass()).error("Could not create link for case type:" + aCase.caseType().get().getDescription(), e);
}
} else
{
// TODO What to do here?
linkBuilder.prototype().href().set("");
}
prototype.caseType().set(linkBuilder.newInstance());
}
if (aCase.isAssigned()) {
prototype.assignedTo().set(((Describable) aCase.assignedTo().get()).getDescription());
prototype.listType().set( "assignments" );
} else {
prototype.listType().set( "inbox" );
}
if (aCase.isStatus(CaseStates.CLOSED) && aCase.resolution().get() != null)
prototype.resolution().set(aCase.resolution().get().getDescription());
prototype.hasContacts().set(aCase.hasContacts());
prototype.hasConversations().set(aCase.hasConversations());
prototype.hasSubmittedForms().set(aCase.hasSubmittedForms());
prototype.hasAttachments().set(aCase.hasAttachments());
prototype.hasUnreadConversation().set( aCase.hasUnreadConversation() );
prototype.hasUnreadForm().set( aCase.hasUnreadForm() );
prototype.unread().set( aCase.unread().get() );
prototype.location().set( aCase.location().get() );
// Labels
LinksBuilder labelsBuilder = new LinksBuilder(module.valueBuilderFactory()).command("delete");
for (Label label : aCase.labels())
{
labelsBuilder.addDescribable(label);
}
prototype.labels().set(labelsBuilder.newLinks());
// Subcases
LinksBuilder subcasesBuilder = new LinksBuilder(module.valueBuilderFactory());
subcasesBuilder.path(basePath + "/workspace/cases");
try
{
for (Case subCase : aCase.subCases())
{
subcasesBuilder.classes(((Status.Data) subCase).status().get().name());
subcasesBuilder.addDescribable(subCase);
}
} catch (Exception e)
{
e.printStackTrace();
}
prototype.restricted().set( aCase.restricted().get() );
prototype.lastLogEntryTime().set(new Date(spi.getEntityState((CaseLogEntity) aCase.caselog().get()).lastModified()));
prototype.dueOn().set(aCase.dueOn().get());
if (aCase.casepriority().get() != null)
{
Priority priority = aCase.casepriority().get();
ValueBuilder<PriorityDTO> priorityDTOBuilder = module.valueBuilderFactory().newValueBuilder(PriorityDTO.class);
priorityDTOBuilder.prototype().text().set(priority.getDescription());
priorityDTOBuilder.prototype().id().set(EntityReference.getEntityReference( priority ).identity());
priorityDTOBuilder.prototype().priority().set(((PrioritySettings.Data)priority).priority().get());
prototype.priority().set(priorityDTOBuilder.newInstance());
}
return builder.newInstance();
}
private TableValue caseTable(Iterable<CaseEntity> cases, final Module module, final Request request, Object[] arguments)
{
//needs relative path in case there is a proxy in front of streamflow server
final String basePath = request.getResourceRef().getBaseRef().getPath();
TableQuery query = (TableQuery) arguments[0];
TableBuilderFactory tbf = new TableBuilderFactory(module.valueBuilderFactory());
TableBuilderFactory tableBuilderFactory = tbf.column("description", "Description", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.getDescription();
}
}).
column( "created", "Created", DATETIME, new Function<CaseEntity, Object>()
{
public Object map( CaseEntity caseEntity )
{
return caseEntity.createdOn().get();
}
} ).
column( "creator", "Creator", STRING, new Function<CaseEntity, Object>()
{
public Object map( CaseEntity caseEntity )
{
return ((Describable) caseEntity.createdBy().get()).getDescription();
}
} ).
column( "due", "Due on", DATETIME, new Function<CaseEntity, Object>()
{
public Object map( CaseEntity caseEntity )
{
return caseEntity.dueOn().get();
}
} ).
column("caseid", "Case id", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.caseId().get();
}
}).
column("href", "Location", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return basePath + "/workspace/cases/" + caseEntity.identity().get() + "/";
}
}, new Function<CaseEntity, String>()
{
public String map(CaseEntity caseEntity)
{
return "View case";
}
}
).
column("owner", "Owner", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.owner().get();
}
}, new Function<CaseEntity, String>()
{
public String map(CaseEntity caseEntity)
{
Owner owner = caseEntity.owner().get();
return owner == null ? null : ((Describable) owner).getDescription();
}
}
).
column("status", "Status", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.status().get().name();
}
}).
column("casetype", "Case type", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
CaseType caseType = caseEntity.caseType().get();
return caseType == null ? null : caseType.getDescription();
}
}).
column("resolution", "Resolution", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
Resolution resolution = caseEntity.resolution().get();
return resolution == null ? null : resolution.getDescription();
}
}).
column("assigned", "Assigned", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
Assignee assignee = caseEntity.assignedTo().get();
return assignee == null ? null : ((Describable) assignee).getDescription();
}
}).
column("hascontacts", "Has contacts", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasContacts();
}
}).
column("hasconversations", "Has conversations", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasConversations();
}
}).
column("hasunreadconversation", "Has unread conversations", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasUnreadConversation();
}
}).
column("hasattachments", "Has attachments", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasAttachments();
}
}).
column("hassubmittedforms", "Has submitted forms", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasSubmittedForms();
}
}).
column("hasunreadform", "Has unread submitted form", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.hasUnreadForm();
}
}).
column("labels", "Labels", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
LinksBuilder labelsBuilder = new LinksBuilder(module.valueBuilderFactory()).command("delete");
for (Label label : caseEntity.labels())
{
labelsBuilder.addDescribable(label);
}
LinksValue linksValue = labelsBuilder.newLinks();
return linksValue;
}
}).
column("subcases", "Subcases", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
LinksBuilder subcasesBuilder = new LinksBuilder(module.valueBuilderFactory());
subcasesBuilder.path(basePath + "/workspace/cases");
try
{
for (Case subCase : caseEntity.subCases())
{
subcasesBuilder.classes(((Status.Data) subCase).status().get().name());
subcasesBuilder.addDescribable(subCase);
}
} catch (Exception e)
{
e.printStackTrace();
}
LinksValue linksValue = subcasesBuilder.newLinks();
return linksValue;
}
}).
column("parent", "Parent case", STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
Case parentCase = caseEntity.parent().get();
if (parentCase != null)
{
ValueBuilder<LinkValue> linkBuilder = module.valueBuilderFactory().newValueBuilder(LinkValue.class);
linkBuilder.prototype().id().set(parentCase.toString());
linkBuilder.prototype().rel().set("parent");
linkBuilder.prototype().href().set(parentCase.toString() + "/");
linkBuilder.prototype().text().set(((CaseId.Data) parentCase).caseId().get());
return linkBuilder.newInstance();
} else
return null;
}
}).
column( "removed", "Marked for delete", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map( CaseEntity caseEntity )
{
return ((Removable.Data)caseEntity).removed().get();
}
} ).
column( "priority", "Priority", STRING, new Function<CaseEntity, Object>()
{
public Object map( CaseEntity caseEntity )
{
if( caseEntity.casepriority().get() != null )
{
Priority priority = caseEntity.casepriority().get();
ValueBuilder<PriorityValue> builder = module.valueBuilderFactory().newValueBuilder( PriorityValue.class );
builder.prototype().id().set( EntityReference.getEntityReference( priority ).identity() );
builder.prototype().priority().set( ((PrioritySettings.Data)priority).priority().get() );
builder.prototype().href().set( "priority" );
builder.prototype().color().set( ((PrioritySettings.Data)priority).color().get() );
builder.prototype().text().set( priority.getDescription() );
return builder.newInstance();
}
return null;
}
} ).
column("unread", "Is unread", BOOLEAN, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
return caseEntity.isUnread();
}
});
if (!(query.select().size() == 1 && "*".equals( query.select().get(0) )))
{
for(String name : query.select())
{
final String fieldName = name.trim();
if (!STANDARD_COLUMNS.contains( "," + fieldName + "," ))
{
tableBuilderFactory.column( fieldName, fieldName, STRING, new Function<CaseEntity, Object>()
{
public Object map(CaseEntity caseEntity)
{
for( SubmittedFormValue form : caseEntity.getLatestSubmittedForms() )
{
SubmittedFieldValue submittedFieldValue = Iterables.first( Iterables.filter( new Specification<SubmittedFieldValue>()
{
public boolean satisfiedBy(SubmittedFieldValue item) {
FieldEntity fieldEntity = module.unitOfWorkFactory().currentUnitOfWork().get( FieldEntity.class, item.field().get().identity() );
return fieldEntity.fieldId().get().equals( fieldName );
};
}, form.fields() ));
if (submittedFieldValue != null)
{
return submittedFieldValue.value().get();
}
}
return null;
}
});
}
}
}
return tableBuilderFactory.newInstance(query).rows(cases).newTable();
}
}