Skip to content

Commit

Permalink
Get user tasks forms as metadata (#314)
Browse files Browse the repository at this point in the history
* feat: expose user task form as metadata

* feat: filter process elements by type
  • Loading branch information
saig0 authored Dec 14, 2022
1 parent 5a911a1 commit 378fd75
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 77 deletions.
3 changes: 1 addition & 2 deletions data/src/main/kotlin/io/zeebe/zeeqs/data/entity/UserTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ data class UserTask(
val elementInstanceKey: Long,
val assignee: String?,
val candidateGroups: String?,
val formKey: String?,
val isCamundaForm: Boolean
val formKey: String?
) {
@Enumerated(EnumType.STRING)
var state: UserTaskState = UserTaskState.CREATED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ data class BpmnElementMetadata(
val errorCode: String? = null,
val calledProcessId: String? = null,
val messageSubscriptionDefinition: MessageSubscriptionDefinition? = null,
val userTaskAssignmentDefinition: UserTaskAssignmentDefinition? = null
val userTaskAssignmentDefinition: UserTaskAssignmentDefinition? = null,
val userTaskForm: UserTaskForm? = null
)
25 changes: 23 additions & 2 deletions data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import io.camunda.zeebe.model.bpmn.instance.*
import io.camunda.zeebe.model.bpmn.instance.zeebe.*
import io.zeebe.zeeqs.data.entity.BpmnElementType
import io.zeebe.zeeqs.data.repository.ProcessRepository
import org.camunda.bpm.model.xml.ModelInstance
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component

private const val CAMUNDA_FORM_KEY_PREFIX = "camunda-forms:bpmn:"

@Component
class ProcessService(val processRepository: ProcessRepository) {

Expand Down Expand Up @@ -126,15 +129,33 @@ class ProcessService(val processRepository: ProcessRepository) {
assignee = it.assignee,
candidateGroups = it.candidateGroups
)
},
userTaskForm = element
.getSingleExtensionElement(ZeebeFormDefinition::class.java)
?.formKey
?.let { formKey ->
UserTaskForm(
key = formKey,
resource = getForm(
model = element.modelInstance,
formKey = formKey
)
)
}
)
}

@Cacheable(cacheNames = ["userTaskForm"])
fun getForm(processDefinitionKey: Long, formKey: String): String? {
return getBpmnModel(processDefinitionKey)
?.getModelElementsByType(ZeebeUserTaskForm::class.java)
?.firstOrNull { it.id == formKey }
?.let { getForm(model = it, formKey = formKey) }
}

private fun getForm(model: ModelInstance, formKey: String): String? {
val normalizedFormKey = formKey.replace(CAMUNDA_FORM_KEY_PREFIX, "")

return model.getModelElementsByType(ZeebeUserTaskForm::class.java)
?.firstOrNull { it.id == normalizedFormKey }
?.textContent
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.zeebe.zeeqs.graphql.resolvers.type
package io.zeebe.zeeqs.data.service

data class UserTaskForm(
val key: String,
val resource: String?
)
)
110 changes: 69 additions & 41 deletions data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package io.zeebe.zeeqs

import io.camunda.zeebe.model.bpmn.Bpmn
import io.camunda.zeebe.model.bpmn.BpmnModelInstance
import io.zeebe.zeeqs.data.entity.BpmnElementType
import io.zeebe.zeeqs.data.entity.Process
import io.zeebe.zeeqs.data.repository.ProcessRepository
import io.zeebe.zeeqs.data.service.BpmnElementInfo
import io.zeebe.zeeqs.data.service.BpmnElementMetadata
import io.zeebe.zeeqs.data.service.ProcessService
import io.zeebe.zeeqs.data.service.UserTaskAssignmentDefinition
import io.zeebe.zeeqs.data.service.*
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.entry
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -36,25 +34,16 @@ class ProcessServiceTest(
// given
val processDefinitionKey = 1L

val bpmn = Bpmn.createExecutableProcess("process")
.startEvent("s").name("start")
.serviceTask("t").name("task")
.zeebeJobType("test")
.userTask("u").name("userTask")
.zeebeAssignee("user1").zeebeCandidateGroups("group1")
.endEvent("e").name("")
.done()

processRepository.save(
Process(
key = processDefinitionKey,
bpmnProcessId = "process",
version = 1,
bpmnXML = Bpmn.convertToString(bpmn),
deployTime = Instant.now().toEpochMilli(),
resourceName = "process.bpmn",
checksum = "checksum"
)
createProcess(
processDefinitionKey = processDefinitionKey,
bpmn = Bpmn.createExecutableProcess("process")
.startEvent("s").name("start")
.serviceTask("t").name("task")
.zeebeJobType("test")
.userTask("u").name("userTask")
.zeebeAssignee("user1").zeebeCandidateGroups("group1")
.endEvent("e").name("")
.done()
)

// when
Expand All @@ -71,6 +60,41 @@ class ProcessServiceTest(
.contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata())))
}

