diff --git a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/delegate/AbstractServiceDelegate.java b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/delegate/AbstractServiceDelegate.java
index 3979c6602..253a73cb7 100755
--- a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/delegate/AbstractServiceDelegate.java
+++ b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/delegate/AbstractServiceDelegate.java
@@ -12,6 +12,7 @@
import org.highmed.dsf.fhir.authorization.read.ReadAccessHelper;
import org.highmed.dsf.fhir.client.FhirWebserviceClientProvider;
import org.highmed.dsf.fhir.task.TaskHelper;
+import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,12 +26,6 @@ public abstract class AbstractServiceDelegate implements JavaDelegate, Initializ
private final TaskHelper taskHelper;
private final ReadAccessHelper readAccessHelper;
- /**
- * @deprecated as of release 0.8.0, use {@link #getExecution()} instead
- */
- @Deprecated
- protected DelegateExecution execution;
-
public AbstractServiceDelegate(FhirWebserviceClientProvider clientProvider, TaskHelper taskHelper,
ReadAccessHelper readAccessHelper)
{
@@ -50,8 +45,6 @@ public void afterPropertiesSet() throws Exception
@Override
public final void execute(DelegateExecution execution) throws Exception
{
- this.execution = execution;
-
try
{
logger.trace("Execution of task with id='{}'", execution.getCurrentActivityId());
@@ -61,12 +54,12 @@ public final void execute(DelegateExecution execution) throws Exception
// Error boundary event, do not stop process execution
catch (BpmnError error)
{
- Task task = getTask();
+ Task task = getTask(execution);
logger.debug("Error while executing service delegate " + getClass().getName(), error);
logger.error(
- "Process {} encountered error boundary event in step {} for task with id {}, error-code: {}, message: {}",
- execution.getProcessDefinitionId(), execution.getActivityInstanceId(), task.getId(),
+ "Process {} encountered error boundary event in step {} for task {}, error-code: {}, message: {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(), getTaskAbsoluteUrl(task),
error.getErrorCode(), error.getMessage());
throw error;
@@ -74,12 +67,12 @@ public final void execute(DelegateExecution execution) throws Exception
// Not an error boundary event, stop process execution
catch (Exception exception)
{
- Task task = getTask();
+ Task task = getTask(execution);
logger.debug("Error while executing service delegate " + getClass().getName(), exception);
- logger.error("Process {} has fatal error in step {} for task with id {}, reason: {}",
- execution.getProcessDefinitionId(), execution.getActivityInstanceId(), task.getId(),
- exception.getMessage());
+ logger.error("Process {} has fatal error in step {} for task {}, reason: {} - {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(), getTaskAbsoluteUrl(task),
+ exception.getClass().getName(), exception.getMessage());
String errorMessage = "Process " + execution.getProcessDefinitionId() + " has fatal error in step "
+ execution.getActivityInstanceId() + ", reason: " + exception.getMessage();
@@ -96,6 +89,13 @@ public final void execute(DelegateExecution execution) throws Exception
}
}
+ protected final String getTaskAbsoluteUrl(Task task)
+ {
+ return task == null ? "?"
+ : task.getIdElement().toVersionless()
+ .withServerBase(clientProvider.getLocalBaseUrl(), ResourceType.Task.name()).getValue();
+ }
+
/**
* Method called by a BPMN service task
*
@@ -109,11 +109,6 @@ public final void execute(DelegateExecution execution) throws Exception
*/
protected abstract void doExecute(DelegateExecution execution) throws BpmnError, Exception;
- protected final DelegateExecution getExecution()
- {
- return execution;
- }
-
protected final TaskHelper getTaskHelper()
{
return taskHelper;
@@ -130,36 +125,42 @@ protected final ReadAccessHelper getReadAccessHelper()
}
/**
+ * @param execution
+ * not null
* @return the active task from execution variables, i.e. the leading task if the main process is running or the
* current task if a subprocess is running.
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final Task getTask()
+ protected final Task getTask(DelegateExecution execution)
{
return taskHelper.getTask(execution);
}
/**
+ * @param execution
+ * not null
* @return the current task from execution variables, the task resource that started the current process or
* subprocess
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final Task getCurrentTaskFromExecutionVariables()
+ protected final Task getCurrentTaskFromExecutionVariables(DelegateExecution execution)
{
return taskHelper.getCurrentTaskFromExecutionVariables(execution);
}
/**
+ * @param execution
+ * not null
* @return the leading task from execution variables, same as current task if not in a subprocess
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_LEADING_TASK
*/
- protected final Task getLeadingTaskFromExecutionVariables()
+ protected final Task getLeadingTaskFromExecutionVariables(DelegateExecution execution)
{
return taskHelper.getLeadingTaskFromExecutionVariables(execution);
}
@@ -168,13 +169,15 @@ protected final Task getLeadingTaskFromExecutionVariables()
* Use this method to update the process engine variable {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK},
* after modifying the {@link Task}.
*
+ * @param execution
+ * not null
* @param task
* not null
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final void updateCurrentTaskInExecutionVariables(Task task)
+ protected final void updateCurrentTaskInExecutionVariables(DelegateExecution execution, Task task)
{
taskHelper.updateCurrentTaskInExecutionVariables(execution, task);
}
@@ -185,13 +188,15 @@ protected final void updateCurrentTaskInExecutionVariables(Task task)
*
* Updates the current task if no leading task is set.
*
+ * @param execution
+ * not null
* @param task
* not null
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_LEADING_TASK
*/
- protected final void updateLeadingTaskInExecutionVariables(Task task)
+ protected final void updateLeadingTaskInExecutionVariables(DelegateExecution execution, Task task)
{
taskHelper.updateLeadingTaskInExecutionVariables(execution, task);
}
diff --git a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/listener/DefaultUserTaskListener.java b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/listener/DefaultUserTaskListener.java
index 86bc1bbae..63e2f8152 100644
--- a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/listener/DefaultUserTaskListener.java
+++ b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/bpe/listener/DefaultUserTaskListener.java
@@ -22,9 +22,9 @@
import org.highmed.dsf.fhir.questionnaire.QuestionnaireResponseHelper;
import org.highmed.dsf.fhir.task.TaskHelper;
import org.hl7.fhir.r4.model.Bundle;
-import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.QuestionnaireResponse;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.StringType;
@@ -44,12 +44,6 @@ public class DefaultUserTaskListener implements TaskListener, InitializingBean
private final TaskHelper taskHelper;
private final ReadAccessHelper readAccessHelper;
- /**
- * @deprecated as of release 0.8.0, use {@link #getExecution()} instead
- */
- @Deprecated
- protected DelegateExecution execution;
-
public DefaultUserTaskListener(FhirWebserviceClientProvider clientProvider,
OrganizationProvider organizationProvider, QuestionnaireResponseHelper questionnaireResponseHelper,
TaskHelper taskHelper, ReadAccessHelper readAccessHelper)
@@ -74,7 +68,7 @@ public void afterPropertiesSet() throws Exception
@Override
public final void notify(DelegateTask userTask)
{
- this.execution = userTask.getExecution();
+ DelegateExecution execution = userTask.getExecution();
try
{
@@ -88,25 +82,31 @@ public final void notify(DelegateTask userTask)
QuestionnaireResponse questionnaireResponse = createDefaultQuestionnaireResponse(
questionnaireUrlWithVersion, businessKey, userTaskId);
- addPlaceholderAnswersToQuestionnaireResponse(questionnaireResponse, questionnaire);
-
- modifyQuestionnaireResponse(userTask, questionnaireResponse);
+ transformQuestionnaireItemsToQuestionnaireResponseItems(questionnaireResponse, questionnaire);
+ beforeQuestionnaireResponseCreate(userTask, questionnaireResponse);
checkQuestionnaireResponse(questionnaireResponse);
- IdType created = clientProvider.getLocalWebserviceClient().withRetryForever(60000)
- .create(questionnaireResponse).getIdElement();
- execution.setVariable(BPMN_EXECUTION_VARIABLE_QUESTIONNAIRE_RESPONSE_ID, created.getIdPart());
+ QuestionnaireResponse created = clientProvider.getLocalWebserviceClient().withRetryForever(60000)
+ .create(questionnaireResponse);
+ execution.setVariable(BPMN_EXECUTION_VARIABLE_QUESTIONNAIRE_RESPONSE_ID,
+ created.getIdElement().getIdPart());
+
+ logger.info("Created QuestionnaireResponse for user task at {}, process waiting for it's completion",
+ created.getIdElement().toVersionless().withServerBase(clientProvider.getLocalBaseUrl(),
+ ResourceType.QuestionnaireResponse.name()));
- logger.info("Created user task with id={}, process waiting for it's completion", created.getValue());
+ afterQuestionnaireResponseCreate(userTask, created);
}
catch (Exception exception)
{
- Task task = getTask();
+ Task task = getTask(execution);
logger.debug("Error while executing user task listener " + getClass().getName(), exception);
- logger.error("Process {} has fatal error in step {} for task with id {}, reason: {}",
- execution.getProcessDefinitionId(), execution.getActivityInstanceId(), task.getId(),
+ logger.error("Process {} has fatal error in step {} for task {}, reason: {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(),
+ task.getIdElement().toVersionless()
+ .withServerBase(clientProvider.getLocalBaseUrl(), ResourceType.Task.name()).getValue(),
exception.getMessage());
String errorMessage = "Process " + execution.getProcessDefinitionId() + " has fatal error in step "
@@ -153,41 +153,55 @@ private QuestionnaireResponse createDefaultQuestionnaireResponse(String question
questionnaireResponse.setAuthor(new Reference().setType(ResourceType.Organization.name())
.setIdentifier(organizationProvider.getLocalIdentifier()));
- questionnaireResponseHelper.addItemLeave(questionnaireResponse,
- CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY, "The business-key of the process execution",
- new StringType(businessKey));
- questionnaireResponseHelper.addItemLeave(questionnaireResponse,
+ if (addBusinessKeyToQuestionnaireResponse())
+ {
+ questionnaireResponseHelper.addItemLeafWithAnswer(questionnaireResponse,
+ CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY, "The business-key of the process execution",
+ new StringType(businessKey));
+ }
+
+ questionnaireResponseHelper.addItemLeafWithAnswer(questionnaireResponse,
CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID, "The user-task-id of the process execution",
new StringType(userTaskId));
- readAccessHelper.addLocal(questionnaireResponse);
-
return questionnaireResponse;
}
- private void addPlaceholderAnswersToQuestionnaireResponse(QuestionnaireResponse questionnaireResponse,
+ private void transformQuestionnaireItemsToQuestionnaireResponseItems(QuestionnaireResponse questionnaireResponse,
Questionnaire questionnaire)
{
questionnaire.getItem().stream()
.filter(i -> !CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY.equals(i.getLinkId()))
.filter(i -> !CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID.equals(i.getLinkId()))
- .forEach(i -> createAndAddAnswerPlaceholder(questionnaireResponse, i));
+ .forEach(i -> transformItem(questionnaireResponse, i));
}
- private void createAndAddAnswerPlaceholder(QuestionnaireResponse questionnaireResponse,
+ private void transformItem(QuestionnaireResponse questionnaireResponse,
Questionnaire.QuestionnaireItemComponent question)
{
- Type answer = questionnaireResponseHelper.transformQuestionTypeToAnswerType(question);
- questionnaireResponseHelper.addItemLeave(questionnaireResponse, question.getLinkId(), question.getText(),
- answer);
+ if (Questionnaire.QuestionnaireItemType.DISPLAY.equals(question.getType()))
+ {
+ questionnaireResponseHelper.addItemLeafWithoutAnswer(questionnaireResponse, question.getLinkId(),
+ question.getText());
+ }
+ else
+ {
+ Type answer = questionnaireResponseHelper.transformQuestionTypeToAnswerType(question);
+ questionnaireResponseHelper.addItemLeafWithAnswer(questionnaireResponse, question.getLinkId(),
+ question.getText(), answer);
+ }
}
private void checkQuestionnaireResponse(QuestionnaireResponse questionnaireResponse)
{
- questionnaireResponse.getItem().stream()
- .filter(i -> CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY.equals(i.getLinkId())).findFirst()
- .orElseThrow(() -> new RuntimeException("QuestionnaireResponse does not contain an item with linkId='"
- + CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY + "'"));
+ if (addBusinessKeyToQuestionnaireResponse())
+ {
+ questionnaireResponse.getItem().stream()
+ .filter(i -> CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY.equals(i.getLinkId())).findFirst()
+ .orElseThrow(
+ () -> new RuntimeException("QuestionnaireResponse does not contain an item with linkId='"
+ + CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY + "'"));
+ }
questionnaireResponse.getItem().stream()
.filter(i -> CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID.equals(i.getLinkId())).findFirst()
@@ -199,23 +213,44 @@ private void checkQuestionnaireResponse(QuestionnaireResponse questionnaireRespo
}
/**
- * Use this method to modify the {@link QuestionnaireResponse} before it will be created in state
- * {@link QuestionnaireResponse.QuestionnaireResponseStatus#INPROGRESS}
+ * Override this method to decided if you want to add the Business-Key to the {@link QuestionnaireResponse}
+ * resource as an item with {@link QuestionnaireResponseItemComponent#getLinkId()} equal to
+ * {@link ConstantsBase#CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY}
+ *
+ * @return false
+ */
+ protected boolean addBusinessKeyToQuestionnaireResponse()
+ {
+ return false;
+ }
+
+ /**
+ * Override this method to modify the {@link QuestionnaireResponse} before it will be created in state
+ * {@link QuestionnaireResponse.QuestionnaireResponseStatus#INPROGRESS} on the DSF FHIR server
*
* @param userTask
* not null
, user task on which this {@link QuestionnaireResponse} is based
- * @param questionnaireResponse
+ * @param beforeCreate
* not null
, containing an answer placeholder for every item in the corresponding
* {@link Questionnaire}
*/
- protected void modifyQuestionnaireResponse(DelegateTask userTask, QuestionnaireResponse questionnaireResponse)
+ protected void beforeQuestionnaireResponseCreate(DelegateTask userTask, QuestionnaireResponse beforeCreate)
{
- // Nothing to do in default behaviour
+ // Nothing to do in default behavior
}
- protected final DelegateExecution getExecution()
+ /**
+ * Override this method to execute code after the {@link QuestionnaireResponse} resource has been created on the
+ * DSF FHIR server
+ *
+ * @param userTask
+ * not null
, user task on which this {@link QuestionnaireResponse} is based
+ * @param afterCreate
+ * not null
, created on the DSF FHIR server
+ */
+ protected void afterQuestionnaireResponseCreate(DelegateTask userTask, QuestionnaireResponse afterCreate)
{
- return execution;
+ // Nothing to do in default behavior
}
protected final TaskHelper getTaskHelper()
@@ -234,36 +269,42 @@ protected final ReadAccessHelper getReadAccessHelper()
}
/**
+ * @param execution
+ * not null
* @return the active task from execution variables, i.e. the leading task if the main process is running or the
* current task if a subprocess is running.
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final Task getTask()
+ protected final Task getTask(DelegateExecution execution)
{
return taskHelper.getTask(execution);
}
/**
+ * @param execution
+ * not null
* @return the current task from execution variables, the task resource that started the current process or
* subprocess
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final Task getCurrentTaskFromExecutionVariables()
+ protected final Task getCurrentTaskFromExecutionVariables(DelegateExecution execution)
{
return taskHelper.getCurrentTaskFromExecutionVariables(execution);
}
/**
+ * @param execution
+ * not null
* @return the leading task from execution variables, same as current task if not in a subprocess
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_LEADING_TASK
*/
- protected final Task getLeadingTaskFromExecutionVariables()
+ protected final Task getLeadingTaskFromExecutionVariables(DelegateExecution execution)
{
return taskHelper.getLeadingTaskFromExecutionVariables(execution);
}
@@ -272,13 +313,15 @@ protected final Task getLeadingTaskFromExecutionVariables()
* Use this method to update the process engine variable {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK},
* after modifying the {@link Task}.
*
+ * @param execution
+ * not null
* @param task
* not null
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_TASK
*/
- protected final void updateCurrentTaskInExecutionVariables(Task task)
+ protected final void updateCurrentTaskInExecutionVariables(DelegateExecution execution, Task task)
{
taskHelper.updateCurrentTaskInExecutionVariables(execution, task);
}
@@ -289,13 +332,15 @@ protected final void updateCurrentTaskInExecutionVariables(Task task)
*
* Updates the current task if no leading task is set.
*
+ * @param execution
+ * not null
* @param task
* not null
* @throws IllegalStateException
* if execution of this service delegate has not been started
* @see ConstantsBase#BPMN_EXECUTION_VARIABLE_LEADING_TASK
*/
- protected final void updateLeadingTaskInExecutionVariables(Task task)
+ protected final void updateLeadingTaskInExecutionVariables(DelegateExecution execution, Task task)
{
taskHelper.updateLeadingTaskInExecutionVariables(execution, task);
}
diff --git a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelper.java b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelper.java
index e0fcf307a..8270ba425 100644
--- a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelper.java
+++ b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelper.java
@@ -37,5 +37,7 @@ Stream getItemLeavesAs
Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemComponent question);
- void addItemLeave(QuestionnaireResponse questionnaireResponse, String linkId, String text, Type answer);
+ void addItemLeafWithoutAnswer(QuestionnaireResponse questionnaireResponse, String linkId, String text);
+
+ void addItemLeafWithAnswer(QuestionnaireResponse questionnaireResponse, String linkId, String text, Type answer);
}
diff --git a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/task/AbstractTaskMessageSend.java b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/task/AbstractTaskMessageSend.java
index 36053a38d..492750202 100755
--- a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/task/AbstractTaskMessageSend.java
+++ b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/task/AbstractTaskMessageSend.java
@@ -84,11 +84,11 @@ public void doExecute(DelegateExecution execution) throws Exception
// String targetOrganizationId = (String) execution.getVariable(Constants.VARIABLE_TARGET_ORGANIZATION_ID);
// String correlationKey = (String) execution.getVariable(Constants.VARIABLE_CORRELATION_KEY);
- Target target = getTarget();
+ Target target = getTarget(execution);
try
{
- sendTask(target, instantiatesUri, messageName, businessKey, profile,
+ sendTask(execution, target, instantiatesUri, messageName, businessKey, profile,
getAdditionalInputParameters(execution));
}
catch (Exception e)
@@ -102,11 +102,11 @@ public void doExecute(DelegateExecution execution) throws Exception
logger.debug("Error while sending Task", e);
if (execution.getBpmnModelElementInstance() instanceof IntermediateThrowEvent)
- handleIntermediateThrowEventError(e, errorMessage);
+ handleIntermediateThrowEventError(execution, e, errorMessage);
else if (execution.getBpmnModelElementInstance() instanceof EndEvent)
- handleEndEventError(e, errorMessage);
+ handleEndEventError(execution, e, errorMessage);
else if (execution.getBpmnModelElementInstance() instanceof SendTask)
- handleSendTaskError(e, errorMessage);
+ handleSendTaskError(execution, e, errorMessage);
else
logger.warn("Error handling for {} not implemented",
execution.getBpmnModelElementInstance().getClass().getName());
@@ -119,14 +119,15 @@ protected void addErrorMessage(Task task, String errorMessage)
errorMessage));
}
- protected void handleIntermediateThrowEventError(Exception exception, String errorMessage)
+ protected void handleIntermediateThrowEventError(DelegateExecution execution, Exception exception,
+ String errorMessage)
{
- Task task = getLeadingTaskFromExecutionVariables();
+ Task task = getLeadingTaskFromExecutionVariables(execution);
logger.debug("Error while executing Task message send " + getClass().getName(), exception);
- logger.error("Process {} has fatal error in step {} for task with id {}, reason: {}",
- getExecution().getProcessDefinitionId(), getExecution().getActivityInstanceId(),
- task == null ? "?" : task.getId(), exception.getMessage());
+ logger.error("Process {} has fatal error in step {} for task {}, reason: {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(), getTaskAbsoluteUrl(task),
+ exception.getMessage());
try
{
@@ -141,25 +142,25 @@ protected void handleIntermediateThrowEventError(Exception exception, String err
}
finally
{
- getExecution().getProcessEngine().getRuntimeService()
- .deleteProcessInstance(getExecution().getProcessInstanceId(), exception.getMessage());
+ execution.getProcessEngine().getRuntimeService().deleteProcessInstance(execution.getProcessInstanceId(),
+ exception.getMessage());
}
}
- protected void handleEndEventError(Exception exception, String errorMessage)
+ protected void handleEndEventError(DelegateExecution execution, Exception exception, String errorMessage)
{
- Task task = getLeadingTaskFromExecutionVariables();
+ Task task = getLeadingTaskFromExecutionVariables(execution);
logger.debug("Error while executing Task message send " + getClass().getName(), exception);
- logger.error("Process {} has fatal error in step {} for task with id {}, reason: {}",
- getExecution().getProcessDefinitionId(), getExecution().getActivityInstanceId(),
- task == null ? "?" : task.getId(), exception.getMessage());
+ logger.error("Process {} has fatal error in step {} for task {}, reason: {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(), getTaskAbsoluteUrl(task),
+ exception.getMessage());
if (task != null)
{
addErrorMessage(task, errorMessage);
task.setStatus(Task.TaskStatus.FAILED);
- updateLeadingTaskInExecutionVariables(task);
+ updateLeadingTaskInExecutionVariables(execution, task);
}
else
logger.warn("Leading Task null, unable to set failed state");
@@ -167,20 +168,20 @@ protected void handleEndEventError(Exception exception, String errorMessage)
// Task update and process-end handled by EndListener
}
- protected void handleSendTaskError(Exception exception, String errorMessage)
+ protected void handleSendTaskError(DelegateExecution execution, Exception exception, String errorMessage)
{
- Task task = getLeadingTaskFromExecutionVariables();
- Targets targets = getTargets();
+ Task task = getLeadingTaskFromExecutionVariables(execution);
+ Targets targets = getTargets(execution);
// if we are a multi instance message send task, remove target
if (targets != null)
{
addErrorMessage(task, errorMessage);
- updateLeadingTaskInExecutionVariables(task);
+ updateLeadingTaskInExecutionVariables(execution, task);
- Target target = getTarget();
+ Target target = getTarget(execution);
targets = targets.removeByEndpointIdentifierValue(target);
- updateTargets(targets);
+ updateTargets(execution, targets);
logger.debug("Target organization {}, endpoint {} with error {} removed from target list",
target.getOrganizationIdentifierValue(), target.getEndpointIdentifierValue(),
exception.getMessage());
@@ -188,9 +189,9 @@ protected void handleSendTaskError(Exception exception, String errorMessage)
if (targets.isEmpty())
{
logger.debug("Error while executing Task message send " + getClass().getName(), exception);
- logger.error("Process {} has fatal error in step {} for task with id {}, last reason: {}",
- getExecution().getProcessDefinitionId(), getExecution().getActivityInstanceId(),
- task == null ? "?" : task.getId(), exception.getMessage());
+ logger.error("Process {} has fatal error in step {} for task {}, last reason: {}",
+ execution.getProcessDefinitionId(), execution.getActivityInstanceId(), getTaskAbsoluteUrl(task),
+ exception.getMessage());
try
{
@@ -204,8 +205,8 @@ protected void handleSendTaskError(Exception exception, String errorMessage)
}
finally
{
- getExecution().getProcessEngine().getRuntimeService()
- .deleteProcessInstance(getExecution().getProcessInstanceId(), exception.getMessage());
+ execution.getProcessEngine().getRuntimeService()
+ .deleteProcessInstance(execution.getProcessInstanceId(), exception.getMessage());
}
}
}
@@ -216,57 +217,39 @@ protected void handleSendTaskError(Exception exception, String errorMessage)
* then {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TARGET}.
*
* @param execution
- * the delegate execution of this process instance
+ * not null
* @return {@link Target} that should receive the message
- * @deprecated use {@link #getTarget()}
*/
- @Deprecated
protected Target getTarget(DelegateExecution execution)
{
return (Target) execution.getVariable(BPMN_EXECUTION_VARIABLE_TARGET);
}
- /**
- * Override this method if the {@link Target} variable is stored in a different process engine variable other
- * then {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TARGET}.
- *
- * @return {@link Target} that should receive the message
- */
- protected Target getTarget()
- {
- if (getExecution() == null)
- throw new IllegalStateException("execution not started");
-
- return (Target) getExecution().getVariable(BPMN_EXECUTION_VARIABLE_TARGET);
- }
-
/**
* Override this method if the {@link Targets} variable is stored in a different process engine variable other
* then {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TARGETS}.
*
+ * @param execution
+ * not null
* @return {@link Targets} that should receive the message
*/
- protected Targets getTargets()
+ protected Targets getTargets(DelegateExecution execution)
{
- if (getExecution() == null)
- throw new IllegalStateException("execution not started");
-
- return (Targets) getExecution().getVariable(BPMN_EXECUTION_VARIABLE_TARGETS);
+ return (Targets) execution.getVariable(BPMN_EXECUTION_VARIABLE_TARGETS);
}
/**
* Override this method if the {@link Targets} variable should stored in a different process engine variable
* other then {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TARGETS}.
*
+ * @param execution
+ * not null
* @param targets
* the targets to save in process engine variable {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_TARGETS}
*/
- protected void updateTargets(Targets targets)
+ protected void updateTargets(DelegateExecution execution, Targets targets)
{
- if (getExecution() == null)
- throw new IllegalStateException("execution not started");
-
- getExecution().setVariable(BPMN_EXECUTION_VARIABLE_TARGETS, TargetsValues.create(targets));
+ execution.setVariable(BPMN_EXECUTION_VARIABLE_TARGETS, TargetsValues.create(targets));
}
/**
@@ -286,33 +269,35 @@ protected Stream getAdditionalInputParameters(DelegateExecut
* {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_ALTERNATIVE_BUSINESS_KEY}
*
* Use this method in combination with overriding
- * {@link #sendTask(Target, String, String, String, String, Stream)} to use an alternative business-key with the
- * communication target.
+ * {@link #sendTask(DelegateExecution, Target, String, String, String, String, Stream)} to use an alternative
+ * business-key with the communication target.
*
*
* @Override
- * protected void sendTask(Target target, String instantiatesUri, String messageName, String businessKey,
- * String profile, Stream<ParameterComponent> additionalInputParameters)
+ * protected void sendTask(DelegateExecution execution, Target target, String instantiatesUri, String messageName,
+ * String businessKey, String profile, Stream<ParameterComponent> additionalInputParameters)
* {
* String alternativeBusinesKey = createAndSaveAlternativeBusinessKey();
- * super.sendTask(target, instantiatesUri, messageName, alternativeBusinesKey, profile,
+ * super.sendTask(execution, target, instantiatesUri, messageName, alternativeBusinesKey, profile,
* additionalInputParameters);
* }
*
*
+ * @param execution
+ * not null
* @return the alternative business-key stored as variable
* {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_ALTERNATIVE_BUSINESS_KEY}
*/
- protected final String createAndSaveAlternativeBusinessKey()
+ protected final String createAndSaveAlternativeBusinessKey(DelegateExecution execution)
{
String alternativeBusinessKey = UUID.randomUUID().toString();
- getExecution().setVariable(BPMN_EXECUTION_VARIABLE_ALTERNATIVE_BUSINESS_KEY, alternativeBusinessKey);
+ execution.setVariable(BPMN_EXECUTION_VARIABLE_ALTERNATIVE_BUSINESS_KEY, alternativeBusinessKey);
return alternativeBusinessKey;
}
- protected void sendTask(Target target, String instantiatesUri, String messageName, String businessKey,
- String profile, Stream additionalInputParameters)
+ protected void sendTask(DelegateExecution execution, Target target, String instantiatesUri, String messageName,
+ String businessKey, String profile, Stream additionalInputParameters)
{
if (messageName.isEmpty() || instantiatesUri.isEmpty())
throw new IllegalStateException("Next process-id or message-name not definied");
diff --git a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/variables/FhirResourceSerializer.java b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/variables/FhirResourceSerializer.java
index bdc7c8241..6cd603a12 100644
--- a/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/variables/FhirResourceSerializer.java
+++ b/dsf-bpe/dsf-bpe-process-base/src/main/java/org/highmed/dsf/fhir/variables/FhirResourceSerializer.java
@@ -9,6 +9,8 @@
import org.camunda.bpm.engine.variable.impl.value.UntypedValueImpl;
import org.highmed.dsf.fhir.variables.FhirResourceValues.FhirResourceValue;
import org.hl7.fhir.r4.model.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import ca.uhn.fhir.context.FhirContext;
@@ -17,6 +19,8 @@
public class FhirResourceSerializer extends PrimitiveValueSerializer implements InitializingBean
{
+ private static final Logger logger = LoggerFactory.getLogger(FhirResourceSerializer.class);
+
private final FhirContext fhirContext;
public FhirResourceSerializer(FhirContext fhirContext)
@@ -73,9 +77,18 @@ public FhirResourceValue readValue(ValueFields valueFields, boolean asTransientV
try
{
- @SuppressWarnings("unchecked")
- Class clazz = (Class) Class.forName(className);
- Resource resource = newJsonParser().parseResource(clazz, new ByteArrayInputStream(bytes));
+ Resource resource;
+ if (className != null)
+ {
+ @SuppressWarnings("unchecked")
+ Class clazz = (Class) Class.forName(className);
+ resource = newJsonParser().parseResource(clazz, new ByteArrayInputStream(bytes));
+ }
+ else
+ {
+ logger.warn("ClassName from DB null, trying to parse FHIR resource without type information");
+ resource = (Resource) newJsonParser().parseResource(new ByteArrayInputStream(bytes));
+ }
return FhirResourceValues.create(resource);
}
diff --git a/dsf-bpe/dsf-bpe-server-jetty/pom.xml b/dsf-bpe/dsf-bpe-server-jetty/pom.xml
index 9d1ea2ce0..b97454738 100755
--- a/dsf-bpe/dsf-bpe-server-jetty/pom.xml
+++ b/dsf-bpe/dsf-bpe-server-jetty/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-bpe-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-bpe/dsf-bpe-server/pom.xml b/dsf-bpe/dsf-bpe-server/pom.xml
index 08f531e0d..e8a52ec65 100755
--- a/dsf-bpe/dsf-bpe-server/pom.xml
+++ b/dsf-bpe/dsf-bpe-server/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-bpe-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/camunda/MultiVersionClassDelegateExecutionListener.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/camunda/MultiVersionClassDelegateExecutionListener.java
index 752d0f52a..d7d54b9af 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/camunda/MultiVersionClassDelegateExecutionListener.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/camunda/MultiVersionClassDelegateExecutionListener.java
@@ -5,7 +5,6 @@
import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.ExecutionListener;
-import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.engine.impl.ProcessEngineLogger;
import org.camunda.bpm.engine.impl.bpmn.delegate.ExecutionListenerInvocation;
import org.camunda.bpm.engine.impl.bpmn.listener.ClassDelegateExecutionListener;
@@ -55,7 +54,7 @@ protected ExecutionListener getExecutionListenerInstance(ProcessKeyAndVersion pr
{
Object delegateInstance = instantiateDelegate(processKeyAndVersion, className, fieldDeclarations);
- if (delegateInstance instanceof TaskListener)
+ if (delegateInstance instanceof ExecutionListener)
{
return (ExecutionListener) delegateInstance;
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DebugLoggingBpmnParseListener.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DebugLoggingBpmnParseListener.java
new file mode 100644
index 000000000..bd0055acc
--- /dev/null
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DebugLoggingBpmnParseListener.java
@@ -0,0 +1,216 @@
+package org.highmed.dsf.bpe.listener;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.ExecutionListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.AbstractBpmnParseListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener;
+import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
+import org.camunda.bpm.engine.impl.pvm.process.ScopeImpl;
+import org.camunda.bpm.engine.impl.util.xml.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+
+public class DebugLoggingBpmnParseListener extends AbstractBpmnParseListener
+ implements BpmnParseListener, InitializingBean
+{
+ private static final class ExecutionListenerLogger implements ExecutionListener
+ {
+ final boolean logVariables;
+
+ ExecutionListenerLogger(boolean logVariables)
+ {
+ this.logVariables = logVariables;
+ }
+
+ @Override
+ public void notify(DelegateExecution execution) throws Exception
+ {
+ if (execution != null)
+ {
+ logger.debug(
+ "EventName: '{}', ActivityInstanceId: '{}', BusinessKey: '{}', CurrentActivityId: '{}', "
+ + "CurrentActivityName: '{}', ProcessDefinitionId: '{}', ProcessInstanceId: '{}'",
+ execution.getEventName(), execution.getActivityInstanceId(), execution.getBusinessKey(),
+ execution.getCurrentActivityId(), execution.getCurrentActivityName(),
+ execution.getProcessDefinitionId(), execution.getProcessInstanceId());
+
+ if (logVariables)
+ logger.debug("Variables: {}", execution.getVariables());
+ }
+ else
+ {
+ logger.warn("Can't log, DelegateExecution is 'null'");
+ }
+ }
+ }
+
+ private static final Logger logger = LoggerFactory.getLogger(DebugLoggingBpmnParseListener.class);
+
+ private final boolean logActivityStart;
+ private final boolean logActivityEnd;
+ private final boolean logVariables;
+
+ private final ExecutionListener listener;
+
+ public DebugLoggingBpmnParseListener(boolean logActivityStart, boolean logActivityEnd, boolean logVariables)
+ {
+ this.logActivityStart = logActivityStart;
+ this.logActivityEnd = logActivityEnd;
+ this.logVariables = logVariables;
+
+ listener = new ExecutionListenerLogger(logVariables);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ if (logActivityStart)
+ logger.warn(
+ "Process activity start debug logging enabled. This should only be activated during process plugin development");
+
+ if (logActivityEnd)
+ logger.warn(
+ "Process activity end debug logging enabled. This should only be activated during process plugin development");
+
+ if (logVariables)
+ logger.warn(
+ "Process variable debug logging enabled. This should only be activated during process plugin development. WARNNING: Confidential information may be leaked via the debug log!");
+ }
+
+ private void addListeners(ActivityImpl activity)
+ {
+ if (logActivityStart)
+ activity.addListener(ExecutionListener.EVENTNAME_START, listener, 0);
+
+ if (logActivityEnd)
+ activity.addListener(ExecutionListener.EVENTNAME_END, listener, 0);
+ }
+
+ @Override
+ public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseInclusiveGateway(Element inclusiveGwElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseEventBasedGateway(Element eventBasedGwElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseTransaction(Element transactionElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseIntermediateThrowEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseIntermediateCatchEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseBoundaryEvent(Element boundaryEventElement, ScopeImpl scopeElement, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+
+ @Override
+ public void parseMultiInstanceLoopCharacteristics(Element activityElement,
+ Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity)
+ {
+ addListeners(activity);
+ }
+}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DefaultBpmnParseListener.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DefaultBpmnParseListener.java
index 25412420e..94ac8b2e5 100755
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DefaultBpmnParseListener.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/DefaultBpmnParseListener.java
@@ -47,7 +47,10 @@ public void parseStartEvent(Element startEventElement, ScopeImpl scope, Activity
@Override
public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl endEventActivity)
{
- endEventActivity.addListener(ExecutionListener.EVENTNAME_END, endListener);
+ // Adding at index 0 to the end phase of the EndEvent, so processes can execute listeners after the Task
+ // resource has been updated.
+ // Listeners added to the end phase of the EndEvent via bpmn are execute after this listener
+ endEventActivity.addListener(ExecutionListener.EVENTNAME_END, endListener, 0);
}
@Override
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/EndListener.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/EndListener.java
index 2051de522..b0adeccbb 100755
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/EndListener.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/EndListener.java
@@ -8,15 +8,19 @@
import static org.highmed.dsf.bpe.ConstantsBase.CODESYSTEM_HIGHMED_BPMN_VALUE_CORRELATION_KEY;
import static org.highmed.dsf.bpe.ConstantsBase.CODESYSTEM_HIGHMED_BPMN_VALUE_MESSAGE_NAME;
+import java.util.Objects;
+
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.ExecutionListener;
import org.camunda.bpm.engine.variable.Variables;
import org.highmed.dsf.bpe.ConstantsBase;
import org.highmed.dsf.fhir.task.TaskHelper;
import org.highmed.fhir.client.FhirWebserviceClient;
+import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
/**
* Added to each BPMN EndEvent by the {@link DefaultBpmnParseListener}. Is used to set the FHIR {@link Task} status as
@@ -24,17 +28,24 @@
* {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_IN_CALLED_PROCESS} back to false
if a called sub process
* ends.
*/
-public class EndListener implements ExecutionListener
+public class EndListener implements ExecutionListener, InitializingBean
{
private static final Logger logger = LoggerFactory.getLogger(EndListener.class);
- private final TaskHelper taskHelper;
private final FhirWebserviceClient webserviceClient;
+ private final TaskHelper taskHelper;
public EndListener(FhirWebserviceClient webserviceClient, TaskHelper taskHelper)
{
- this.taskHelper = taskHelper;
this.webserviceClient = webserviceClient;
+ this.taskHelper = taskHelper;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ Objects.requireNonNull(webserviceClient, "webserviceClient");
+ Objects.requireNonNull(taskHelper, "taskHelper");
}
@Override
@@ -82,9 +93,10 @@ private void log(DelegateExecution execution, Task task)
CODESYSTEM_HIGHMED_BPMN_VALUE_BUSINESS_KEY).orElse(null);
String correlationKey = taskHelper.getFirstInputParameterStringValue(task, CODESYSTEM_HIGHMED_BPMN,
CODESYSTEM_HIGHMED_BPMN_VALUE_CORRELATION_KEY).orElse(null);
- String taskId = task.getIdElement().getIdPart();
+ String taskUrl = task.getIdElement().toVersionless()
+ .withServerBase(webserviceClient.getBaseUrl(), ResourceType.Task.name()).getValue();
- logger.info("Process {} finished [message: {}, businessKey: {}, correlationKey: {}, taskId: {}]", processUrl,
- messageName, businessKey, correlationKey, taskId);
+ logger.info("Process {} finished [message: {}, businessKey: {}, correlationKey: {}, task: {}]", processUrl,
+ messageName, businessKey, correlationKey, taskUrl);
}
}
\ No newline at end of file
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/StartListener.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/StartListener.java
index b5b3f8601..0e7200864 100755
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/StartListener.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/listener/StartListener.java
@@ -16,24 +16,35 @@
import org.highmed.dsf.bpe.ConstantsBase;
import org.highmed.dsf.fhir.task.TaskHelper;
import org.highmed.dsf.fhir.variables.FhirResourceValues;
+import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
/**
* Added to each BPMN StartEvent by the {@link DefaultBpmnParseListener}. Initializes the
* {@link ConstantsBase#BPMN_EXECUTION_VARIABLE_IN_CALLED_PROCESS} variable with false
for processes
* started via a {@link Task} resource.
*/
-public class StartListener implements ExecutionListener
+public class StartListener implements ExecutionListener, InitializingBean
{
private static final Logger logger = LoggerFactory.getLogger(StartListener.class);
- private TaskHelper taskHelper;
+ private final TaskHelper taskHelper;
+ private final String baseUrl;
- public StartListener(TaskHelper taskHelper)
+ public StartListener(TaskHelper taskHelper, String baseUrl)
{
- this.taskHelper = Objects.requireNonNull(taskHelper, "taskHelper");
+ this.taskHelper = taskHelper;
+ this.baseUrl = baseUrl;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ Objects.requireNonNull(taskHelper, "taskHelper");
+ Objects.requireNonNull(baseUrl, "baseUrl");
}
@Override
@@ -69,9 +80,10 @@ private void log(DelegateExecution execution, Task task)
CODESYSTEM_HIGHMED_BPMN_VALUE_BUSINESS_KEY).orElse(null);
String correlationKey = taskHelper.getFirstInputParameterStringValue(task, CODESYSTEM_HIGHMED_BPMN,
CODESYSTEM_HIGHMED_BPMN_VALUE_CORRELATION_KEY).orElse(null);
- String taskId = task.getIdElement().getIdPart();
+ String taskUrl = task.getIdElement().toVersionless().withServerBase(baseUrl, ResourceType.Task.name())
+ .getValue();
- logger.info("Starting process {} [message: {}, businessKey: {}, correlationKey: {}, taskId: {}]", processUrl,
- messageName, businessKey, correlationKey, taskId);
+ logger.info("Starting process {} [message: {}, businessKey: {}, correlationKey: {}, task: {}]", processUrl,
+ messageName, businessKey, correlationKey, taskUrl);
}
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/BpmnServiceDelegateValidationServiceImpl.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/BpmnServiceDelegateValidationServiceImpl.java
index b89ee21d0..7b808d412 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/BpmnServiceDelegateValidationServiceImpl.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/BpmnServiceDelegateValidationServiceImpl.java
@@ -5,6 +5,9 @@
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.RepositoryService;
+import org.camunda.bpm.engine.delegate.ExecutionListener;
+import org.camunda.bpm.engine.delegate.JavaDelegate;
+import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.model.bpmn.instance.EndEvent;
import org.camunda.bpm.model.bpmn.instance.ExtensionElements;
@@ -50,7 +53,7 @@ public void afterPropertiesSet() throws Exception
@Override
public void validateModels()
{
- logger.debug("Validating bpmn models, checking service delegate availability");
+ logger.debug("Validating bpmn models, checking service delegate / listener availability");
RepositoryService repositoryService = processEngine.getRepositoryService();
@@ -73,44 +76,48 @@ private void validateBeanAvailabilityForProcess(ModelElementInstance parent, Pro
{
// service tasks
parent.getChildElementsByType(ServiceTask.class).stream().filter(t -> t != null)
- .map(ServiceTask::getCamundaClass).forEach(c -> validateBeanAvailability(process, c));
+ .map(ServiceTask::getCamundaClass)
+ .forEach(c -> validateBeanAvailability(process, c, JavaDelegate.class));
// send tasks
parent.getChildElementsByType(SendTask.class).stream().filter(t -> t != null).map(SendTask::getCamundaClass)
- .forEach(c -> validateBeanAvailability(process, c));
+ .forEach(c -> validateBeanAvailability(process, c, JavaDelegate.class));
// user tasks: task listeners
parent.getChildElementsByType(UserTask.class).stream().filter(t -> t != null)
.flatMap(u -> u.getChildElementsByType(ExtensionElements.class).stream()).filter(e -> e != null)
.flatMap(e -> e.getChildElementsByType(CamundaTaskListener.class).stream()).filter(t -> t != null)
- .map(CamundaTaskListener::getCamundaClass).forEach(c -> validateBeanAvailability(process, c));
+ .map(CamundaTaskListener::getCamundaClass)
+ .forEach(c -> validateBeanAvailability(process, c, TaskListener.class));
// all elements: execution listeners
parent.getChildElementsByType(FlowNode.class).stream().filter(t -> t != null)
.flatMap(u -> u.getChildElementsByType(ExtensionElements.class).stream()).filter(e -> e != null)
.flatMap(e -> e.getChildElementsByType(CamundaExecutionListener.class).stream()).filter(t -> t != null)
- .map(CamundaExecutionListener::getCamundaClass).forEach(c -> validateBeanAvailability(process, c));
+ .map(CamundaExecutionListener::getCamundaClass)
+ .forEach(c -> validateBeanAvailability(process, c, ExecutionListener.class));
// intermediate message throw events
parent.getChildElementsByType(IntermediateThrowEvent.class).stream().filter(e -> e != null)
.flatMap(e -> e.getEventDefinitions().stream()
.filter(def -> def != null && def instanceof MessageEventDefinition))
.map(def -> (MessageEventDefinition) def).map(MessageEventDefinition::getCamundaClass)
- .forEach(c -> validateBeanAvailability(process, c));
+ .forEach(c -> validateBeanAvailability(process, c, JavaDelegate.class));
// end events
parent.getChildElementsByType(EndEvent.class).stream().filter(e -> e != null)
.flatMap(e -> e.getEventDefinitions().stream()
.filter(def -> def != null && def instanceof MessageEventDefinition))
.map(def -> (MessageEventDefinition) def).map(MessageEventDefinition::getCamundaClass)
- .forEach(c -> validateBeanAvailability(process, c));
+ .forEach(c -> validateBeanAvailability(process, c, JavaDelegate.class));
// sub processes
parent.getChildElementsByType(SubProcess.class).stream().filter(s -> s != null)
.forEach(subProcess -> validateBeanAvailabilityForProcess(subProcess, process));
}
- private void validateBeanAvailability(Process process, String className)
+ // throws exceptions and stops bpe startup, these exceptions are not expected for tested processes
+ private void validateBeanAvailability(Process process, String className, Class> expectedInterface)
{
if (className == null || className.isBlank())
return;
@@ -120,11 +127,12 @@ private void validateBeanAvailability(Process process, String className)
logger.trace("Checking {} available in {}", className, processKeyAndVersion);
- Class> serviceClass = loadClass(processKeyAndVersion, className);
- loadBean(processKeyAndVersion, serviceClass);
+ Class> serviceClass = loadClass(processKeyAndVersion, expectedInterface, className);
+ checkExpectedInterface(processKeyAndVersion, expectedInterface, serviceClass);
+ loadBean(processKeyAndVersion, expectedInterface, serviceClass);
}
- private Class> loadClass(ProcessKeyAndVersion processKeyAndVersion, String className)
+ private Class> loadClass(ProcessKeyAndVersion processKeyAndVersion, Class> expectedInterface, String className)
{
try
{
@@ -133,12 +141,24 @@ private Class> loadClass(ProcessKeyAndVersion processKeyAndVersion, String cla
}
catch (ClassNotFoundException e)
{
- logger.warn("Service delegate class {} defined in process {} not found", className, processKeyAndVersion);
+ logger.warn("{} {} defined in process {} not found", expectedInterface.getClass().getSimpleName(),
+ className, processKeyAndVersion);
throw new RuntimeException(e);
}
}
- private void loadBean(ProcessKeyAndVersion processKeyAndVersion, Class> serviceClass)
+ private void checkExpectedInterface(ProcessKeyAndVersion processKeyAndVersion, Class> expectedInterface,
+ Class> serviceClass)
+ {
+ if (!expectedInterface.isAssignableFrom(serviceClass))
+ {
+ logger.warn("Class {} defined in process {} is not implementing the {} interface", serviceClass.getName(),
+ processKeyAndVersion, expectedInterface.getSimpleName());
+ throw new RuntimeException(serviceClass.getName() + " must implement " + expectedInterface.getName());
+ }
+ }
+
+ private void loadBean(ProcessKeyAndVersion processKeyAndVersion, Class> expectedInterface, Class> serviceClass)
{
try
{
@@ -147,8 +167,9 @@ private void loadBean(ProcessKeyAndVersion processKeyAndVersion, Class> servic
}
catch (BeansException e)
{
- logger.error("Unable to find service delegate bean of type {} defined in process {}: {}",
- serviceClass.getName(), processKeyAndVersion, e.getMessage());
+ logger.warn("Unable to find {} bean of type {} defined in process {}: {}",
+ expectedInterface.getSimpleName(), serviceClass.getName(), processKeyAndVersion, e.getMessage());
+ throw e;
}
}
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/SmtpMailService.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/SmtpMailService.java
index 122e6c250..127ce65f4 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/SmtpMailService.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/service/SmtpMailService.java
@@ -508,9 +508,12 @@ private MimeMultipart signMessage(MimeBodyPart body)
{
try
{
- return new MimeMultipart(body);
+ if (body.getContent() != null && body.getContent() instanceof MimeMultipart)
+ return (MimeMultipart) body.getContent();
+ else
+ return new MimeMultipart(body);
}
- catch (MessagingException e)
+ catch (MessagingException | IOException e)
{
throw new RuntimeException(e);
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/CamundaConfig.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/CamundaConfig.java
index 2e27b3676..a99576a7a 100755
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/CamundaConfig.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/CamundaConfig.java
@@ -15,6 +15,7 @@
import org.highmed.dsf.bpe.delegate.DelegateProvider;
import org.highmed.dsf.bpe.delegate.DelegateProviderImpl;
import org.highmed.dsf.bpe.listener.CallActivityListener;
+import org.highmed.dsf.bpe.listener.DebugLoggingBpmnParseListener;
import org.highmed.dsf.bpe.listener.DefaultBpmnParseListener;
import org.highmed.dsf.bpe.listener.DefaultUserTaskListener;
import org.highmed.dsf.bpe.listener.EndListener;
@@ -88,7 +89,7 @@ private String toString(char[] password)
@Bean
public StartListener startListener()
{
- return new StartListener(fhirConfig.taskHelper());
+ return new StartListener(fhirConfig.taskHelper(), clientProvider.getLocalBaseUrl());
}
@Bean
@@ -109,6 +110,13 @@ public DefaultBpmnParseListener defaultBpmnParseListener()
return new DefaultBpmnParseListener(startListener(), endListener(), callActivityListener());
}
+ @Bean
+ public DebugLoggingBpmnParseListener debugLoggingBpmnParseListener()
+ {
+ return new DebugLoggingBpmnParseListener(propertiesConfig.getDebugLogMessageOnActivityStart(),
+ propertiesConfig.getDebugLogMessageOnActivityEnd(), propertiesConfig.getDebugLogMessageVariables());
+ }
+
@Bean
public SpringProcessEngineConfiguration processEngineConfiguration() throws IOException
{
@@ -118,7 +126,7 @@ public SpringProcessEngineConfiguration processEngineConfiguration() throws IOEx
c.setTransactionManager(transactionManager());
c.setDatabaseSchemaUpdate("false");
c.setJobExecutorActivate(true);
- c.setCustomPreBPMNParseListeners(List.of(defaultBpmnParseListener()));
+ c.setCustomPreBPMNParseListeners(List.of(defaultBpmnParseListener(), debugLoggingBpmnParseListener()));
c.setCustomPreVariableSerializers(baseSerializers);
c.setFallbackSerializerFactory(getFallbackSerializerFactory());
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/PropertiesConfig.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/PropertiesConfig.java
index c3c19baed..8dae12e6a 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/PropertiesConfig.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/bpe/spring/config/PropertiesConfig.java
@@ -250,6 +250,18 @@ public class PropertiesConfig
@Value("${org.highmed.dsf.bpe.mail.mailOnErrorLogEventDebugLogLocation:/opt/bpe/log/bpe.log}")
private String mailOnErrorLogEventDebugLogLocation;
+ @Documentation(description = "To enable debug log messages for every bpmn activity start, set to `true`.", recommendation = "This debug function should only be activated during process plugin development.")
+ @Value("${org.highmed.dsf.bpe.debug.log.message.onActivityStart:false}")
+ private boolean debugLogMessageOnActivityStart;
+
+ @Documentation(description = "To enable debug log messages for every bpmn activity end, set to `true`.", recommendation = "This debug function should only be activated during process plugin development.")
+ @Value("${org.highmed.dsf.bpe.debug.log.message.onActivityEnd:false}")
+ private boolean debugLogMessageOnActivityEnd;
+
+ @Documentation(description = "To enable loging bpmn variables for every bpmn activity start or end, when logging of these events is enabled, set to `true`.", recommendation = "This debug function should only be activated during process plugin development. WARNNING: Confidential information may be leaked via the debug log!")
+ @Value("${org.highmed.dsf.bpe.debug.log.message.variables:false}")
+ private boolean debugLogMessageVariables;
+
@Bean // static in order to initialize before @Configuration classes
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(
ConfigurableEnvironment environment)
@@ -548,4 +560,19 @@ public String getMailOnErrorLogEventDebugLogLocation()
{
return mailOnErrorLogEventDebugLogLocation;
}
+
+ public boolean getDebugLogMessageOnActivityStart()
+ {
+ return debugLogMessageOnActivityStart;
+ }
+
+ public boolean getDebugLogMessageOnActivityEnd()
+ {
+ return debugLogMessageOnActivityEnd;
+ }
+
+ public boolean getDebugLogMessageVariables()
+ {
+ return debugLogMessageVariables;
+ }
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHandler.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHandler.java
index 2cfd534fd..c9ac07000 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHandler.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHandler.java
@@ -47,25 +47,32 @@ public void onResource(QuestionnaireResponse questionnaireResponse)
String user = questionnaireResponse.getAuthor().getIdentifier().getValue();
String userType = questionnaireResponse.getAuthor().getType();
String businessKey = getStringValueFromItems(items, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY,
- questionnaireResponseId)
- .orElseThrow(() -> new RuntimeException(
- "Missing linkId " + CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY));
- String taskId = getStringValueFromItems(items, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID,
- questionnaireResponseId)
- .orElseThrow(() -> new RuntimeException(
- "Missing linkId " + CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID));
-
- logger.info("User task '{}' for Questionnaire '{}' completed [userTaskId: {}, businessKey: {}, user: {}]",
- questionnaireResponseId, questionnaire, taskId, businessKey, user + "|" + userType);
-
- Map variables = Map.of(BPMN_EXECUTION_VARIABLE_QUESTIONNAIRE_RESPONSE_COMPLETED,
- FhirResourceValues.create(questionnaireResponse));
- userTaskService.complete(taskId, variables);
+ questionnaireResponseId).orElse("?");
+
+ Optional userTaskIdOpt = getStringValueFromItems(items,
+ CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID, questionnaireResponseId);
+
+ userTaskIdOpt.ifPresentOrElse(userTaskId ->
+ {
+ logger.info(
+ "QuestionnaireResponse '{}' for Questionnaire '{}' completed [userTaskId: {}, businessKey: {}, user: {}]",
+ questionnaireResponseId, questionnaire, userTaskId, businessKey, user + "|" + userType);
+
+ Map variables = Map.of(BPMN_EXECUTION_VARIABLE_QUESTIONNAIRE_RESPONSE_COMPLETED,
+ FhirResourceValues.create(questionnaireResponse));
+ userTaskService.complete(userTaskId, variables);
+ }, () ->
+ {
+ logger.warn(
+ "QuestionnaireResponse '{}' for Questionnaire '{}' has no answer with item.linkId '{}' [businessKey: {}, user: {}], ignoring QuestionnaireResponse",
+ questionnaireResponseId, questionnaire, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID,
+ businessKey, user + "|" + userType);
+ });
}
- catch (Exception exception)
+ catch (Exception e)
{
- // TODO handle exception
- throw new RuntimeException(exception);
+ logger.warn("Unable to complete UserTask", e);
+ throw new RuntimeException(e);
}
}
@@ -79,7 +86,7 @@ private Optional getStringValueFromItems(
if (answers.size() == 0)
{
- logger.warn("QuestionnaireResponse with id '{}' did not contain any linkId '{}'", questionnaireResponseId,
+ logger.info("QuestionnaireResponse with id '{}' did not contain any linkId '{}'", questionnaireResponseId,
linkId);
return Optional.empty();
}
diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelperImpl.java b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelperImpl.java
index 5ef5b2c49..7134e043a 100644
--- a/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelperImpl.java
+++ b/dsf-bpe/dsf-bpe-server/src/main/java/org/highmed/dsf/fhir/questionnaire/QuestionnaireResponseHelperImpl.java
@@ -49,7 +49,8 @@ private Stream leaves(
}
@Override
- public void addItemLeave(QuestionnaireResponse questionnaireResponse, String linkId, String text, Type answer)
+ public void addItemLeafWithAnswer(QuestionnaireResponse questionnaireResponse, String linkId, String text,
+ Type answer)
{
List answerComponent = Collections
.singletonList(new QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().setValue(answer));
@@ -57,6 +58,12 @@ public void addItemLeave(QuestionnaireResponse questionnaireResponse, String lin
questionnaireResponse.addItem().setLinkId(linkId).setText(text).setAnswer(answerComponent);
}
+ @Override
+ public void addItemLeafWithoutAnswer(QuestionnaireResponse questionnaireResponse, String linkId, String text)
+ {
+ questionnaireResponse.addItem().setLinkId(linkId).setText(text);
+ }
+
@Override
public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemComponent question)
{
@@ -82,8 +89,8 @@ public Type transformQuestionTypeToAnswerType(Questionnaire.QuestionnaireItemCom
case REFERENCE:
return new Reference("http://example.org/fhir/Placeholder/id");
default:
- throw new RuntimeException(
- "Type '" + question.getType().getDisplay() + "' in Questionnaire.item is not supported");
+ throw new RuntimeException("Type '" + question.getType().getDisplay()
+ + "' in Questionnaire.item is not supported as answer type");
}
}
}
diff --git a/dsf-bpe/dsf-bpe-webservice-client/pom.xml b/dsf-bpe/dsf-bpe-webservice-client/pom.xml
index 03abb546c..896a48f7c 100755
--- a/dsf-bpe/dsf-bpe-webservice-client/pom.xml
+++ b/dsf-bpe/dsf-bpe-webservice-client/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-bpe-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-bpe/pom.xml b/dsf-bpe/pom.xml
index 33e260d2f..a26f67448 100755
--- a/dsf-bpe/pom.xml
+++ b/dsf-bpe/pom.xml
@@ -8,7 +8,7 @@
org.highmed.dsf
dsf-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-consent/dsf-consent-client-stub/pom.xml b/dsf-consent/dsf-consent-client-stub/pom.xml
index 3f565da81..76a7be8ee 100644
--- a/dsf-consent/dsf-consent-client-stub/pom.xml
+++ b/dsf-consent/dsf-consent-client-stub/pom.xml
@@ -9,7 +9,7 @@
dsf-consent-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-consent/dsf-consent-client/pom.xml b/dsf-consent/dsf-consent-client/pom.xml
index a8c6b2bdf..870cd2c59 100644
--- a/dsf-consent/dsf-consent-client/pom.xml
+++ b/dsf-consent/dsf-consent-client/pom.xml
@@ -9,7 +9,7 @@
dsf-consent-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-consent/pom.xml b/dsf-consent/pom.xml
index 24d963486..029275508 100644
--- a/dsf-consent/pom.xml
+++ b/dsf-consent/pom.xml
@@ -10,7 +10,7 @@
dsf-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-docker-test-setup-3medic-ttp-docker/docker-compose.yml b/dsf-docker-test-setup-3medic-ttp-docker/docker-compose.yml
index 7680914cd..8d477c66c 100644
--- a/dsf-docker-test-setup-3medic-ttp-docker/docker-compose.yml
+++ b/dsf-docker-test-setup-3medic-ttp-docker/docker-compose.yml
@@ -354,7 +354,7 @@ services:
ORG_HIGHMED_DSF_BPE_MAIL_FROMADDRESS: bpe@medic1-docker
ORG_HIGHMED_DSF_BPE_MAIL_TOADDRESSES: bpe@medic1-docker
#ORG_HIGHMED_DSF_BPE_MAIL_SENDTESTMAILONSTARTUP: 'false' # default no test mail on startup
- ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: highmedorg_computeFeasibility/0.6.0,highmedorg_computeDataSharing/0.6.0,highmedorg_requestUpdateResources/0.6.0,highmedorg_updateAllowList/0.6.0
+ ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: highmedorg_computeFeasibility/0.7.0,highmedorg_computeDataSharing/0.7.0,highmedorg_requestUpdateResources/0.7.0,highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
@@ -420,7 +420,7 @@ services:
ORG_HIGHMED_DSF_BPE_MAIL_FROMADDRESS: bpe@medic2-docker
ORG_HIGHMED_DSF_BPE_MAIL_TOADDRESSES: bpe@medic2-docker
#ORG_HIGHMED_DSF_BPE_MAIL_SENDTESTMAILONSTARTUP: 'false' # default no test mail on startup
- ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: highmedorg_computeFeasibility/0.6.0, highmedorg_computeDataSharing/0.6.0, highmedorg_requestUpdateResources/0.6.0, highmedorg_updateAllowList/0.6.0
+ ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: highmedorg_computeFeasibility/0.7.0, highmedorg_computeDataSharing/0.7.0, highmedorg_requestUpdateResources/0.7.0, highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
@@ -487,10 +487,10 @@ services:
ORG_HIGHMED_DSF_BPE_MAIL_TOADDRESSES: bpe@medic3-docker
#ORG_HIGHMED_DSF_BPE_MAIL_SENDTESTMAILONSTARTUP: 'false' # default no test mail on startup
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: >
- highmedorg_computeFeasibility/0.6.0,
- highmedorg_computeDataSharing/0.6.0,
- highmedorg_requestUpdateResources/0.6.0,
- highmedorg_updateAllowList/0.6.0
+ highmedorg_computeFeasibility/0.7.0,
+ highmedorg_computeDataSharing/0.7.0,
+ highmedorg_requestUpdateResources/0.7.0,
+ highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
@@ -559,16 +559,16 @@ services:
ORG_HIGHMED_DSF_BPE_MAIL_SENDTESTMAILONSTARTUP: 'true'
ORG_HIGHMED_DSF_BPE_MAIL_SENDMAILONERRORLOGEVENT: 'true'
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: |
- highmedorg_executeUpdateResources/0.6.0
- highmedorg_downloadAllowList/0.6.0
- highmedorg_localServicesIntegration/0.6.0
- highmedorg_requestFeasibility/0.6.0
- highmedorg_executeFeasibility/0.6.0
- highmedorg_requestDataSharing/0.6.0
- highmedorg_executeDataSharing/0.6.0
- highmedorg_executeFeasibilityMpcMultiShare/0.6.0
- highmedorg_executeFeasibilityMpcSingleShare/0.6.0
- highmedorg_requestFeasibilityMpc/0.6.0
+ highmedorg_executeUpdateResources/0.7.0
+ highmedorg_downloadAllowList/0.7.0
+ highmedorg_localServicesIntegration/0.7.0
+ highmedorg_requestFeasibility/0.7.0
+ highmedorg_executeFeasibility/0.7.0
+ highmedorg_requestDataSharing/0.7.0
+ highmedorg_executeDataSharing/0.7.0
+ highmedorg_executeFeasibilityMpcMultiShare/0.7.0
+ highmedorg_executeFeasibilityMpcSingleShare/0.7.0
+ highmedorg_requestFeasibilityMpc/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
diff --git a/dsf-docker-test-setup-3medic-ttp/medic1/bpe/docker-compose.yml b/dsf-docker-test-setup-3medic-ttp/medic1/bpe/docker-compose.yml
index 1c1a71865..8267c8474 100755
--- a/dsf-docker-test-setup-3medic-ttp/medic1/bpe/docker-compose.yml
+++ b/dsf-docker-test-setup-3medic-ttp/medic1/bpe/docker-compose.yml
@@ -45,10 +45,10 @@ services:
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE: Test_MeDIC_1
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_BASE_URL: https://medic1/fhir
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: |
- highmedorg_computeFeasibility/0.6.0
- highmedorg_computeDataSharing/0.6.0
- highmedorg_requestUpdateResources/0.6.0
- highmedorg_updateAllowList/0.6.0
+ highmedorg_computeFeasibility/0.7.0
+ highmedorg_computeDataSharing/0.7.0
+ highmedorg_requestUpdateResources/0.7.0
+ highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
diff --git a/dsf-docker-test-setup-3medic-ttp/medic2/bpe/docker-compose.yml b/dsf-docker-test-setup-3medic-ttp/medic2/bpe/docker-compose.yml
index e8f1bb807..85de518c8 100755
--- a/dsf-docker-test-setup-3medic-ttp/medic2/bpe/docker-compose.yml
+++ b/dsf-docker-test-setup-3medic-ttp/medic2/bpe/docker-compose.yml
@@ -45,10 +45,10 @@ services:
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE: Test_MeDIC_2
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_BASE_URL: https://medic2/fhir
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: |
- highmedorg_computeFeasibility/0.6.0
- highmedorg_computeDataSharing/0.6.0
- highmedorg_requestUpdateResources/0.6.0
- highmedorg_updateAllowList/0.6.0
+ highmedorg_computeFeasibility/0.7.0
+ highmedorg_computeDataSharing/0.7.0
+ highmedorg_requestUpdateResources/0.7.0
+ highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
diff --git a/dsf-docker-test-setup-3medic-ttp/medic3/bpe/docker-compose.yml b/dsf-docker-test-setup-3medic-ttp/medic3/bpe/docker-compose.yml
index d9bbb345b..5604b0532 100755
--- a/dsf-docker-test-setup-3medic-ttp/medic3/bpe/docker-compose.yml
+++ b/dsf-docker-test-setup-3medic-ttp/medic3/bpe/docker-compose.yml
@@ -45,10 +45,10 @@ services:
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE: Test_MeDIC_3
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_BASE_URL: https://medic3/fhir
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: |
- highmedorg_computeFeasibility/0.6.0
- highmedorg_computeDataSharing/0.6.0
- highmedorg_requestUpdateResources/0.6.0
- highmedorg_updateAllowList/0.6.0
+ highmedorg_computeFeasibility/0.7.0
+ highmedorg_computeDataSharing/0.7.0
+ highmedorg_requestUpdateResources/0.7.0
+ highmedorg_updateAllowList/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
diff --git a/dsf-docker-test-setup-3medic-ttp/ttp/bpe/docker-compose.yml b/dsf-docker-test-setup-3medic-ttp/ttp/bpe/docker-compose.yml
index 994f5da78..86c96e8a2 100755
--- a/dsf-docker-test-setup-3medic-ttp/ttp/bpe/docker-compose.yml
+++ b/dsf-docker-test-setup-3medic-ttp/ttp/bpe/docker-compose.yml
@@ -45,16 +45,16 @@ services:
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE: Test_TTP
ORG_HIGHMED_DSF_BPE_FHIR_SERVER_BASE_URL: https://ttp/fhir
ORG_HIGHMED_DSF_BPE_PROCESS_EXCLUDED: |
- highmedorg_executeUpdateResources/0.6.0
- highmedorg_downloadAllowList/0.6.0
- highmedorg_localServicesIntegration/0.6.0
- highmedorg_requestFeasibility/0.6.0
- highmedorg_executeFeasibility/0.6.0
- highmedorg_requestDataSharing/0.6.0
- highmedorg_executeDataSharing/0.6.0
- highmedorg_executeFeasibilityMpcMultiShare/0.6.0
- highmedorg_executeFeasibilityMpcSingleShare/0.6.0
- highmedorg_requestFeasibilityMpc/0.6.0
+ highmedorg_executeUpdateResources/0.7.0
+ highmedorg_downloadAllowList/0.7.0
+ highmedorg_localServicesIntegration/0.7.0
+ highmedorg_requestFeasibility/0.7.0
+ highmedorg_executeFeasibility/0.7.0
+ highmedorg_requestDataSharing/0.7.0
+ highmedorg_executeDataSharing/0.7.0
+ highmedorg_executeFeasibilityMpcMultiShare/0.7.0
+ highmedorg_executeFeasibilityMpcSingleShare/0.7.0
+ highmedorg_requestFeasibilityMpc/0.7.0
# property org.highmed.dsf.bpe.allow.list.organization should only be set for testing, do not configure property in production, potential security risk
ORG_HIGHMED_DSF_BPE_ALLOW_LIST_ORGANIZATION: Test_TTP
networks:
diff --git a/dsf-fhir/dsf-fhir-auth/pom.xml b/dsf-fhir/dsf-fhir-auth/pom.xml
index 79c4296cc..ea41878d1 100644
--- a/dsf-fhir/dsf-fhir-auth/pom.xml
+++ b/dsf-fhir/dsf-fhir-auth/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/dsf-fhir-rest-adapter/pom.xml b/dsf-fhir/dsf-fhir-rest-adapter/pom.xml
index aaccbb44a..210c29c9b 100755
--- a/dsf-fhir/dsf-fhir-rest-adapter/pom.xml
+++ b/dsf-fhir/dsf-fhir-rest-adapter/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/dsf-fhir-rest-adapter/src/main/java/org/highmed/dsf/fhir/adapter/QuestionnaireResponseHtmlFhirAdapter.java b/dsf-fhir/dsf-fhir-rest-adapter/src/main/java/org/highmed/dsf/fhir/adapter/QuestionnaireResponseHtmlFhirAdapter.java
index c87c6fff1..388e00bc6 100644
--- a/dsf-fhir/dsf-fhir-rest-adapter/src/main/java/org/highmed/dsf/fhir/adapter/QuestionnaireResponseHtmlFhirAdapter.java
+++ b/dsf-fhir/dsf-fhir-rest-adapter/src/main/java/org/highmed/dsf/fhir/adapter/QuestionnaireResponseHtmlFhirAdapter.java
@@ -113,18 +113,43 @@ private String getProcessInstanceId(QuestionnaireResponse questionnaireResponse)
private void writeRow(QuestionnaireResponse.QuestionnaireResponseItemComponent item, boolean isCompleted,
OutputStreamWriter out) throws IOException
+ {
+ if (item.hasAnswer())
+ writeFormRow(item, isCompleted, out);
+ else
+ writeDisplayRow(item, out);
+ }
+
+ private void writeDisplayRow(QuestionnaireResponse.QuestionnaireResponseItemComponent item, OutputStreamWriter out)
+ throws IOException
{
String linkId = item.getLinkId();
- String style = display(linkId) ? "" : "style=\"display:none;\"";
- out.write("\n");
+ out.write("
\n");
+ out.write("
" + item.getText() + "\n");
+ out.write("
\n");
+ }
+
+ private void writeFormRow(QuestionnaireResponse.QuestionnaireResponseItemComponent item, boolean isCompleted,
+ OutputStreamWriter out) throws IOException
+ {
+ String linkId = item.getLinkId();
+
+ out.write("
\n");
out.write("
" + item.getText() + " \n");
+
writeFormInput(item.getAnswerFirstRep(), linkId, isCompleted, out);
+
out.write("
\n");
out.write("
\n");
}
+ private String style(String linkId)
+ {
+ return display(linkId) ? "" : "style=\"display:none;\"";
+ }
+
private boolean display(String linkId)
{
return !(CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY.equals(linkId)
@@ -136,74 +161,79 @@ private void writeFormInput(QuestionnaireResponse.QuestionnaireResponseItemAnswe
{
Type type = answerPlaceholder.getValue();
- if (type instanceof StringType)
- {
- String value = ((StringType) type).getValue();
- out.write("
\n");
- }
- else if (type instanceof IntegerType)
- {
- String value = String.valueOf(((IntegerType) type).getValue());
- out.write("
\n");
- }
- else if (type instanceof DecimalType)
- {
- String value = String.valueOf(((DecimalType) type).getValue());
- out.write("
\n");
- }
- else if (type instanceof BooleanType)
- {
- boolean valueIsTrue = ((BooleanType) type).getValue();
-
- out.write("
\n");
- out.write(" Yes \n");
- out.write(" No \n");
- out.write("
\n");
- }
- else if (type instanceof DateType)
- {
- Date value = ((DateType) type).getValue();
- String date = DATE_FORMAT.format(value);
-
- out.write("
\n");
- }
- else if (type instanceof TimeType)
- {
- String value = ((TimeType) type).getValue();
- out.write("
\n");
- }
- else if (type instanceof DateTimeType)
- {
- Date value = ((DateTimeType) type).getValue();
- String dateTime = DATE_TIME_FORMAT.format(value);
-
- out.write("
\n");
- }
- else if (type instanceof UriType)
- {
- String value = ((UriType) type).getValue();
- out.write("
\n");
- }
- else if (type instanceof Reference)
- {
- String value = ((Reference) type).getReference();
- out.write("
\n");
- }
- else
+ // if type is null, the corresponding Questionnaire.item is of type display
+ if (type != null)
{
- throw new RuntimeException("Answer type '" + ((type != null) ? type.getClass().getName() : "null")
- + "' in QuestionnaireResponse.item is not supported");
+ if (type instanceof StringType)
+ {
+ String value = ((StringType) type).getValue();
+ out.write("
\n");
+ }
+ else if (type instanceof IntegerType)
+ {
+ String value = String.valueOf(((IntegerType) type).getValue());
+ out.write("
\n");
+ }
+ else if (type instanceof DecimalType)
+ {
+ String value = String.valueOf(((DecimalType) type).getValue());
+ out.write("
\n");
+ }
+ else if (type instanceof BooleanType)
+ {
+ boolean valueIsTrue = ((BooleanType) type).getValue();
+
+ out.write("
\n");
+ out.write(" Yes \n");
+ out.write(" No \n");
+ out.write("
\n");
+ }
+ else if (type instanceof DateType)
+ {
+ Date value = ((DateType) type).getValue();
+ String date = DATE_FORMAT.format(value);
+
+ out.write("
\n");
+ }
+ else if (type instanceof TimeType)
+ {
+ String value = ((TimeType) type).getValue();
+ out.write("
\n");
+ }
+ else if (type instanceof DateTimeType)
+ {
+ Date value = ((DateTimeType) type).getValue();
+ String dateTime = DATE_TIME_FORMAT.format(value);
+
+ out.write("
\n");
+ }
+ else if (type instanceof UriType)
+ {
+ String value = ((UriType) type).getValue();
+ out.write("
\n");
+ }
+ else if (type instanceof Reference)
+ {
+ String value = ((Reference) type).getReference();
+ out.write("
\n");
+ }
+ else
+ {
+ throw new RuntimeException("Answer type '" + type.getClass().getName()
+ + "' in QuestionnaireResponse.item is not supported");
+ }
}
}
}
diff --git a/dsf-fhir/dsf-fhir-server-jetty/pom.xml b/dsf-fhir/dsf-fhir-server-jetty/pom.xml
index 49d5c86a5..3bfeb3b7a 100755
--- a/dsf-fhir/dsf-fhir-server-jetty/pom.xml
+++ b/dsf-fhir/dsf-fhir-server-jetty/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/dsf-fhir-server/pom.xml b/dsf-fhir/dsf-fhir-server/pom.xml
index a3f937b7f..9db6c4ed1 100755
--- a/dsf-fhir/dsf-fhir-server/pom.xml
+++ b/dsf-fhir/dsf-fhir-server/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/AbstractMetaTagAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/AbstractMetaTagAuthorizationRule.java
index 6c31e0d7e..6dacd21ff 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/AbstractMetaTagAuthorizationRule.java
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/AbstractMetaTagAuthorizationRule.java
@@ -3,6 +3,7 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -44,6 +45,14 @@ public AbstractMetaTagAuthorizationRule(Class resourceType, DaoProvider daoPr
resourceTypeName = resourceType.getAnnotation(ResourceDef.class).name();
}
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ super.afterPropertiesSet();
+
+ Objects.requireNonNull(parameterConverter, "parameterConverter");
+ }
+
protected final boolean hasValidReadAccessTag(Connection connection, Resource resource)
{
return readAccessHelper.isValid(resource,
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java
index 3fd1afcc0..756a0bb2b 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/authorization/QuestionnaireResponseAuthorizationRule.java
@@ -1,9 +1,12 @@
package org.highmed.dsf.fhir.authorization;
+import static org.highmed.dsf.bpe.ConstantsBase.*;
+
import java.sql.Connection;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -15,39 +18,82 @@
import org.highmed.dsf.fhir.help.ParameterConverter;
import org.highmed.dsf.fhir.service.ReferenceResolver;
import org.hl7.fhir.r4.model.QuestionnaireResponse;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
+import org.hl7.fhir.r4.model.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class QuestionnaireResponseAuthorizationRule
- extends AbstractMetaTagAuthorizationRule
+ extends AbstractAuthorizationRule
{
private static final Logger logger = LoggerFactory.getLogger(QuestionnaireResponseAuthorizationRule.class);
+ private final ParameterConverter parameterConverter;
+
public QuestionnaireResponseAuthorizationRule(DaoProvider daoProvider, String serverBase,
ReferenceResolver referenceResolver, OrganizationProvider organizationProvider,
ReadAccessHelper readAccessHelper, ParameterConverter parameterConverter)
{
super(QuestionnaireResponse.class, daoProvider, serverBase, referenceResolver, organizationProvider,
- readAccessHelper, parameterConverter);
+ readAccessHelper);
+ this.parameterConverter = parameterConverter;
}
@Override
- protected Optional newResourceOkForCreate(Connection connection, User user,
- QuestionnaireResponse newResource)
+ public void afterPropertiesSet() throws Exception
{
- List errors = new ArrayList();
+ super.afterPropertiesSet();
- if (!hasValidReadAccessTag(connection, newResource))
+ Objects.requireNonNull(parameterConverter, "parameterConverter");
+ }
+
+ @Override
+ public Optional reasonCreateAllowed(Connection connection, User user, QuestionnaireResponse newResource)
+ {
+ if (isLocalUser(user))
{
- errors.add("QuestionnaireResponse is missing valid read access tag");
+ Optional errors = newResourceOk(connection, user, newResource,
+ EnumSet.of(QuestionnaireResponseStatus.INPROGRESS));
+ if (errors.isEmpty())
+ {
+ if (!resourceExists(connection, newResource))
+ {
+ logger.info(
+ "Create of QuestionnaireResponse authorized for local user '{}', QuestionnaireResponse does not exist",
+ user.getName());
+ return Optional.of("local user, QuestionnaireResponse does not exist yet");
+ }
+ else
+ {
+ logger.warn("Create of QuestionnaireResponse unauthorized, QuestionnaireResponse already exists");
+ return Optional.empty();
+ }
+ }
+ else
+ {
+ logger.warn("Create of QuestionnaireResponse unauthorized, {}", errors.get());
+ return Optional.empty();
+ }
}
+ else
+ {
+ logger.warn("Create of QuestionnaireResponse unauthorized, not a local user");
+ return Optional.empty();
+ }
+ }
+
+ private Optional newResourceOk(Connection connection, User user, QuestionnaireResponse newResource,
+ EnumSet allowedStatus)
+ {
+ List errors = new ArrayList();
if (newResource.hasStatus())
{
- if (!QuestionnaireResponseStatus.INPROGRESS.equals(newResource.getStatus()))
+ if (!allowedStatus.contains(newResource.getStatus()))
{
- errors.add("QuestionnaireResponse.status not in-progress and version 1");
+ errors.add("QuestionnaireResponse.status not one of " + allowedStatus);
}
}
else
@@ -55,51 +101,118 @@ protected Optional newResourceOkForCreate(Connection connection, User us
errors.add("QuestionnaireResponse.status missing");
}
+ getItemAndValidate(newResource, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID, errors);
+
if (errors.isEmpty())
return Optional.empty();
else
return Optional.of(errors.stream().collect(Collectors.joining(", ")));
}
- @Override
- protected Optional newResourceOkForUpdate(Connection connection, User user,
- QuestionnaireResponse newResource)
+ private Optional getItemAndValidate(QuestionnaireResponse newResource, String linkId, List errors)
{
- List errors = new ArrayList();
+ List userTaskIds = newResource.getItem().stream()
+ .filter(QuestionnaireResponseItemComponent::hasLinkId).filter(i -> linkId.equals(i.getLinkId()))
+ .collect(Collectors.toList());
- if (!hasValidReadAccessTag(connection, newResource))
+ if (userTaskIds.size() != 1)
{
- errors.add("QuestionnaireResponse is missing valid read access tag");
+ if (errors != null)
+ errors.add("QuestionnaireResponse.item('user-task-id') missing or more than one");
+
+ return Optional.empty();
}
- if (newResource.hasStatus())
+ QuestionnaireResponseItemComponent item = userTaskIds.get(0);
+
+ if (!item.hasAnswer() || item.getAnswer().size() != 1)
{
- if (!EnumSet.of(QuestionnaireResponseStatus.COMPLETED, QuestionnaireResponseStatus.STOPPED)
- .contains(newResource.getStatus()))
- {
- errors.add("QuestionnaireResponse.status not (completed or stopped) and version 2");
- }
+ if (errors != null)
+ errors.add("QuestionnaireResponse.item('user-task-id').answer missing or more than one");
+
+ return Optional.empty();
}
- else
+
+ QuestionnaireResponseItemAnswerComponent answer = item.getAnswerFirstRep();
+
+ if (!answer.hasValue() || !(answer.getValue() instanceof StringType))
{
- errors.add("QuestionnaireResponse.status missing");
+ if (errors != null)
+ errors.add("QuestionnaireResponse.item('user-task-id').answer.value missing or not a string");
+
+ return Optional.empty();
}
- if (errors.isEmpty())
+ StringType value = (StringType) answer.getValue();
+
+ if (!value.hasValue())
+ {
+ if (errors != null)
+ errors.add("QuestionnaireResponse.item('user-task-id').answer.value is blank");
+
return Optional.empty();
- else
- return Optional.of(errors.stream().collect(Collectors.joining(", ")));
+ }
+
+ return Optional.of(value.getValue());
}
- @Override
- protected boolean resourceExists(Connection connection, QuestionnaireResponse newResource)
+ private boolean resourceExists(Connection connection, QuestionnaireResponse newResource)
{
- // no unique criteria for QuestionnaireResponse
+ // TODO implement unique criteria based on UserTask.id when implemented as identifier
return false;
}
@Override
- protected boolean modificationsOk(Connection connection, QuestionnaireResponse oldResource,
+ public Optional reasonReadAllowed(Connection connection, User user, QuestionnaireResponse existingResource)
+ {
+ if (isLocalUser(user))
+ {
+ logger.info("Read of QuestionnaireResponse authorized for local user '{}'", user.getName());
+ return Optional.of("task.restriction.recipient resolved and local user part of referenced organization");
+ }
+ else
+ {
+ logger.warn("Read of QuestionnaireResponse unauthorized, not a local user", user.getName());
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Optional reasonUpdateAllowed(Connection connection, User user, QuestionnaireResponse oldResource,
+ QuestionnaireResponse newResource)
+ {
+ if (isLocalUser(user))
+ {
+ Optional errors = newResourceOk(connection, user, newResource,
+ EnumSet.of(QuestionnaireResponseStatus.COMPLETED, QuestionnaireResponseStatus.STOPPED));
+ if (errors.isEmpty())
+ {
+ if (modificationsOk(connection, oldResource, newResource))
+ {
+ logger.info("Update of QuestionnaireResponse authorized for local user '{}', modification allowed",
+ user.getName());
+ return Optional.of("local user; modification allowed");
+ }
+ else
+ {
+ logger.warn("Update of QuestionnaireResponse unauthorized, modification not allowed");
+ return Optional.empty();
+ }
+ }
+ else
+ {
+ logger.warn("Update of QuestionnaireResponse unauthorized, {}", errors.get());
+ return Optional.empty();
+ }
+ }
+ else
+ {
+ logger.warn("Update of QuestionnaireResponse unauthorized, not a local user");
+ return Optional.empty();
+ }
+ }
+
+ private boolean modificationsOk(Connection connection, QuestionnaireResponse oldResource,
QuestionnaireResponse newResource)
{
boolean statusModificationOk = QuestionnaireResponseStatus.INPROGRESS.equals(oldResource.getStatus())
@@ -113,6 +226,78 @@ protected boolean modificationsOk(Connection connection, QuestionnaireResponse o
QuestionnaireResponseStatus.COMPLETED + "|" + QuestionnaireResponseStatus.STOPPED,
oldResource.getStatus(), newResource.getStatus());
- return statusModificationOk;
+ String oldUserTaskId = getItemAndValidate(oldResource, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID,
+ new ArrayList<>()).orElse(null);
+ String newUserTaskId = getItemAndValidate(newResource, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID,
+ new ArrayList<>()).orElse(null);
+
+ boolean userTaskIdOk = Objects.equals(oldUserTaskId, newUserTaskId);
+
+ if (!userTaskIdOk)
+ logger.warn(
+ "Modifications only allowed if item.answer with linkId '{}' not changed, change from '{}' to '{}' not allowed",
+ CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID, oldUserTaskId, newUserTaskId);
+
+ String oldBusinessKey = getItemAndValidate(oldResource, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY,
+ new ArrayList<>()).orElse(null);
+ String newBusinessKey = getItemAndValidate(newResource, CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY,
+ new ArrayList<>()).orElse(null);
+
+ boolean businesssKeyOk = Objects.equals(oldBusinessKey, newBusinessKey);
+
+ if (!userTaskIdOk)
+ logger.warn(
+ "Modifications only allowed if item.answer with linkId '{}' not changed, change from '{}' to '{}' not allowed",
+ CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_BUSINESS_KEY, oldUserTaskId, newUserTaskId);
+
+ return statusModificationOk && userTaskIdOk && businesssKeyOk;
+ }
+
+ @Override
+ public Optional reasonDeleteAllowed(Connection connection, User user, QuestionnaireResponse oldResource)
+ {
+ if (isLocalUser(user))
+ {
+ logger.info("Delete of QuestionnaireResponse authorized for local user '{}'", user.getName());
+ return Optional.of("local user");
+ }
+ else
+ {
+ logger.warn("Delete of QuestionnaireResponse unauthorized, not a local user");
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public final Optional reasonSearchAllowed(User user)
+ {
+ logger.info("Search of QuestionnaireResponse authorized for {} user '{}', will be filtered by user role",
+ user.getRole(), user.getName());
+ return Optional.of("Allowed for all, filtered by user role");
+ }
+
+ @Override
+ public final Optional reasonHistoryAllowed(User user)
+ {
+ logger.info("History of {} authorized for {} user '{}', will be filtered by user role", user.getRole(),
+ user.getName());
+ return Optional.of("Allowed for all, filtered by user role");
+ }
+
+ @Override
+ public Optional reasonPermanentDeleteAllowed(Connection connection, User user,
+ QuestionnaireResponse oldResource)
+ {
+ if (isLocalPermanentDeleteUser(user))
+ {
+ logger.info("Permanent delete of QuestionnaireResponse authorized for local delete user '{}'", resourceType,
+ user.getName());
+ return Optional.of("local delete user");
+ }
+ else
+ {
+ logger.warn("Permanent delete of QuestionnaireResponse unauthorized, not a local delete user");
+ return Optional.empty();
+ }
}
}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/HistoryUserFilterFactoryImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/HistoryUserFilterFactoryImpl.java
index cff351bc5..e6f8c8258 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/HistoryUserFilterFactoryImpl.java
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/HistoryUserFilterFactoryImpl.java
@@ -26,6 +26,8 @@
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PractitionerRole;
import org.hl7.fhir.r4.model.Provenance;
+import org.hl7.fhir.r4.model.Questionnaire;
+import org.hl7.fhir.r4.model.QuestionnaireResponse;
import org.hl7.fhir.r4.model.ResearchStudy;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StructureDefinition;
@@ -58,6 +60,8 @@ public HistoryUserFilterFactoryImpl()
filtersByResource.put(Practitioner.class, PractitionerHistoryUserFilter::new);
filtersByResource.put(PractitionerRole.class, PractitionerRoleHistoryUserFilter::new);
filtersByResource.put(Provenance.class, ProvenanceHistoryUserFilter::new);
+ filtersByResource.put(Questionnaire.class, QuestionnaireHistoryUserFilter::new);
+ filtersByResource.put(QuestionnaireResponse.class, QuestionnaireResponseHistoryUserFilter::new);
filtersByResource.put(ResearchStudy.class, ResearchStudyHistoryUserFilter::new);
filtersByResource.put(StructureDefinition.class, StructureDefinitionHistoryUserFilter::new);
filtersByResource.put(Subscription.class, SubscriptionHistoryUserFilter::new);
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireHistoryUserFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireHistoryUserFilter.java
new file mode 100644
index 000000000..e73a58c72
--- /dev/null
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireHistoryUserFilter.java
@@ -0,0 +1,23 @@
+package org.highmed.dsf.fhir.history.user;
+
+import org.highmed.dsf.fhir.authentication.User;
+import org.highmed.dsf.fhir.search.parameters.user.QuestionnaireUserFilter;
+import org.hl7.fhir.r4.model.Questionnaire;
+
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+
+public class QuestionnaireHistoryUserFilter extends QuestionnaireUserFilter implements HistoryUserFilter
+{
+ private static final String RESOURCE_TYPE = Questionnaire.class.getAnnotation(ResourceDef.class).name();
+
+ public QuestionnaireHistoryUserFilter(User user)
+ {
+ super(user, HistoryUserFilter.RESOURCE_TABLE, HistoryUserFilter.RESOURCE_ID_COLUMN);
+ }
+
+ @Override
+ public String getFilterQuery()
+ {
+ return HistoryUserFilter.getFilterQuery(RESOURCE_TYPE, super.getFilterQuery());
+ }
+}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireResponseHistoryUserFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireResponseHistoryUserFilter.java
new file mode 100644
index 000000000..8079d0298
--- /dev/null
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/history/user/QuestionnaireResponseHistoryUserFilter.java
@@ -0,0 +1,23 @@
+package org.highmed.dsf.fhir.history.user;
+
+import org.highmed.dsf.fhir.authentication.User;
+import org.highmed.dsf.fhir.search.parameters.user.QuestionnaireResponseUserFilter;
+import org.hl7.fhir.r4.model.QuestionnaireResponse;
+
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+
+public class QuestionnaireResponseHistoryUserFilter extends QuestionnaireResponseUserFilter implements HistoryUserFilter
+{
+ private static final String RESOURCE_TYPE = QuestionnaireResponse.class.getAnnotation(ResourceDef.class).name();
+
+ public QuestionnaireResponseHistoryUserFilter(User user)
+ {
+ super(user);
+ }
+
+ @Override
+ public String getFilterQuery()
+ {
+ return HistoryUserFilter.getFilterQuery(RESOURCE_TYPE, super.getFilterQuery());
+ }
+}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/user/QuestionnaireResponseUserFilter.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/user/QuestionnaireResponseUserFilter.java
index f21f32a16..07aac517b 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/user/QuestionnaireResponseUserFilter.java
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/search/parameters/user/QuestionnaireResponseUserFilter.java
@@ -1,19 +1,41 @@
package org.highmed.dsf.fhir.search.parameters.user;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
import org.highmed.dsf.fhir.authentication.User;
+import org.highmed.dsf.fhir.authentication.UserRole;
-public class QuestionnaireResponseUserFilter extends AbstractMetaTagAuthorizationRoleUserFilter
+public class QuestionnaireResponseUserFilter extends AbstractUserFilter
{
- private static final String RESOURCE_TABLE = "current_questionnaire_responses";
- private static final String RESOURCE_ID_COLUMN = "questionnaire_response_id";
-
public QuestionnaireResponseUserFilter(User user)
{
- super(user, RESOURCE_TABLE, RESOURCE_ID_COLUMN);
+ super(user, null, null);
+ }
+
+ @Override
+ public String getFilterQuery()
+ {
+ // read allowed for local users
+ if (UserRole.LOCAL.equals(user.getRole()))
+ return "";
+
+ // read not allowed for non local users
+ else
+ return "FALSE";
+ }
+
+ @Override
+ public int getSqlParameterCount()
+ {
+ // no parameters
+ return 0;
}
- public QuestionnaireResponseUserFilter(User user, String resourceTable, String resourceIdColumn)
+ @Override
+ public void modifyStatement(int parameterIndex, int subqueryParameterIndex, PreparedStatement statement)
+ throws SQLException
{
- super(user, resourceTable, resourceIdColumn);
+ // no parameters to modify
}
}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java
index e196a5ad7..4da402786 100755
--- a/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java
+++ b/dsf-fhir/dsf-fhir-server/src/main/java/org/highmed/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java
@@ -235,17 +235,36 @@ else if (read.hasEntity())
{
audit.info("Read of resource {} denied for user '{}' ({}), not a {}", resourceTypeName + "/" + id,
getCurrentUser().getName(), getCurrentUser().getSubjectDn(), resourceTypeName);
-
- logger.warn("Not allowing access to entity of type {}", read.getEntity().getClass().getName());
return forbidden("read");
}
+ else if (!read.hasEntity() && Status.NOT_MODIFIED.getStatusCode() == read.getStatus())
+ {
+ Optional dbResource = exceptionHandler.handleSqlAndResourceDeletedException(serverBase, resourceTypeName,
+ () -> dao.read(parameterConverter.toUuid(resourceTypeName, id)));
+ Optional reasonReadAllowed = authorizationRule.reasonReadAllowed(getCurrentUser(),
+ dbResource.get());
+
+ if (reasonReadAllowed.isEmpty())
+ {
+ audit.info("Read of resource {} denied for user '{}' ({})", dbResource.get().getIdElement().getValue(),
+ getCurrentUser().getName(), getCurrentUser().getSubjectDn());
+ return forbidden("read");
+ }
+ else
+ {
+ audit.info("Read of resource {} allowed for user '{}' ({}): {}",
+ dbResource.get().getIdElement().getValue(), getCurrentUser().getName(),
+ getCurrentUser().getSubjectDn(), reasonReadAllowed.get());
+ return read;
+ }
+ }
else
{
audit.info("Read of resource {} for user '{}' ({}) returned without entity, status {}",
resourceTypeName + "/" + id, getCurrentUser().getName(), getCurrentUser().getSubjectDn(),
read.getStatus());
- logger.warn("Returning with status {}, but no entity", read.getStatus());
+ logger.info("Returning with status {}, but no entity", read.getStatus());
return read;
}
}
@@ -289,17 +308,36 @@ else if (read.hasEntity())
audit.info("Read of resource {} denied for user '{}' ({}), not a {}",
resourceTypeName + "/" + id + "/_history/" + version, getCurrentUser().getName(),
getCurrentUser().getSubjectDn(), resourceTypeName);
-
- logger.warn("Not allowing access to entity of type {}", read.getEntity().getClass().getName());
return forbidden("read");
}
+ else if (!read.hasEntity() && Status.NOT_MODIFIED.getStatusCode() == read.getStatus())
+ {
+ Optional dbResource = exceptionHandler.handleSqlAndResourceDeletedException(serverBase, resourceTypeName,
+ () -> dao.readVersion(parameterConverter.toUuid(resourceTypeName, id), version));
+ Optional reasonReadAllowed = authorizationRule.reasonReadAllowed(getCurrentUser(),
+ dbResource.get());
+
+ if (reasonReadAllowed.isEmpty())
+ {
+ audit.info("Read of resource {} denied for user '{}' ({})", dbResource.get().getIdElement().getValue(),
+ getCurrentUser().getName(), getCurrentUser().getSubjectDn());
+ return forbidden("read");
+ }
+ else
+ {
+ audit.info("Read of resource {} allowed for user '{}' ({}): {}",
+ dbResource.get().getIdElement().getValue(), getCurrentUser().getName(),
+ getCurrentUser().getSubjectDn(), reasonReadAllowed.get());
+ return read;
+ }
+ }
else
{
audit.info("Read of resource {} for user '{}' ({}) returned without entity, status {}",
resourceTypeName + "/" + id + "/_history/" + version, getCurrentUser().getName(),
getCurrentUser().getSubjectDn(), read.getStatus());
- logger.warn("Returning with status {}, but no entity", read.getStatus());
+ logger.info("Returning with status {}, but no entity", read.getStatus());
return read;
}
}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.changelog.xml b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.changelog.xml
index 5a3e26361..344e6e0bf 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.changelog.xml
+++ b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.changelog.xml
@@ -65,4 +65,7 @@
+
+
+
diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.history.changelog-0.9.0.xml b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.history.changelog-0.9.0.xml
new file mode 100644
index 000000000..77038a510
--- /dev/null
+++ b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.history.changelog-0.9.0.xml
@@ -0,0 +1,372 @@
+
+
+
+
+
+
+
+
+ SELECT id, version, type, method, last_updated, resource
+ FROM (
+
+ SELECT activity_definition_id AS id, version, 'ActivityDefinition' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (activity_definition->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ activity_definition AS resource
+ FROM activity_definitions
+
+ UNION
+
+ SELECT activity_definition_id AS id, version + 1, 'ActivityDefinition' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM activity_definitions
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT binary_id AS id, version, 'Binary' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (binary_json->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ binary_json AS resource
+ FROM binaries
+
+ UNION
+
+ SELECT binary_id AS id, version + 1, 'Binary' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM binaries
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT bundle_id AS id, version, 'Bundle' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (bundle->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ bundle AS resource
+ FROM bundles
+
+ UNION
+
+ SELECT bundle_id AS id, version + 1, 'Bundle' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM bundles
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT code_system_id AS id, version, 'CodeSystem' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (code_system->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ code_system AS resource
+ FROM code_systems
+
+ UNION
+
+ SELECT code_system_id AS id, version + 1, 'CodeSystem' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM code_systems
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT endpoint_id AS id, version, 'Endpoint' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (endpoint->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ endpoint AS resource
+ FROM endpoints
+
+ UNION
+
+ SELECT endpoint_id AS id, version + 1, 'Endpoint' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM endpoints
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT group_id AS id, version, 'Group' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (group_json->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ group_json AS resource
+ FROM groups
+
+ UNION
+
+ SELECT group_id AS id, version + 1, 'Group' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM groups
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT healthcare_service_id AS id, version, 'HealthcareService' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (healthcare_service->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ healthcare_service AS resource
+ FROM healthcare_services
+
+ UNION
+
+ SELECT healthcare_service_id AS id, version + 1, 'HealthcareService' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM healthcare_services
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT library_id AS id, version, 'Library' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (library->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ library AS resource
+ FROM libraries
+
+ UNION
+
+ SELECT library_id AS id, version + 1, 'Library' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM libraries
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT location_id AS id, version, 'Location' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (location->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ location AS resource
+ FROM locations
+
+ UNION
+
+ SELECT location_id AS id, version + 1, 'Location' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM locations
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT measure_id AS id, version, 'Measure' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (measure->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ measure AS resource
+ FROM measures
+
+ UNION
+
+ SELECT measure_id AS id, version + 1, 'Measure' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM measures
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT measure_report_id AS id, version, 'MeasureReport' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (measure_report->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ measure_report AS resource
+ FROM measure_reports
+
+ UNION
+
+ SELECT measure_report_id AS id, version + 1, 'MeasureReport' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM measure_reports
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT naming_system_id AS id, version, 'NamingSystem' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (naming_system->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ naming_system AS resource
+ FROM naming_systems
+
+ UNION
+
+ SELECT naming_system_id AS id, version + 1, 'NamingSystem' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM naming_systems
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT organization_id AS id, version, 'Organization' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (organization->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ organization AS resource
+ FROM organizations
+
+ UNION
+
+ SELECT organization_id AS id, version + 1, 'Organization' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM organizations
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT organization_affiliation_id AS id, version, 'OrganizationAffiliation' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (organization_affiliation->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ organization_affiliation AS resource
+ FROM organization_affiliations
+
+ UNION
+
+ SELECT organization_affiliation_id AS id, version + 1, 'OrganizationAffiliation' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM organization_affiliations
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT questionnaire_id AS id, version, 'Questionnaire' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (questionnaire->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ questionnaire AS resource
+ FROM questionnaires
+
+ UNION
+
+ SELECT questionnaire_id AS id, version + 1, 'Questionnaire' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM questionnaires
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT questionnaire_response_id AS id, version, 'QuestionnaireResponse' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (questionnaire_response->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ questionnaire_response AS resource
+ FROM questionnaire_responses
+
+ UNION
+
+ SELECT questionnaire_response_id AS id, version + 1, 'QuestionnaireResponse' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM questionnaire_responses
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT patient_id AS id, version, 'Patient' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (patient->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ patient AS resource
+ FROM patients
+
+ UNION
+
+ SELECT patient_id AS id, version + 1, 'Patient' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM patients
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT practitioner_role_id AS id, version, 'PractitionerRole' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (practitioner_role->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ practitioner_role AS resource
+ FROM practitioner_roles
+
+ UNION
+
+ SELECT practitioner_role_id AS id, version + 1, 'PractitionerRole' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM practitioner_roles
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT practitioner_id AS id, version, 'Practitioner' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (practitioner->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ practitioner AS resource
+ FROM practitioners
+
+ UNION
+
+ SELECT practitioner_id AS id, version + 1, 'Practitioner' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM practitioners
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT provenance_id AS id, version, 'Provenance' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (provenance->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ provenance AS resource
+ FROM provenances
+
+ UNION
+
+ SELECT provenance_id AS id, version + 1, 'Provenance' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM provenances
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT research_study_id AS id, version, 'ResearchStudy' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (research_study->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ research_study AS resource
+ FROM research_studies
+
+ UNION
+
+ SELECT research_study_id AS id, version + 1, 'ResearchStudy' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM research_studies
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT structure_definition_id AS id, version, 'StructureDefinition' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (structure_definition->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ structure_definition AS resource
+ FROM structure_definitions
+
+ UNION
+
+ SELECT structure_definition_id AS id, version + 1, 'StructureDefinition' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM structure_definitions
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT subscription_id AS id, version, 'Subscription' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (subscription->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ subscription AS resource
+ FROM subscriptions
+
+ UNION
+
+ SELECT subscription_id AS id, version + 1, 'Subscription' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM subscriptions
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT task_id AS id, version, 'Task' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (task->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ task AS resource
+ FROM tasks
+
+ UNION
+
+ SELECT task_id AS id, version + 1, 'Task' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM tasks
+ WHERE deleted IS NOT NULL
+
+ UNION
+
+ SELECT value_set_id AS id, version, 'ValueSet' AS type,
+ CASE WHEN version = 1 THEN 'POST' ELSE 'PUT' END AS method,
+ (value_set->'meta'->>'lastUpdated')::TIMESTAMP AS last_updated,
+ value_set AS resource
+ FROM value_sets
+
+ UNION
+
+ SELECT value_set_id AS id, version + 1, 'ValueSet' AS type, 'DELETE' AS method, deleted AS last_updated, NULL AS resource
+ FROM value_sets
+ WHERE deleted IS NOT NULL
+
+ ) AS history
+ ORDER BY last_updated, id, version
+
+
+
+ ALTER TABLE history OWNER TO ${db.liquibase_user};
+ GRANT ALL ON TABLE history TO ${db.liquibase_user};
+ GRANT SELECT ON TABLE history TO ${db.server_users_group};
+
+
+
\ No newline at end of file
diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.questionnaire_responses.changelog-0.9.0.xml b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.questionnaire_responses.changelog-0.9.0.xml
new file mode 100644
index 000000000..ef112f272
--- /dev/null
+++ b/dsf-fhir/dsf-fhir-server/src/main/resources/db/db.questionnaire_responses.changelog-0.9.0.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+ DROP TRIGGER questionnaire_responses_insert ON questionnaire_responses;
+ DROP TRIGGER questionnaire_responses_update ON questionnaire_responses;
+ DROP FUNCTION on_questionnaire_responses_update;
+ DROP FUNCTION on_questionnaire_responses_insert;
+
+
+
diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.css b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.css
index 8deb6d22e..41dcb0b29 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.css
+++ b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.css
@@ -20,6 +20,14 @@ fieldset#qr-form-fieldset {
background-color: #f2f2f2;
}
+.row-display {
+ padding-top: 15px;
+}
+
+.p-display {
+ margin: 0;
+}
+
.error {
background-color: #ffa590;
}
diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js
index 2b1a7b0ca..b6e5e388f 100644
--- a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js
+++ b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js
@@ -18,12 +18,15 @@ function readAnswersFromForm(questionnaireResponse, errors) {
questionnaireResponse.status = "completed";
questionnaireResponse.item.forEach((item) => {
- const id = item.linkId
- const answer = item.answer[0]
- const answerType = Object.keys(answer)[0]
+ if (item.hasOwnProperty('answer')) {
+ const id = item.linkId
- if (id !== "business-key" && id !== "user-task-id") {
- answer[answerType] = readAndValidateValue(id, answerType, errors)
+ if (id !== "business-key" && id !== "user-task-id") {
+ const answer = item.answer[0]
+ const answerType = Object.keys(answer)[0]
+
+ answer[answerType] = readAndValidateValue(id, answerType, errors)
+ }
}
})
}
@@ -31,7 +34,7 @@ function readAnswersFromForm(questionnaireResponse, errors) {
function readAndValidateValue(id, answerType, errors) {
const value = document.getElementById(id).value
- const rowElement = document.getElementById(id + "-row");
+ const rowElement = document.getElementById(id + "-answer-row");
const errorListElement = document.getElementById(id + "-error");
errorListElement.replaceChildren()
@@ -196,7 +199,13 @@ function updateQuestionnaireResponse(questionnaireResponse) {
disableSpinner()
window.scrollTo(0, 0);
location.reload();
- } else {
+ } else if (response.status >= 400 && response.status < 600) {
+ response.text().then((responseText) => {
+ document.open();
+ document.write(responseText);
+ document.close();
+ });
+ } else {
const status = response.status
const statusText = response.statusText === null ? " - " + response.statusText : ""
diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/dao/QuestionnaireResponseDaoTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/dao/QuestionnaireResponseDaoTest.java
index e962355d2..71a09b1a3 100755
--- a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/dao/QuestionnaireResponseDaoTest.java
+++ b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/dao/QuestionnaireResponseDaoTest.java
@@ -1,15 +1,13 @@
package org.highmed.dsf.fhir.dao;
-import static org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import static org.junit.Assert.assertEquals;
import org.highmed.dsf.fhir.dao.jdbc.QuestionnaireResponseDaoJdbc;
import org.hl7.fhir.r4.model.QuestionnaireResponse;
-import org.junit.Test;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
public class QuestionnaireResponseDaoTest
extends AbstractResourceDaoTest
- implements ReadAccessDaoTest
{
public QuestionnaireResponseDaoTest()
{
@@ -42,200 +40,4 @@ protected void checkUpdates(QuestionnaireResponse resource)
{
assertEquals(QuestionnaireResponseStatus.COMPLETED, resource.getStatus());
}
-
- @Override
- @Test
- public void testReadAccessTriggerAll() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerAll();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerLocal() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerLocal();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganization() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganization();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganizationResourceFirst() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganizationResourceFirst();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganization2Organizations1Matching() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganization2Organizations1Matching();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganization2Organizations2Matching() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganization2Organizations2Matching();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRole() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRole();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleResourceFirst() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleResourceFirst();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRole2Organizations1Matching() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRole2Organizations1Matching();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRole2Organizations2Matching() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRole2Organizations2Matching();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerAllUpdate() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerAllUpdate();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerLocalUpdate() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerLocalUpdate();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganizationUpdate() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganizationUpdate();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleUpdate() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleUpdate();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleUpdateMemberOrganizationNonActive() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleUpdateMemberOrganizationNonActive();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleUpdateParentOrganizationNonActive() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleUpdateParentOrganizationNonActive();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleUpdateMemberAndParentOrganizationNonActive() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleUpdateMemberAndParentOrganizationNonActive();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerAllDelete() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerAllDelete();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerLocalDelete() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerLocalDelete();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerOrganizationDelete() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerOrganizationDelete();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleDelete() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleDelete();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleDeleteMember() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleDeleteMember();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleDeleteParent() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleDeleteParent();
- }
-
- @Override
- @Test
- public void testReadAccessTriggerRoleDeleteMemberAndParent() throws Exception
- {
- ReadAccessDaoTest.super.testReadAccessTriggerRoleDeleteMemberAndParent();
- }
-
- @Override
- @Test
- public void testSearchWithUserFilterAfterReadAccessTriggerAllWithLocalUser() throws Exception
- {
- ReadAccessDaoTest.super.testSearchWithUserFilterAfterReadAccessTriggerAllWithLocalUser();
- }
-
- @Override
- @Test
- public void testSearchWithUserFilterAfterReadAccessTriggerLocalwithLocalUser() throws Exception
- {
- ReadAccessDaoTest.super.testSearchWithUserFilterAfterReadAccessTriggerLocalwithLocalUser();
- }
-
- @Override
- @Test
- public void testSearchWithUserFilterAfterReadAccessTriggerAllWithRemoteUser() throws Exception
- {
- ReadAccessDaoTest.super.testSearchWithUserFilterAfterReadAccessTriggerAllWithRemoteUser();
- }
-
- @Override
- @Test
- public void testSearchWithUserFilterAfterReadAccessTriggerLocalWithRemoteUser() throws Exception
- {
- ReadAccessDaoTest.super.testSearchWithUserFilterAfterReadAccessTriggerLocalWithRemoteUser();
- }
}
diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java
index 2a68d5574..f82a44f03 100644
--- a/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java
+++ b/dsf-fhir/dsf-fhir-server/src/test/java/org/highmed/dsf/fhir/integration/QuestionnaireResponseIntegrationTest.java
@@ -3,17 +3,22 @@
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Date;
+import java.util.List;
import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
+import org.highmed.dsf.bpe.ConstantsBase;
import org.highmed.dsf.fhir.authentication.OrganizationProvider;
import org.highmed.dsf.fhir.dao.QuestionnaireDao;
import org.highmed.dsf.fhir.dao.QuestionnaireResponseDao;
@@ -25,14 +30,14 @@
import org.hl7.fhir.r4.model.QuestionnaireResponse;
import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.StringType;
+import org.hl7.fhir.r4.model.Type;
import org.junit.Test;
public class QuestionnaireResponseIntegrationTest extends AbstractIntegrationTest
{
private static final Date AUTHORED = Date
.from(LocalDateTime.parse("2022-01-01T00:00:00").toInstant(ZoneOffset.UTC));
- private static final String IDENTIFIER_SYSTEM = "http://highmed.org/fhir/CodeSystem/user-task-id";
- private static final String IDENTIFIER_VALUE = "foo";
private static final String QUESTIONNAIRE_URL = "http://highmed.org/fhir/Questionnaire/userTask/foo";
private static final String QUESTIONNAIRE_VERSION = "1.0.0";
private static final String QUESTIONNAIRE = QUESTIONNAIRE_URL + "|" + QUESTIONNAIRE_VERSION;
@@ -66,6 +71,22 @@ public void testCreateNotAllowedByLocalUser() throws Exception
}
}
+ @Test(expected = WebApplicationException.class)
+ public void testCreateNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+
+ try
+ {
+ getExternalWebserviceClient().create(questionnaireResponse);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
@Test
public void testUpdateAllowedByLocalUser() throws Exception
{
@@ -103,6 +124,52 @@ public void testUpdateNotAllowedByLocalUser() throws Exception
}
}
+ @Test(expected = WebApplicationException.class)
+ public void testUpdateNotAllowedByLocalUserNowUserTaskId() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ created.setStatus(QuestionnaireResponseStatus.STOPPED);
+ created.getItem().clear();
+
+ try
+ {
+ getWebserviceClient().update(created);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testUpdateNotAllowedByLocalUserChangedUserTaskId() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ created.setStatus(QuestionnaireResponseStatus.STOPPED);
+ created.getItem().clear();
+ addItem(created, ConstantsBase.CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID, "UserTask ID",
+ new StringType(UUID.randomUUID().toString()));
+
+ try
+ {
+ getWebserviceClient().update(created);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
@Test(expected = WebApplicationException.class)
public void testSecondUpdateNotAllowedByLocalUser() throws Exception
{
@@ -124,6 +191,26 @@ public void testSecondUpdateNotAllowedByLocalUser() throws Exception
}
}
+ @Test(expected = WebApplicationException.class)
+ public void testUpdateNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ created.setStatus(QuestionnaireResponseStatus.COMPLETED);
+ try
+ {
+ getExternalWebserviceClient().update(created);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
@Test
public void testSearchByDate() throws Exception
{
@@ -150,13 +237,17 @@ public void testSearchByDate() throws Exception
@Test
public void testSearchByIdentifier() throws Exception
{
+ final String value = UUID.randomUUID().toString();
+ final String system = "http://foo/fhir/sid/Test";
+
QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ questionnaireResponse.getIdentifier().setSystem(system).setValue(value);
QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
.getBean(QuestionnaireResponseDao.class);
questionnaireResponseDao.create(questionnaireResponse);
Bundle searchBundle = getWebserviceClient().search(QuestionnaireResponse.class,
- Map.of("identifier", Collections.singletonList(IDENTIFIER_SYSTEM + "|" + IDENTIFIER_VALUE)));
+ Map.of("identifier", Collections.singletonList(system + "|" + value)));
assertNotNull(searchBundle.getEntry());
assertEquals(1, searchBundle.getEntry().size());
@@ -164,11 +255,28 @@ public void testSearchByIdentifier() throws Exception
assertNotNull(searchBundle.getEntry().get(0).getResource());
assertTrue(searchBundle.getEntry().get(0).getResource() instanceof QuestionnaireResponse);
- QuestionnaireResponse searchQuestionnaireResponse = (QuestionnaireResponse) searchBundle.getEntry().get(0)
+ QuestionnaireResponse foundQuestionnaireResponse = (QuestionnaireResponse) searchBundle.getEntry().get(0)
.getResource();
- assertTrue(searchQuestionnaireResponse.hasIdentifier());
- assertEquals(IDENTIFIER_SYSTEM, searchQuestionnaireResponse.getIdentifier().getSystem());
- assertEquals(IDENTIFIER_VALUE, searchQuestionnaireResponse.getIdentifier().getValue());
+ assertTrue(foundQuestionnaireResponse.hasIdentifier());
+ }
+
+ @Test
+ public void testSearchByIdentifierRemoteUser() throws Exception
+ {
+ final String value = UUID.randomUUID().toString();
+ final String system = "http://foo/fhir/sid/Test";
+
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ questionnaireResponse.getIdentifier().setSystem(system).setValue(value);
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ questionnaireResponseDao.create(questionnaireResponse);
+
+ Bundle searchBundle = getExternalWebserviceClient().search(QuestionnaireResponse.class,
+ Map.of("identifier", Collections.singletonList(system + "|" + value)));
+
+ assertNotNull(searchBundle.getEntry());
+ assertEquals(0, searchBundle.getEntry().size());
}
@Test
@@ -392,10 +500,6 @@ private QuestionnaireResponse createQuestionnaireResponse()
assertNotNull(organizationProvider);
QuestionnaireResponse questionnaireResponse = new QuestionnaireResponse();
- questionnaireResponse.getMeta().addTag().setSystem("http://highmed.org/fhir/CodeSystem/read-access-tag")
- .setCode("ALL");
-
- questionnaireResponse.getIdentifier().setSystem(IDENTIFIER_SYSTEM).setValue(IDENTIFIER_VALUE);
questionnaireResponse.setQuestionnaire(QUESTIONNAIRE);
@@ -406,9 +510,273 @@ private QuestionnaireResponse createQuestionnaireResponse()
+ organizationProvider.getLocalOrganization().get().getIdElement().getIdPart();
questionnaireResponse.setSubject(new Reference(organizationReference));
- questionnaireResponse.addItem().setLinkId("foo").setText("Approve?").addAnswer()
- .setValue(new BooleanType(true));
+ addItem(questionnaireResponse, ConstantsBase.CODESYSTEM_HIGHMED_BPMN_USER_TASK_VALUE_USER_TASK_ID,
+ "UserTask ID", new StringType(UUID.randomUUID().toString()));
return questionnaireResponse;
}
+
+ private void addItem(QuestionnaireResponse questionnaireResponse, String linkId, String text, Type answer)
+ {
+ List answerComponent = Collections
+ .singletonList(new QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().setValue(answer));
+
+ questionnaireResponse.addItem().setLinkId(linkId).setText(text).setAnswer(answerComponent);
+ }
+
+ @Test
+ public void testDeleteAllowedByLocalUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ getWebserviceClient().delete(QuestionnaireResponse.class, created.getIdElement().getIdPart());
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testDeleteNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ try
+ {
+ getExternalWebserviceClient().delete(QuestionnaireResponse.class, created.getIdElement().getIdPart());
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test
+ public void testReadAllowedByLocalUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ QuestionnaireResponse read = getWebserviceClient().read(QuestionnaireResponse.class,
+ created.getIdElement().getIdPart());
+
+ assertNotNull(read);
+ assertNotNull(read.getIdElement().getIdPart());
+ assertEquals(created.getIdElement().getIdPart(), read.getIdElement().getIdPart());
+ assertNotNull(read.getIdElement().getVersionIdPart());
+ assertEquals("1", read.getIdElement().getVersionIdPart());
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testReadNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ try
+ {
+ getExternalWebserviceClient().read(QuestionnaireResponse.class, created.getIdElement().getIdPart());
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testReadNotAllowedByRemoteUserWithVersion() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ try
+ {
+ getExternalWebserviceClient().read(QuestionnaireResponse.class, created.getIdElement().getIdPart(),
+ created.getIdElement().getVersionIdPart());
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test
+ public void testNotModifiedCheckAllowedByLocalUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ QuestionnaireResponse read = getWebserviceClient().read(created);
+ assertNotNull(read);
+ assertTrue(created == read);
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testNotModifiedCheckNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+
+ try
+ {
+ getExternalWebserviceClient().read(created);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test
+ public void testNotModifiedCheckAllowedByLocalUserWithModification() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+ QuestionnaireResponse updated = questionnaireResponseDao.update(created);
+ assertEquals("2", updated.getIdElement().getVersionIdPart());
+
+ QuestionnaireResponse read = getWebserviceClient().read(created);
+ assertNotNull(read);
+ assertTrue(created != read);
+
+ assertEquals("2", read.getIdElement().getVersionIdPart());
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testNotModifiedCheckNotAllowedByRemoteUserWithModification() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+ QuestionnaireResponse updated = questionnaireResponseDao.update(created);
+ assertEquals("2", updated.getIdElement().getVersionIdPart());
+
+ try
+ {
+ getExternalWebserviceClient().read(created);
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
+
+ @Test
+ public void testHistory() throws Exception
+ {
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(createQuestionnaireResponse());
+
+ Bundle historyBundle = getWebserviceClient().history(QuestionnaireResponse.class,
+ created.getIdElement().getIdPart());
+
+ assertNotNull(historyBundle.getEntry());
+ assertEquals(1, historyBundle.getEntry().size());
+ assertNotNull(historyBundle.getEntry().get(0));
+ assertNotNull(historyBundle.getEntry().get(0).getResource());
+ assertTrue(historyBundle.getEntry().get(0).getResource() instanceof QuestionnaireResponse);
+
+ Bundle historyBundle2 = getWebserviceClient().history(QuestionnaireResponse.class);
+
+ assertNotNull(historyBundle2.getEntry());
+ assertEquals(1, historyBundle2.getEntry().size());
+ assertNotNull(historyBundle2.getEntry().get(0));
+ assertNotNull(historyBundle2.getEntry().get(0).getResource());
+ assertTrue(historyBundle2.getEntry().get(0).getResource() instanceof QuestionnaireResponse);
+
+ Bundle historyBundle3 = getWebserviceClient().history(1, Integer.MAX_VALUE);
+
+ assertNotNull(historyBundle3.getEntry());
+
+ List qrFromBundle = historyBundle3.getEntry().stream()
+ .filter(e -> e.hasResource() && e.getResource() instanceof QuestionnaireResponse)
+ .map(e -> (QuestionnaireResponse) e.getResource()).collect(Collectors.toList());
+
+ assertEquals(1, qrFromBundle.size());
+ assertNotNull(qrFromBundle.get(0));
+ }
+
+ @Test
+ public void testHistoryRemoteUser() throws Exception
+ {
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(createQuestionnaireResponse());
+
+ Bundle historyBundle = getExternalWebserviceClient().history(QuestionnaireResponse.class,
+ created.getIdElement().getIdPart());
+
+ assertNotNull(historyBundle.getEntry());
+ assertEquals(0, historyBundle.getEntry().size());
+
+ Bundle historyBundle2 = getExternalWebserviceClient().history(QuestionnaireResponse.class);
+
+ assertNotNull(historyBundle2.getEntry());
+ assertEquals(0, historyBundle2.getEntry().size());
+
+ Bundle historyBundle3 = getExternalWebserviceClient().history(1, Integer.MAX_VALUE);
+
+ assertNotNull(historyBundle3.getEntry());
+ assertNotSame(0, historyBundle3.getEntry().size());
+
+ List qrFromBundle = historyBundle3.getEntry().stream()
+ .filter(e -> e.hasResource() && e.getResource() instanceof QuestionnaireResponse)
+ .map(e -> (QuestionnaireResponse) e.getResource()).collect(Collectors.toList());
+
+ assertEquals(0, qrFromBundle.size());
+ }
+
+ @Test
+ public void testDeletePermanentlyAllowedByLocalUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+ questionnaireResponseDao.delete(UUID.fromString(created.getIdElement().getIdPart()));
+
+ getWebserviceClient().deletePermanently(QuestionnaireResponse.class, created.getIdElement().getIdPart());
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testDeletePermanentlyNotAllowedByRemoteUser() throws Exception
+ {
+ QuestionnaireResponse questionnaireResponse = createQuestionnaireResponse();
+ QuestionnaireResponseDao questionnaireResponseDao = getSpringWebApplicationContext()
+ .getBean(QuestionnaireResponseDao.class);
+ QuestionnaireResponse created = questionnaireResponseDao.create(questionnaireResponse);
+ questionnaireResponseDao.delete(UUID.fromString(created.getIdElement().getIdPart()));
+
+ try
+ {
+ getExternalWebserviceClient().deletePermanently(QuestionnaireResponse.class,
+ created.getIdElement().getIdPart());
+ }
+ catch (WebApplicationException e)
+ {
+ assertEquals(Status.FORBIDDEN.getStatusCode(), e.getResponse().getStatus());
+ throw e;
+ }
+ }
}
diff --git a/dsf-fhir/dsf-fhir-validation/pom.xml b/dsf-fhir/dsf-fhir-validation/pom.xml
index 829dcb62f..19002618d 100644
--- a/dsf-fhir/dsf-fhir-validation/pom.xml
+++ b/dsf-fhir/dsf-fhir-validation/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
@@ -93,7 +93,7 @@
-
diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml
similarity index 93%
rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml
rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml
index 371401aef..9f57e353f 100644
--- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml
+++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml
@@ -6,11 +6,11 @@
-
+
-
+
@@ -43,113 +43,113 @@
-
+
-
+
+ value="(type = 'display') or (type = 'string') or (type = 'text') or (type = 'integer') or (type = 'decimal') or (type = 'boolean') or (type = 'date') or (type = 'time') or (type = 'dateTime') or (type = 'reference') or (type = 'url')"/>
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -170,6 +170,7 @@
+
diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml.post
similarity index 85%
rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml.post
rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml.post
index ae0a8e800..6d52c310c 100644
--- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.8.0.xml.post
+++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-0.9.0.xml.post
@@ -1 +1 @@
-url=http://highmed.org/fhir/StructureDefinition/questionnaire&version=0.8.0
\ No newline at end of file
+url=http://highmed.org/fhir/StructureDefinition/questionnaire&version=0.9.0
\ No newline at end of file
diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml
similarity index 97%
rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml
rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml
index ce16df829..fb2a23c02 100644
--- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml
+++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml
@@ -6,11 +6,11 @@
-
+
-
+
@@ -50,90 +50,90 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -146,12 +146,13 @@
-
+
+
diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml.post b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml.post
similarity index 76%
rename from dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml.post
rename to dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml.post
index 8d2cefaaa..daecb75f0 100644
--- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.8.0.xml.post
+++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/StructureDefinition/highmed-questionnaire-response-0.9.0.xml.post
@@ -1 +1 @@
-url=http://highmed.org/fhir/StructureDefinition/questionnaire-response&version=0.8.0
\ No newline at end of file
+url=http://highmed.org/fhir/StructureDefinition/questionnaire-response&version=0.9.0
\ No newline at end of file
diff --git a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete
index 7bec00659..18613cd5e 100644
--- a/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete
+++ b/dsf-fhir/dsf-fhir-validation/src/main/resources/fhir/resources.delete
@@ -1,4 +1,6 @@
ValueSet?url=http://hl7.org/fhir/ValueSet/mimetypes&version=4.0.1&date=eq2019-11-01T09:29:23
ValueSet?url=http://hl7.org/fhir/ValueSet/mimetypes&version=4.0.1&date=eq2021-02-12
CodeSystem?url=urn:ietf:bcp:13&version=4.0.1&date=eq2020-05-29
-CodeSystem?url=urn:ietf:bcp:13&version=4.0.1&date=eq2021-02-12
\ No newline at end of file
+CodeSystem?url=urn:ietf:bcp:13&version=4.0.1&date=eq2021-02-12
+StructureDefinition?url=http://highmed.org/fhir/StructureDefinition/questionnaire&version=0.8.0&date=eq2022-10-11
+StructureDefinition?url=http://highmed.org/fhir/StructureDefinition/questionnaire-response&version=0.8.0&date=eq2022-10-11
\ No newline at end of file
diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireProfileTest.java
index 92c7bf2b6..555d98b04 100644
--- a/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireProfileTest.java
+++ b/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireProfileTest.java
@@ -25,7 +25,7 @@ public class QuestionnaireProfileTest
@ClassRule
public static final ValidationSupportRule validationRule = new ValidationSupportRule(
- Arrays.asList("highmed-questionnaire-0.8.0.xml"), Collections.emptyList(), Collections.emptyList());
+ Arrays.asList("highmed-questionnaire-0.9.0.xml"), Collections.emptyList(), Collections.emptyList());
private ResourceValidator resourceValidator = new ResourceValidatorImpl(validationRule.getFhirContext(),
validationRule.getValidationSupport());
@@ -90,6 +90,12 @@ public void testQuestionnaireValidTypeReference()
testQuestionnaireValidType(Questionnaire.QuestionnaireItemType.REFERENCE);
}
+ @Test
+ public void testQuestionnaireValidTypeDisplay()
+ {
+ testQuestionnaireValidType(Questionnaire.QuestionnaireItemType.DISPLAY);
+ }
+
private void testQuestionnaireValidType(Questionnaire.QuestionnaireItemType type)
{
Questionnaire res = createQuestionnaire(type);
@@ -108,12 +114,6 @@ public void testQuestionnaireInvalidTypeGroup()
testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.GROUP);
}
- @Test
- public void testQuestionnaireInvalidTypeDisplay()
- {
- testQuestionnaireInvalidType(Questionnaire.QuestionnaireItemType.DISPLAY);
- }
-
@Test
public void testQuestionnaireInvalidTypeQuestion()
{
diff --git a/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java b/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java
index a66b0bbe4..6315db17f 100644
--- a/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java
+++ b/dsf-fhir/dsf-fhir-validation/src/test/java/org/highmed/dsf/fhir/profiles/QuestionnaireResponseProfileTest.java
@@ -36,7 +36,7 @@ public class QuestionnaireResponseProfileTest
@ClassRule
public static final ValidationSupportRule validationRule = new ValidationSupportRule(
- Arrays.asList("highmed-questionnaire-response-0.8.0.xml"), Collections.emptyList(),
+ Arrays.asList("highmed-questionnaire-response-0.9.0.xml"), Collections.emptyList(),
Collections.emptyList());
private ResourceValidator resourceValidator = new ResourceValidatorImpl(validationRule.getFhirContext(),
@@ -96,10 +96,26 @@ public void testQuestionnaireResponseValidTypeReference()
testQuestionnaireResponseValidType(new Reference("Observation/foo"));
}
+ @Test
+ public void testQuestionnaireResponseValidTypeReferenceWithBusinessKey()
+ {
+ testQuestionnaireResponseValidTypeWithBusinessKey(new Reference("Observation/foo"));
+ }
+
+ private void testQuestionnaireResponseValidTypeWithBusinessKey(Type type)
+ {
+ QuestionnaireResponse res = createQuestionnaireResponseWithBusinessKey(type);
+ testQuestionnaireResponse(res);
+ }
+
private void testQuestionnaireResponseValidType(Type type)
{
QuestionnaireResponse res = createQuestionnaireResponse(type);
+ testQuestionnaireResponse(res);
+ }
+ private void testQuestionnaireResponse(QuestionnaireResponse res)
+ {
ValidationResult result = resourceValidator.validate(res);
result.getMessages().stream().map(m -> m.getLocationString() + " " + m.getLocationLine() + ":"
+ m.getLocationCol() + " - " + m.getSeverity() + ": " + m.getMessage()).forEach(logger::info);
@@ -183,17 +199,24 @@ public void testQuestionnaireResponseInvalidCompletedWithAuthorReferenceAndAutho
.filter(m -> m.getMessage().startsWith("author-if-completed")).count());
}
+ private QuestionnaireResponse createQuestionnaireResponseWithBusinessKey(Type type)
+ {
+ QuestionnaireResponse res = createQuestionnaireResponse(type);
+ res.addItem().setLinkId("business-key").setText("The business-key of the process execution").addAnswer()
+ .setValue(new StringType(UUID.randomUUID().toString()));
+
+ return res;
+ }
+
private QuestionnaireResponse createQuestionnaireResponse(Type type)
{
QuestionnaireResponse res = new QuestionnaireResponse();
res.getMeta().addProfile("http://highmed.org/fhir/StructureDefinition/questionnaire-response");
res.setQuestionnaire("http://highmed.org/fhir/Questionnaire/hello-world|0.1.0");
res.setStatus(QuestionnaireResponse.QuestionnaireResponseStatus.INPROGRESS);
- res.addItem().setLinkId("business-key").setText("The business-key of the process execution").addAnswer()
- .setValue(new StringType(UUID.randomUUID().toString()));
res.addItem().setLinkId("user-task-id").setText("The user-task-id of the process execution").addAnswer()
.setValue(new StringType("1"));
- ;
+ res.addItem().setLinkId("valid-display").setText("valid-display");
res.addItem().setLinkId("valid-answer").setText("valid answer").addAnswer().setValue(type);
return res;
diff --git a/dsf-fhir/dsf-fhir-webservice-client/pom.xml b/dsf-fhir/dsf-fhir-webservice-client/pom.xml
index e6304ed77..91805ca49 100755
--- a/dsf-fhir/dsf-fhir-webservice-client/pom.xml
+++ b/dsf-fhir/dsf-fhir-webservice-client/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/dsf-fhir-webservice-client/src/main/java/org/highmed/fhir/client/BasicFhirWebserviceClient.java b/dsf-fhir/dsf-fhir-webservice-client/src/main/java/org/highmed/fhir/client/BasicFhirWebserviceClient.java
index b95450b47..4ccd57091 100644
--- a/dsf-fhir/dsf-fhir-webservice-client/src/main/java/org/highmed/fhir/client/BasicFhirWebserviceClient.java
+++ b/dsf-fhir/dsf-fhir-webservice-client/src/main/java/org/highmed/fhir/client/BasicFhirWebserviceClient.java
@@ -97,12 +97,12 @@ default Bundle history(int page, int count)
default Bundle history(Class extends Resource> resourceType)
{
- return history(null, null);
+ return history(resourceType, null);
}
default Bundle history(Class extends Resource> resourceType, int page, int count)
{
- return history(null, null, page, count);
+ return history(resourceType, null, page, count);
}
default Bundle history(Class extends Resource> resourceType, String id)
diff --git a/dsf-fhir/dsf-fhir-websocket-client/pom.xml b/dsf-fhir/dsf-fhir-websocket-client/pom.xml
index e3bafc629..0e9b598e1 100755
--- a/dsf-fhir/dsf-fhir-websocket-client/pom.xml
+++ b/dsf-fhir/dsf-fhir-websocket-client/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-fhir-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-fhir/pom.xml b/dsf-fhir/pom.xml
index 7d0c7171e..1e061701d 100755
--- a/dsf-fhir/pom.xml
+++ b/dsf-fhir/pom.xml
@@ -8,7 +8,7 @@
org.highmed.dsf
dsf-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-mpi/dsf-mpi-client-pdq/pom.xml b/dsf-mpi/dsf-mpi-client-pdq/pom.xml
index 56005968a..aafc79430 100644
--- a/dsf-mpi/dsf-mpi-client-pdq/pom.xml
+++ b/dsf-mpi/dsf-mpi-client-pdq/pom.xml
@@ -9,7 +9,7 @@
dsf-mpi-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-mpi/dsf-mpi-client-stub/pom.xml b/dsf-mpi/dsf-mpi-client-stub/pom.xml
index 4001fed2d..1e6100592 100644
--- a/dsf-mpi/dsf-mpi-client-stub/pom.xml
+++ b/dsf-mpi/dsf-mpi-client-stub/pom.xml
@@ -9,7 +9,7 @@
dsf-mpi-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-mpi/dsf-mpi-client/pom.xml b/dsf-mpi/dsf-mpi-client/pom.xml
index f2f283a83..9b09c92b1 100644
--- a/dsf-mpi/dsf-mpi-client/pom.xml
+++ b/dsf-mpi/dsf-mpi-client/pom.xml
@@ -9,7 +9,7 @@
dsf-mpi-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-mpi/pom.xml b/dsf-mpi/pom.xml
index 8ca3691f9..a24a23851 100644
--- a/dsf-mpi/pom.xml
+++ b/dsf-mpi/pom.xml
@@ -10,7 +10,7 @@
dsf-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-openehr/dsf-openehr-client-impl/pom.xml b/dsf-openehr/dsf-openehr-client-impl/pom.xml
index 568b21393..2412446ba 100644
--- a/dsf-openehr/dsf-openehr-client-impl/pom.xml
+++ b/dsf-openehr/dsf-openehr-client-impl/pom.xml
@@ -9,7 +9,7 @@
dsf-openehr-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-openehr/dsf-openehr-client-stub/pom.xml b/dsf-openehr/dsf-openehr-client-stub/pom.xml
index a818a4794..71e607aba 100644
--- a/dsf-openehr/dsf-openehr-client-stub/pom.xml
+++ b/dsf-openehr/dsf-openehr-client-stub/pom.xml
@@ -8,7 +8,7 @@
dsf-openehr-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-openehr/dsf-openehr-client/pom.xml b/dsf-openehr/dsf-openehr-client/pom.xml
index ec3f0f171..8d357c1e5 100644
--- a/dsf-openehr/dsf-openehr-client/pom.xml
+++ b/dsf-openehr/dsf-openehr-client/pom.xml
@@ -8,7 +8,7 @@
dsf-openehr-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-openehr/dsf-openehr-model/pom.xml b/dsf-openehr/dsf-openehr-model/pom.xml
index dd70be2bc..31ebfee04 100644
--- a/dsf-openehr/dsf-openehr-model/pom.xml
+++ b/dsf-openehr/dsf-openehr-model/pom.xml
@@ -8,7 +8,7 @@
dsf-openehr-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-openehr/pom.xml b/dsf-openehr/pom.xml
index fcc551984..2eec4d13d 100755
--- a/dsf-openehr/pom.xml
+++ b/dsf-openehr/pom.xml
@@ -10,7 +10,7 @@
dsf-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/dsf-pseudonymization-base/pom.xml b/dsf-pseudonymization/dsf-pseudonymization-base/pom.xml
index c442a48da..362b5d0a4 100644
--- a/dsf-pseudonymization/dsf-pseudonymization-base/pom.xml
+++ b/dsf-pseudonymization/dsf-pseudonymization-base/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-pseudonymization-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/dsf-pseudonymization-client-stub/pom.xml b/dsf-pseudonymization/dsf-pseudonymization-client-stub/pom.xml
index 1534d6423..0310e04e9 100644
--- a/dsf-pseudonymization/dsf-pseudonymization-client-stub/pom.xml
+++ b/dsf-pseudonymization/dsf-pseudonymization-client-stub/pom.xml
@@ -9,7 +9,7 @@
dsf-pseudonymization-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/dsf-pseudonymization-client/pom.xml b/dsf-pseudonymization/dsf-pseudonymization-client/pom.xml
index 12e3094ba..06e58cbbf 100644
--- a/dsf-pseudonymization/dsf-pseudonymization-client/pom.xml
+++ b/dsf-pseudonymization/dsf-pseudonymization-client/pom.xml
@@ -9,7 +9,7 @@
dsf-pseudonymization-pom
org.highmed.dsf
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/dsf-pseudonymization-medic/pom.xml b/dsf-pseudonymization/dsf-pseudonymization-medic/pom.xml
index 91654d4c5..9b5c1e041 100644
--- a/dsf-pseudonymization/dsf-pseudonymization-medic/pom.xml
+++ b/dsf-pseudonymization/dsf-pseudonymization-medic/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-pseudonymization-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/dsf-pseudonymization-ttp/pom.xml b/dsf-pseudonymization/dsf-pseudonymization-ttp/pom.xml
index 8c6f8000c..01a0141e3 100644
--- a/dsf-pseudonymization/dsf-pseudonymization-ttp/pom.xml
+++ b/dsf-pseudonymization/dsf-pseudonymization-ttp/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-pseudonymization-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-pseudonymization/pom.xml b/dsf-pseudonymization/pom.xml
index c416cdc81..46ecd0075 100644
--- a/dsf-pseudonymization/pom.xml
+++ b/dsf-pseudonymization/pom.xml
@@ -8,7 +8,7 @@
org.highmed.dsf
dsf-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-build-info-reader/pom.xml b/dsf-tools/dsf-tools-build-info-reader/pom.xml
index 5bf0c9ce4..3e0a75b84 100644
--- a/dsf-tools/dsf-tools-build-info-reader/pom.xml
+++ b/dsf-tools/dsf-tools-build-info-reader/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-bundle-generator/pom.xml b/dsf-tools/dsf-tools-bundle-generator/pom.xml
index 841048370..bde2f09ce 100755
--- a/dsf-tools/dsf-tools-bundle-generator/pom.xml
+++ b/dsf-tools/dsf-tools-bundle-generator/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-db-migration/pom.xml b/dsf-tools/dsf-tools-db-migration/pom.xml
index 75776c53e..f1a208e7c 100755
--- a/dsf-tools/dsf-tools-db-migration/pom.xml
+++ b/dsf-tools/dsf-tools-db-migration/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-docker-secrets-reader/pom.xml b/dsf-tools/dsf-tools-docker-secrets-reader/pom.xml
index 6a2365eee..a12295586 100644
--- a/dsf-tools/dsf-tools-docker-secrets-reader/pom.xml
+++ b/dsf-tools/dsf-tools-docker-secrets-reader/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-documentation-generator/pom.xml b/dsf-tools/dsf-tools-documentation-generator/pom.xml
index 475c590d2..a950151f8 100644
--- a/dsf-tools/dsf-tools-documentation-generator/pom.xml
+++ b/dsf-tools/dsf-tools-documentation-generator/pom.xml
@@ -9,7 +9,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-proxy-test/pom.xml b/dsf-tools/dsf-tools-proxy-test/pom.xml
index 7a0988305..4bdfbdfd7 100755
--- a/dsf-tools/dsf-tools-proxy-test/pom.xml
+++ b/dsf-tools/dsf-tools-proxy-test/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/dsf-tools-test-data-generator/pom.xml b/dsf-tools/dsf-tools-test-data-generator/pom.xml
index c9390cb00..851ea7851 100755
--- a/dsf-tools/dsf-tools-test-data-generator/pom.xml
+++ b/dsf-tools/dsf-tools-test-data-generator/pom.xml
@@ -7,7 +7,7 @@
org.highmed.dsf
dsf-tools-pom
- 0.8.0
+ 0.9.0
diff --git a/dsf-tools/pom.xml b/dsf-tools/pom.xml
index cb029123f..d8ed10d06 100755
--- a/dsf-tools/pom.xml
+++ b/dsf-tools/pom.xml
@@ -8,7 +8,7 @@
org.highmed.dsf
dsf-pom
- 0.8.0
+ 0.9.0
diff --git a/pom.xml b/pom.xml
index 3527b7003..3d6d89de7 100755
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.highmed.dsf
dsf-pom
- 0.8.0
+ 0.9.0
pom
@@ -133,6 +133,12 @@
liquibase-core
4.16.1
+
+
+ org.yaml
+ snakeyaml
+ 1.32
+
org.postgresql
postgresql
@@ -211,7 +217,7 @@
com.fasterxml.jackson.core
jackson-databind
- 2.13.4
+ 2.14.0-rc1
com.fasterxml.jackson.core
@@ -331,12 +337,17 @@
${hapi.fhir.version}
-
+
org.apache.commons
commons-compress
1.21
+
+ org.apache.commons
+ commons-text
+ 1.10.0
+
org.apache.httpcomponents
httpclient