@Test
fun `should return user task form`() {
// given
val processDefinitionKey = 1L

createProcess(
processDefinitionKey = processDefinitionKey,
bpmn = Bpmn.createExecutableProcess("process")
.startEvent()
.userTask("user_task_A").name("A")
.zeebeUserTaskForm("form_A", """{"x":1}""")
.done()
)

// when
val info = processService.getBpmnElementInfo(processDefinitionKey)!!

// then
assertThat(info["user_task_A"])
.isNotNull()
.isEqualTo(
BpmnElementInfo(
elementId = "user_task_A",
elementName = "A",
elementType = BpmnElementType.USER_TASK,
metadata = BpmnElementMetadata(
userTaskForm = UserTaskForm(
key = "camunda-forms:bpmn:form_A",
resource = """{"x":1}"""
)
)
)
)
}

@Test
fun `should return nothing if process does not exist`() {
// given
Expand All @@ -94,24 +118,14 @@ class ProcessServiceTest(

@BeforeEach
fun `store process`() {
val bpmn = Bpmn.createExecutableProcess("process")
.startEvent()
.userTask("A")
.zeebeUserTaskForm(formKey, userForm)
.endEvent()
.done()

processRepository.save(
Process(
key = processDefinitionKey,
bpmnProcessId = "process",
version = 1,
bpmnXML = Bpmn.convertToString(bpmn),
deployTime = Instant.now().toEpochMilli(),
resourceName = "process.bpmn",
checksum = "checksum"
)
)
createProcess(
processDefinitionKey = 1L,
bpmn = Bpmn.createExecutableProcess("process")
.startEvent()
.userTask("A")
.zeebeUserTaskForm(formKey, userForm)
.endEvent()
.done())
}

@Test
Expand Down Expand Up @@ -150,4 +164,18 @@ class ProcessServiceTest(
}
}

private fun createProcess(processDefinitionKey: Long, bpmn: BpmnModelInstance?) {
processRepository.save(
Process(
key = processDefinitionKey,
bpmnProcessId = "process",
version = 1,
bpmnXML = Bpmn.convertToString(bpmn),
deployTime = Instant.now().toEpochMilli(),
resourceName = "process.bpmn",
checksum = "checksum"
)
)
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package io.zeebe.zeeqs.graphql.resolvers.type

import graphql.kickstart.tools.GraphQLResolver
import io.zeebe.zeeqs.data.entity.MessageSubscription
import io.zeebe.zeeqs.data.entity.Timer
import io.zeebe.zeeqs.data.entity.Process
import io.zeebe.zeeqs.data.entity.ProcessInstanceState
import io.zeebe.zeeqs.data.entity.*
import io.zeebe.zeeqs.data.repository.MessageSubscriptionRepository
import io.zeebe.zeeqs.data.repository.TimerRepository
import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository
Expand Down Expand Up @@ -41,10 +38,11 @@ class ProcessResolver(
return messageSubscriptionRepository.findByProcessDefinitionKeyAndElementInstanceKeyIsNull(process.key)
}

fun elements(process: Process): List<BpmnElement> {
fun elements(process: Process, elementTypeIn: List<BpmnElementType>): List<BpmnElement> {
return processService
.getBpmnElementInfo(process.key)
?.values
?.filter { elementTypeIn.isEmpty() || elementTypeIn.contains(it.elementType) }
?.map { asBpmnElement(process, it) }
?: emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import graphql.kickstart.tools.GraphQLResolver
import io.zeebe.zeeqs.data.entity.ElementInstance
import io.zeebe.zeeqs.data.entity.ProcessInstance
import io.zeebe.zeeqs.data.entity.UserTask
import io.zeebe.zeeqs.data.service.UserTaskForm
import io.zeebe.zeeqs.data.repository.ElementInstanceRepository
import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository
import io.zeebe.zeeqs.data.repository.UserTaskRepository
import io.zeebe.zeeqs.data.service.ProcessService
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
Expand Down Expand Up @@ -42,14 +42,10 @@ class UserTaskResolver(
return userTask.formKey?.let { formKey ->
UserTaskForm(
key = formKey,
resource = formKey
.takeIf { userTask.isCamundaForm }
?.let {
processService.getForm(
processDefinitionKey = userTask.processDefinitionKey,
formKey = formKey
)
}
resource = processService.getForm(
processDefinitionKey = userTask.processDefinitionKey,
formKey = formKey
)
)
}
}
Expand Down
4 changes: 3 additions & 1 deletion graphql-api/src/main/resources/graphql/Element.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ type BpmnElementMetadata {
calledProcessId: String
# the definition of the message subscription if the element is a message catch event
messageSubscriptionDefinition: MessageSubscriptionDefinition
# the assignment definition if the element is an user task
# the assignment definition if the element is a user task
userTaskAssignmentDefinition: UserTaskAssignmentDefinition
# the user form if the element is a user task
userTaskForm: UserTaskForm
}

# The definition of a message subscription from a BPMN element.
Expand Down
5 changes: 4 additions & 1 deletion graphql-api/src/main/resources/graphql/Process.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ type Process {
# the opened message subscriptions of the message start events of the process
messageSubscriptions: [MessageSubscription!]
# all BPMN elements of the process
elements: [BpmnElement!]
elements(
# Filter the BPMN elements by the given types. If empty, return all elements.
elementTypeIn: [BpmnElementType!] = []
): [BpmnElement!]
# BPMN element of the process by its id
element(elementId: String): BpmnElement
}
Expand Down
Loading

0 comments on commit 378fd75

Please sign in to comment.