From 38d3ac6da79a4bcf7a5381d35e32db2867ce5bc5 Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Mon, 25 Mar 2024 14:59:11 +0300 Subject: [PATCH 1/6] feat: get process instances by variable name and value --- .../repository/ProcessInstanceRepository.kt | 9 +++ .../data/repository/VariableRepository.kt | 4 + .../data/service/ProcessInstanceService.kt | 79 +++++++++++++++++++ .../query/ProcessInstanceQueryResolver.kt | 12 ++- .../graphql/resolvers/type/ProcessResolver.kt | 25 ++++-- .../main/resources/graphql/Process.graphqls | 4 +- .../graphql/ProcessInstance.graphqls | 4 +- 7 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/ProcessInstanceRepository.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/ProcessInstanceRepository.kt index f9bcff71..95959bbc 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/ProcessInstanceRepository.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/ProcessInstanceRepository.kt @@ -6,17 +6,26 @@ import org.springframework.data.domain.Pageable import org.springframework.data.repository.PagingAndSortingRepository import org.springframework.stereotype.Repository +interface ProcessInstanceKeyOnly { + fun getKey(): Long +} + @Repository interface ProcessInstanceRepository : PagingAndSortingRepository { fun findByParentProcessInstanceKey(parentProcessInstanceKey: Long): List + fun findByProcessDefinitionKeyAndStateIn(processDefinitionKey: Long, stateIn: List): List + fun findByProcessDefinitionKeyAndStateIn(processDefinitionKey: Long, stateIn: List, pageable: Pageable): List fun countByProcessDefinitionKeyAndStateIn(processDefinitionKey: Long, stateIn: List): Long fun findByStateIn(stateIn: List, pageable: Pageable): List + fun findByStateIn(stateIn: List): List + fun countByStateIn(stateIn: List): Long + fun findByStateInAndKeyIn(stateIn: List, key: List, pageable: Pageable): List } \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt index 5f9a315e..82c87003 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt @@ -13,4 +13,8 @@ interface VariableRepository : PagingAndSortingRepository { @Transactional(readOnly = true) fun findByScopeKey(scopeKey: Long): List + + @Transactional(readOnly = true) + fun findByProcessInstanceKeyInAndName(processInstanceKey: List, name: String): List + } \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt new file mode 100644 index 00000000..24c871ab --- /dev/null +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt @@ -0,0 +1,79 @@ +package io.zeebe.zeeqs.data.service + +import io.zeebe.zeeqs.data.entity.ProcessInstance +import io.zeebe.zeeqs.data.entity.ProcessInstanceState +import io.zeebe.zeeqs.data.entity.Variable +import io.zeebe.zeeqs.data.repository.ProcessInstanceKeyOnly +import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository +import io.zeebe.zeeqs.data.repository.VariableRepository +import org.springframework.data.domain.PageRequest +import org.springframework.stereotype.Component + +@Component +class ProcessInstanceService( + private val processInstancesRepository: ProcessInstanceRepository, + private val variableRepository: VariableRepository) { + + private fun getVariables(stateIn: List, variableName: String, variableValue: String): List { + val processInstances = processInstancesRepository.findByStateIn(stateIn).toList(); + return getVariablesByProcessInstanceKeys(processInstances, variableName, variableValue); + } + + private fun getVariables(stateIn: List, processDefinitionKey: Long, variableName: String, variableValue: String): List { + val processInstances = processInstancesRepository.findByProcessDefinitionKeyAndStateIn(processDefinitionKey, stateIn).toList(); + return getVariablesByProcessInstanceKeys(processInstances, variableName, variableValue); + + } + + private fun getVariablesByProcessInstanceKeys(processInstances: List, variableName: String, variableValue: String): List { + val variables = variableRepository.findByProcessInstanceKeyInAndName(processInstances.map { it.getKey() }, variableName); + val filteredVariables = variables.filter { it.value == variableValue }; + return filteredVariables; + } + + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, variableName: String?, variableValue: String?): List { + if(variableName != null && variableValue != null) { + val filteredVariables = getVariables(stateIn, variableName, variableValue); + val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); + return filteredProcessInstances; + } + else { + return processInstancesRepository.findByStateIn(stateIn, PageRequest.of(page, perPage)).toList(); + } + } + + fun countProcessInstances(stateIn: List, variableName: String?, variableValue: String?): Long { + if(variableName != null && variableValue != null) { + val filteredVariables = getVariables(stateIn, variableName, variableValue); + return filteredVariables.count().toLong(); + } + + else { + return processInstancesRepository.countByStateIn(stateIn); + } + } + + + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, processDefinitionKey: Long, variableName: String?, variableValue: String?): List { + if(variableName != null && variableValue != null) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variableName, variableValue); + val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); + return filteredProcessInstances; + } + else { + return processInstancesRepository.findByStateIn(stateIn, PageRequest.of(page, perPage)).toList(); + } + } + + fun countProcessInstances(stateIn: List, processDefinitionKey: Long, variableName: String?, variableValue: String?): Long { + if(variableName != null && variableValue != null) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variableName, variableValue); + return filteredVariables.count().toLong(); + } + + else { + return processInstancesRepository.countByStateIn(stateIn); + } + } + +} \ No newline at end of file diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt index 7943c905..7310f050 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt @@ -3,6 +3,7 @@ package io.zeebe.zeeqs.graphql.resolvers.query import io.zeebe.zeeqs.data.entity.ProcessInstance import io.zeebe.zeeqs.data.entity.ProcessInstanceState import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository +import io.zeebe.zeeqs.data.service.ProcessInstanceService import io.zeebe.zeeqs.graphql.resolvers.connection.ProcessInstanceConnection import org.springframework.data.domain.PageRequest import org.springframework.data.repository.findByIdOrNull @@ -12,18 +13,21 @@ import org.springframework.stereotype.Controller @Controller class ProcessInstanceQueryResolver( - val processInstanceRepository: ProcessInstanceRepository + val processInstanceRepository: ProcessInstanceRepository, + val processInstanceService: ProcessInstanceService ) { @QueryMapping fun processInstances( @Argument perPage: Int, @Argument page: Int, - @Argument stateIn: List + @Argument stateIn: List, + @Argument variableName: String?, + @Argument variableValue: String? ): ProcessInstanceConnection { return ProcessInstanceConnection( - getItems = { processInstanceRepository.findByStateIn(stateIn, PageRequest.of(page, perPage)).toList() }, - getCount = { processInstanceRepository.countByStateIn(stateIn) } + getItems = { processInstanceService.getProcessInstances(perPage, page, stateIn, variableName, variableValue) }, + getCount = { processInstanceService.countProcessInstances(stateIn, variableName, variableValue) } ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt index 2a10a279..98172e21 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt @@ -6,6 +6,7 @@ import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository import io.zeebe.zeeqs.data.repository.SignalSubscriptionRepository import io.zeebe.zeeqs.data.repository.TimerRepository import io.zeebe.zeeqs.data.service.BpmnElementInfo +import io.zeebe.zeeqs.data.service.ProcessInstanceService import io.zeebe.zeeqs.data.service.ProcessService import io.zeebe.zeeqs.graphql.resolvers.connection.ProcessInstanceConnection import org.springframework.data.domain.PageRequest @@ -19,6 +20,7 @@ class ProcessResolver( val timerRepository: TimerRepository, val messageSubscriptionRepository: MessageSubscriptionRepository, val processService: ProcessService, + val processInstanceService: ProcessInstanceService, private val signalSubscriptionRepository: SignalSubscriptionRepository ) { @@ -27,20 +29,27 @@ class ProcessResolver( process: Process, @Argument perPage: Int, @Argument page: Int, - @Argument stateIn: List + @Argument stateIn: List, + @Argument variableName: String?, + @Argument variableValue: String? ): ProcessInstanceConnection { return ProcessInstanceConnection( getItems = { - processInstanceRepository.findByProcessDefinitionKeyAndStateIn( - process.key, - stateIn, - PageRequest.of(page, perPage) + processInstanceService.getProcessInstances( + perPage, + page, + stateIn, + process.key, + variableName, + variableValue ).toList() }, getCount = { - processInstanceRepository.countByProcessDefinitionKeyAndStateIn( - process.key, - stateIn + processInstanceService.countProcessInstances( + stateIn, + process.key, + variableName, + variableValue ) } ) diff --git a/graphql-api/src/main/resources/graphql/Process.graphqls b/graphql-api/src/main/resources/graphql/Process.graphqls index 24dd7c93..d182d2bd 100644 --- a/graphql-api/src/main/resources/graphql/Process.graphqls +++ b/graphql-api/src/main/resources/graphql/Process.graphqls @@ -13,7 +13,9 @@ type Process { processInstances( perPage: Int = 10, page: Int = 0, - stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED]): ProcessInstanceConnection! + stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED] + variableName: String = null, + variableValue: String = null): ProcessInstanceConnection! # the scheduled timers of the timer start events of the process timers: [Timer!] # the opened message subscriptions of the message start events of the process diff --git a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls index acafefd8..ff7afe3e 100644 --- a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls +++ b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls @@ -73,7 +73,9 @@ type Query { processInstances( perPage: Int = 10, page: Int = 0, - stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED] + stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED], + variableName: String = null, + variableValue: String = null ): ProcessInstanceConnection! } From 39104fc093d836f028b2bf15bf1c9d0056416f51 Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Sun, 31 Mar 2024 11:14:55 +0300 Subject: [PATCH 2/6] fix: bug fix for not filtering by instance key --- .../io/zeebe/zeeqs/data/service/ProcessInstanceService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt index 24c871ab..dc860be7 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt @@ -61,7 +61,7 @@ class ProcessInstanceService( return filteredProcessInstances; } else { - return processInstancesRepository.findByStateIn(stateIn, PageRequest.of(page, perPage)).toList(); + return processInstancesRepository.findByProcessDefinitionKeyAndStateIn(processDefinitionKey, stateIn, PageRequest.of(page, perPage)).toList(); } } @@ -72,7 +72,7 @@ class ProcessInstanceService( } else { - return processInstancesRepository.countByStateIn(stateIn); + return processInstancesRepository.countByProcessDefinitionKeyAndStateIn(processDefinitionKey, stateIn); } } From 1814d0081cd28b39c16011fbeb40ee4d3a6b6ba4 Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Tue, 30 Apr 2024 11:56:45 +0300 Subject: [PATCH 3/6] feat: return group elements, and return extension properties with documentation for elements --- .../zeeqs/data/entity/BpmnElementType.kt | 3 +- .../service/BpmnElementExtensionProperties.kt | 6 +++ .../zeeqs/data/service/BpmnElementInfo.kt | 6 ++- .../zeeqs/data/service/ProcessService.kt | 54 +++++++++++++------ .../io/zeebe/zeeqs/ProcessServiceTest.kt | 11 ++-- .../resolvers/type/BpmnElementResolver.kt | 16 ++++++ .../main/resources/graphql/Element.graphqls | 14 +++++ 7 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/BpmnElementType.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/BpmnElementType.kt index 570be2e6..08ba6586 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/BpmnElementType.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/BpmnElementType.kt @@ -25,5 +25,6 @@ enum class BpmnElementType { BUSINESS_RULE_TASK, SCRIPT_TASK, SEND_TASK, - INCLUSIVE_GATEWAY + INCLUSIVE_GATEWAY, + GROUP } diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt new file mode 100644 index 00000000..5bc1f02e --- /dev/null +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt @@ -0,0 +1,6 @@ +package io.zeebe.zeeqs.data.service + +data class BpmnElementExtensionProperties ( + val name: String? = null, + val value: String? = null, +) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt index f3d786ea..74520c5a 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt @@ -1,10 +1,14 @@ package io.zeebe.zeeqs.data.service +import io.camunda.zeebe.model.bpmn.instance.Documentation +import io.camunda.zeebe.model.bpmn.instance.zeebe.ZeebeProperty import io.zeebe.zeeqs.data.entity.BpmnElementType data class BpmnElementInfo( val elementId: String, val elementName: String?, val elementType: BpmnElementType, - val metadata: BpmnElementMetadata + val metadata: BpmnElementMetadata, + val extensionProperties: Collection?, + val documentation: String? ) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt index 794a692f..fa698a17 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt @@ -20,18 +20,28 @@ class ProcessService(val processRepository: ProcessRepository) { @Cacheable(cacheNames = ["bpmnElementInfo"]) fun getBpmnElementInfo(processDefinitionKey: Long): Map? { return getBpmnModel(processDefinitionKey) - ?.let { it.getModelElementsByType(FlowElement::class.java) } - ?.map { flowElement -> - Pair( - flowElement.id, BpmnElementInfo( - elementId = flowElement.id, - elementName = flowElement.name, - elementType = getBpmnElementType(flowElement), - metadata = getMetadata(flowElement) - ) - ) - } - ?.toMap() + ?.let { model -> + (model.getModelElementsByType(FlowElement::class.java)?.associate { flowElement -> + flowElement.id to BpmnElementInfo( + elementId = flowElement.id, + elementName = flowElement.name, + elementType = getBpmnElementType(flowElement), + metadata = getMetadata(flowElement), + extensionProperties = getExtensionProperties(flowElement), + documentation = getDocumentation(flowElement) + ) + }.orEmpty() + + model.getModelElementsByType(Group::class.java)?.associate { groupElement -> + groupElement.id to BpmnElementInfo( + elementId = groupElement.id, + elementName = groupElement.category?.value ?: "", + elementType = getBpmnElementType(groupElement), + metadata = getMetadata(groupElement), + extensionProperties = getExtensionProperties(groupElement), + documentation = getDocumentation(groupElement) + ) + }.orEmpty()) + } } private fun getBpmnModel(processDefinitionKey: Long): BpmnModelInstance? { @@ -41,7 +51,7 @@ class ProcessService(val processRepository: ProcessRepository) { ?.let { Bpmn.readModelFromStream(it) } } - private fun getBpmnElementType(element: FlowElement): BpmnElementType { + private fun getBpmnElementType(element: BaseElement): BpmnElementType { return when (element.elementType.typeName) { BpmnModelConstants.BPMN_ELEMENT_PROCESS -> BpmnElementType.PROCESS BpmnModelConstants.BPMN_ELEMENT_SUB_PROCESS -> getBpmnSubprocessType(element) @@ -63,11 +73,12 @@ class ProcessService(val processRepository: ProcessRepository) { BpmnModelConstants.BPMN_ELEMENT_BUSINESS_RULE_TASK -> BpmnElementType.BUSINESS_RULE_TASK BpmnModelConstants.BPMN_ELEMENT_SCRIPT_TASK -> BpmnElementType.SCRIPT_TASK BpmnModelConstants.BPMN_ELEMENT_INCLUSIVE_GATEWAY -> BpmnElementType.INCLUSIVE_GATEWAY + BpmnModelConstants.BPMN_ELEMENT_GROUP -> BpmnElementType.GROUP else -> BpmnElementType.UNKNOWN } } - private fun getBpmnSubprocessType(element: FlowElement) = + private fun getBpmnSubprocessType(element: BaseElement) = if (element is SubProcess) { if (element.triggeredByEvent()) { BpmnElementType.EVENT_SUB_PROCESS @@ -78,7 +89,7 @@ class ProcessService(val processRepository: ProcessRepository) { BpmnElementType.UNKNOWN } - private fun getMetadata(element: FlowElement): BpmnElementMetadata { + private fun getMetadata(element: BaseElement): BpmnElementMetadata { return BpmnElementMetadata( jobType = element .getSingleExtensionElement(ZeebeTaskDefinition::class.java) @@ -156,6 +167,19 @@ class ProcessService(val processRepository: ProcessRepository) { ) } + private fun getExtensionProperties(element: BaseElement): Collection? { + return element.extensionElements?.elementsQuery + ?.filterByType(ZeebeProperties::class.java) + ?.singleResult() + ?.properties + ?.map { BpmnElementExtensionProperties(name = it.name, value = it.value) } + } + + + private fun getDocumentation(element: BaseElement): String { + return element.documentations.joinToString(separator = "") { it.textContent } + } + @Cacheable(cacheNames = ["userTaskForm"]) fun getForm(processDefinitionKey: Long, formKey: String): String? { return getBpmnModel(processDefinitionKey) diff --git a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt index 337243b1..8b5445cf 100644 --- a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt +++ b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt @@ -52,12 +52,12 @@ class ProcessServiceTest( // then assertThat(info) .isNotNull() - .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata()))) - .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test")))) + .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata(), listOf(BpmnElementExtensionProperties()), ""))) + .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test"), listOf(BpmnElementExtensionProperties()), ""))) .contains(entry("u", BpmnElementInfo("u", "userTask", BpmnElementType.USER_TASK, BpmnElementMetadata( - userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")))) + userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")), listOf(BpmnElementExtensionProperties()), "")) ) - .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata()))) + .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata(), listOf(BpmnElementExtensionProperties()), ""))) } @Test @@ -90,7 +90,8 @@ class ProcessServiceTest( key = "camunda-forms:bpmn:form_A", resource = """{"x":1}""" ) - ) + ), + listOf(BpmnElementExtensionProperties()), "" ) ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt index b867d74b..1704a716 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt @@ -5,6 +5,7 @@ import io.zeebe.zeeqs.data.entity.ElementInstanceState import io.zeebe.zeeqs.data.entity.Process import io.zeebe.zeeqs.data.repository.ElementInstanceRepository import io.zeebe.zeeqs.data.repository.ProcessRepository +import io.zeebe.zeeqs.data.service.BpmnElementExtensionProperties import io.zeebe.zeeqs.data.service.BpmnElementInfo import io.zeebe.zeeqs.data.service.BpmnElementMetadata import io.zeebe.zeeqs.data.service.ProcessService @@ -41,6 +42,21 @@ class BpmnElementResolver( ?: BpmnElementMetadata() } + + @SchemaMapping(typeName = "BpmnElement", field = "extensionProperties") + fun extensionProperties(element: BpmnElement): Collection? { + return findElementInfo(element) + ?.extensionProperties + } + + + @SchemaMapping(typeName = "BpmnElement", field = "documentation") + fun documentation(element: BpmnElement): String? { + return findElementInfo(element) + ?.documentation + + } + @SchemaMapping(typeName = "BpmnElement", field = "process") fun process(element: BpmnElement): Process? { return processRepository.findByIdOrNull(element.processDefinitionKey) diff --git a/graphql-api/src/main/resources/graphql/Element.graphqls b/graphql-api/src/main/resources/graphql/Element.graphqls index 4e84b9e2..3209d162 100644 --- a/graphql-api/src/main/resources/graphql/Element.graphqls +++ b/graphql-api/src/main/resources/graphql/Element.graphqls @@ -10,6 +10,12 @@ type BpmnElement { # the metadata of the BPMN element metadata: BpmnElementMetadata! + # extension properties of the BPMN element + extensionProperties: [BpmnElementExtensionProperties] + + # documentation of the BPMN element + documentation: String + # the process that contains the BPMN element process: Process # the instances of the BPMN element @@ -46,6 +52,7 @@ enum BpmnElementType { SCRIPT_TASK SEND_TASK INCLUSIVE_GATEWAY + GROUP } # Additional metadata that are defined statically on the BPMN element. @@ -84,4 +91,11 @@ type UserTaskAssignmentDefinition { assignee: String # the candidate groups candidateGroups: String +} + +type BpmnElementExtensionProperties { + # the name of property + name: String + # the value of property + value: String } \ No newline at end of file From b37ae7919ddcf8dd71da2fdd22c601b140186e11 Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Sun, 5 May 2024 15:26:38 +0300 Subject: [PATCH 4/6] feat: filter by multiple variables --- .../zeebe/zeeqs/data/entity/VariableFilter.kt | 12 +++++ .../data/repository/VariableRepository.kt | 4 ++ .../data/service/ProcessInstanceService.kt | 51 +++++++++++-------- .../zeeqs/data/service/ProcessService.kt | 12 +++-- .../io/zeebe/zeeqs/ProcessServiceTest.kt | 10 ++-- .../query/ProcessInstanceQueryResolver.kt | 8 +-- .../graphql/resolvers/type/ProcessResolver.kt | 9 ++-- .../main/resources/graphql/Process.graphqls | 3 +- .../graphql/ProcessInstance.graphqls | 3 +- .../main/resources/graphql/Variable.graphqls | 12 +++++ 10 files changed, 79 insertions(+), 45 deletions(-) create mode 100644 data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt new file mode 100644 index 00000000..eaa49231 --- /dev/null +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt @@ -0,0 +1,12 @@ +package io.zeebe.zeeqs.data.entity + +enum class EqualityOperation { + EQUALS, + CONTAINS +} + +class VariableFilter ( + val name: String, + val value: String, + val equalityOperation: EqualityOperation = EqualityOperation.EQUALS +) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt index 82c87003..54107210 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/repository/VariableRepository.kt @@ -17,4 +17,8 @@ interface VariableRepository : PagingAndSortingRepository { @Transactional(readOnly = true) fun findByProcessInstanceKeyInAndName(processInstanceKey: List, name: String): List + + @Transactional(readOnly = true) + fun findByProcessInstanceKeyInAndNameIn(processInstanceKey: List, name: List): List + } \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt index dc860be7..e9d0468c 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt @@ -1,8 +1,6 @@ package io.zeebe.zeeqs.data.service -import io.zeebe.zeeqs.data.entity.ProcessInstance -import io.zeebe.zeeqs.data.entity.ProcessInstanceState -import io.zeebe.zeeqs.data.entity.Variable +import io.zeebe.zeeqs.data.entity.* import io.zeebe.zeeqs.data.repository.ProcessInstanceKeyOnly import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository import io.zeebe.zeeqs.data.repository.VariableRepository @@ -14,26 +12,35 @@ class ProcessInstanceService( private val processInstancesRepository: ProcessInstanceRepository, private val variableRepository: VariableRepository) { - private fun getVariables(stateIn: List, variableName: String, variableValue: String): List { + private fun getVariables(stateIn: List, variables: List): List { val processInstances = processInstancesRepository.findByStateIn(stateIn).toList(); - return getVariablesByProcessInstanceKeys(processInstances, variableName, variableValue); + return getVariablesByProcessInstanceKeys(processInstances, variables); } - private fun getVariables(stateIn: List, processDefinitionKey: Long, variableName: String, variableValue: String): List { + private fun getVariables(stateIn: List, processDefinitionKey: Long, variables: List): List { val processInstances = processInstancesRepository.findByProcessDefinitionKeyAndStateIn(processDefinitionKey, stateIn).toList(); - return getVariablesByProcessInstanceKeys(processInstances, variableName, variableValue); + return getVariablesByProcessInstanceKeys(processInstances, variables); } - private fun getVariablesByProcessInstanceKeys(processInstances: List, variableName: String, variableValue: String): List { - val variables = variableRepository.findByProcessInstanceKeyInAndName(processInstances.map { it.getKey() }, variableName); - val filteredVariables = variables.filter { it.value == variableValue }; + private fun getVariablesByProcessInstanceKeys(processInstances: List, variables: List): List { + val variableNames = variables.map { it.name } + val processInstancesKeys = processInstances.map { it.getKey() } + val variablesList = variableRepository.findByProcessInstanceKeyInAndNameIn(processInstancesKeys, variableNames); + val filteredVariables = variablesList.filter { variable -> + variables.any { filter -> + when (filter.equalityOperation) { + EqualityOperation.EQUALS -> variable.name == filter.name && variable.value == filter.value + EqualityOperation.CONTAINS -> variable.name == filter.name && variable.value.contains(filter.value) + } + } + } return filteredVariables; } - fun getProcessInstances(perPage: Int, page: Int, stateIn: List, variableName: String?, variableValue: String?): List { - if(variableName != null && variableValue != null) { - val filteredVariables = getVariables(stateIn, variableName, variableValue); + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, variables: List?): List { + if(!variables.isNullOrEmpty()) { + val filteredVariables = getVariables(stateIn, variables); val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); return filteredProcessInstances; } @@ -42,9 +49,9 @@ class ProcessInstanceService( } } - fun countProcessInstances(stateIn: List, variableName: String?, variableValue: String?): Long { - if(variableName != null && variableValue != null) { - val filteredVariables = getVariables(stateIn, variableName, variableValue); + fun countProcessInstances(stateIn: List, variables: List?): Long { + if(!variables.isNullOrEmpty()) { + val filteredVariables = getVariables(stateIn, variables); return filteredVariables.count().toLong(); } @@ -54,9 +61,9 @@ class ProcessInstanceService( } - fun getProcessInstances(perPage: Int, page: Int, stateIn: List, processDefinitionKey: Long, variableName: String?, variableValue: String?): List { - if(variableName != null && variableValue != null) { - val filteredVariables = getVariables(stateIn, processDefinitionKey, variableName, variableValue); + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, processDefinitionKey: Long, variables: List?): List { + if(!variables.isNullOrEmpty()) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variables); val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); return filteredProcessInstances; } @@ -65,9 +72,9 @@ class ProcessInstanceService( } } - fun countProcessInstances(stateIn: List, processDefinitionKey: Long, variableName: String?, variableValue: String?): Long { - if(variableName != null && variableValue != null) { - val filteredVariables = getVariables(stateIn, processDefinitionKey, variableName, variableValue); + fun countProcessInstances(stateIn: List, processDefinitionKey: Long, variables: List?): Long { + if(!variables.isNullOrEmpty()) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variables); return filteredVariables.count().toLong(); } diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt index fa698a17..192a822e 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt @@ -168,11 +168,15 @@ class ProcessService(val processRepository: ProcessRepository) { } private fun getExtensionProperties(element: BaseElement): Collection? { - return element.extensionElements?.elementsQuery + return element.extensionElements?.elementsQuery ?.filterByType(ZeebeProperties::class.java) - ?.singleResult() - ?.properties - ?.map { BpmnElementExtensionProperties(name = it.name, value = it.value) } + ?.findSingleResult() + ?.map { properties -> + properties.properties?.map { property -> + BpmnElementExtensionProperties(name = property.name, value = property.value) + } + } + ?.orElse(null) } diff --git a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt index 8b5445cf..2ca531f5 100644 --- a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt +++ b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt @@ -52,12 +52,12 @@ class ProcessServiceTest( // then assertThat(info) .isNotNull() - .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata(), listOf(BpmnElementExtensionProperties()), ""))) - .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test"), listOf(BpmnElementExtensionProperties()), ""))) + .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata(), null, ""))) + .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test"), null, ""))) .contains(entry("u", BpmnElementInfo("u", "userTask", BpmnElementType.USER_TASK, BpmnElementMetadata( - userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")), listOf(BpmnElementExtensionProperties()), "")) + userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")), null, "")) ) - .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata(), listOf(BpmnElementExtensionProperties()), ""))) + .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata(), null, ""))) } @Test @@ -91,7 +91,7 @@ class ProcessServiceTest( resource = """{"x":1}""" ) ), - listOf(BpmnElementExtensionProperties()), "" + null, "" ) ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt index 7310f050..4b7af6c8 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt @@ -2,6 +2,7 @@ package io.zeebe.zeeqs.graphql.resolvers.query import io.zeebe.zeeqs.data.entity.ProcessInstance import io.zeebe.zeeqs.data.entity.ProcessInstanceState +import io.zeebe.zeeqs.data.entity.VariableFilter import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository import io.zeebe.zeeqs.data.service.ProcessInstanceService import io.zeebe.zeeqs.graphql.resolvers.connection.ProcessInstanceConnection @@ -22,12 +23,11 @@ class ProcessInstanceQueryResolver( @Argument perPage: Int, @Argument page: Int, @Argument stateIn: List, - @Argument variableName: String?, - @Argument variableValue: String? + @Argument variables: List? ): ProcessInstanceConnection { return ProcessInstanceConnection( - getItems = { processInstanceService.getProcessInstances(perPage, page, stateIn, variableName, variableValue) }, - getCount = { processInstanceService.countProcessInstances(stateIn, variableName, variableValue) } + getItems = { processInstanceService.getProcessInstances(perPage, page, stateIn, variables) }, + getCount = { processInstanceService.countProcessInstances(stateIn, variables) } ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt index 98172e21..1a28c8eb 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt @@ -30,8 +30,7 @@ class ProcessResolver( @Argument perPage: Int, @Argument page: Int, @Argument stateIn: List, - @Argument variableName: String?, - @Argument variableValue: String? + @Argument variables: List? ): ProcessInstanceConnection { return ProcessInstanceConnection( getItems = { @@ -40,16 +39,14 @@ class ProcessResolver( page, stateIn, process.key, - variableName, - variableValue + variables ).toList() }, getCount = { processInstanceService.countProcessInstances( stateIn, process.key, - variableName, - variableValue + variables ) } ) diff --git a/graphql-api/src/main/resources/graphql/Process.graphqls b/graphql-api/src/main/resources/graphql/Process.graphqls index d182d2bd..78c65ba6 100644 --- a/graphql-api/src/main/resources/graphql/Process.graphqls +++ b/graphql-api/src/main/resources/graphql/Process.graphqls @@ -14,8 +14,7 @@ type Process { perPage: Int = 10, page: Int = 0, stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED] - variableName: String = null, - variableValue: String = null): ProcessInstanceConnection! + variables: [VariableFilter] = null): ProcessInstanceConnection! # the scheduled timers of the timer start events of the process timers: [Timer!] # the opened message subscriptions of the message start events of the process diff --git a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls index ff7afe3e..470fedec 100644 --- a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls +++ b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls @@ -74,8 +74,7 @@ type Query { perPage: Int = 10, page: Int = 0, stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED], - variableName: String = null, - variableValue: String = null + variables: [VariableFilter] = null ): ProcessInstanceConnection! } diff --git a/graphql-api/src/main/resources/graphql/Variable.graphqls b/graphql-api/src/main/resources/graphql/Variable.graphqls index 29e38f97..a6d467ff 100644 --- a/graphql-api/src/main/resources/graphql/Variable.graphqls +++ b/graphql-api/src/main/resources/graphql/Variable.graphqls @@ -14,3 +14,15 @@ type VariableUpdate { value: String! timestamp(zoneId: String = "Z"): String! } + +input VariableFilter { + name: String!, + value: String!, + equalityOperation: EqualityOperation = EQUALS +} + +# The type of a variable value filter +enum EqualityOperation { + EQUALS, + CONTAINS +} From 556c15630c5c3e0a0a43f8cce9163630d9c5a943 Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Mon, 6 May 2024 13:02:03 +0300 Subject: [PATCH 5/6] feat: add filter operation to variables filter --- .../zeebe/zeeqs/data/entity/VariableFilter.kt | 14 ++++- .../data/service/ProcessInstanceService.kt | 59 +++++++++++-------- .../query/ProcessInstanceQueryResolver.kt | 8 +-- .../graphql/resolvers/type/ProcessResolver.kt | 6 +- .../main/resources/graphql/Process.graphqls | 2 +- .../graphql/ProcessInstance.graphqls | 2 +- .../main/resources/graphql/Variable.graphqls | 15 ++++- 7 files changed, 66 insertions(+), 40 deletions(-) diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt index eaa49231..4cfee606 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/entity/VariableFilter.kt @@ -1,12 +1,22 @@ package io.zeebe.zeeqs.data.entity -enum class EqualityOperation { +enum class ComparisonOperation { EQUALS, CONTAINS } +enum class FilterOperation { + AND, + OR +} + class VariableFilter ( val name: String, val value: String, - val equalityOperation: EqualityOperation = EqualityOperation.EQUALS + val comparisonOperation: ComparisonOperation = ComparisonOperation.EQUALS +) + +class VariableFilterGroup ( + val variables: List, + val filterOperation: FilterOperation = FilterOperation.OR ) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt index e9d0468c..a8d24311 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessInstanceService.kt @@ -12,35 +12,42 @@ class ProcessInstanceService( private val processInstancesRepository: ProcessInstanceRepository, private val variableRepository: VariableRepository) { - private fun getVariables(stateIn: List, variables: List): List { + private fun getVariables(stateIn: List, variableFilterGroup: VariableFilterGroup): List { val processInstances = processInstancesRepository.findByStateIn(stateIn).toList(); - return getVariablesByProcessInstanceKeys(processInstances, variables); + return getVariablesByProcessInstanceKeys(processInstances, variableFilterGroup); } - private fun getVariables(stateIn: List, processDefinitionKey: Long, variables: List): List { + private fun getVariables(stateIn: List, processDefinitionKey: Long, variableFilterGroup: VariableFilterGroup): List { val processInstances = processInstancesRepository.findByProcessDefinitionKeyAndStateIn(processDefinitionKey, stateIn).toList(); - return getVariablesByProcessInstanceKeys(processInstances, variables); + return getVariablesByProcessInstanceKeys(processInstances, variableFilterGroup); } - private fun getVariablesByProcessInstanceKeys(processInstances: List, variables: List): List { - val variableNames = variables.map { it.name } + private fun matchesFilter(variable: Variable, filter: VariableFilter): Boolean { + return when (filter.comparisonOperation) { + ComparisonOperation.EQUALS -> variable.name == filter.name && variable.value == filter.value + ComparisonOperation.CONTAINS -> variable.name == filter.name && variable.value.contains(filter.value) + } + } + + private fun getVariablesByProcessInstanceKeys(processInstances: List, variableFilterGroup: VariableFilterGroup): List { val processInstancesKeys = processInstances.map { it.getKey() } - val variablesList = variableRepository.findByProcessInstanceKeyInAndNameIn(processInstancesKeys, variableNames); - val filteredVariables = variablesList.filter { variable -> - variables.any { filter -> - when (filter.equalityOperation) { - EqualityOperation.EQUALS -> variable.name == filter.name && variable.value == filter.value - EqualityOperation.CONTAINS -> variable.name == filter.name && variable.value.contains(filter.value) - } + val variableNames = variableFilterGroup.variables.map { it.name } + val variablesList = variableRepository.findByProcessInstanceKeyInAndNameIn(processInstancesKeys, variableNames) + + return variablesList.filter { variable -> + if (variableFilterGroup.filterOperation == FilterOperation.AND) { + variableFilterGroup.variables.all { matchesFilter(variable, it) } + } else { + variableFilterGroup.variables.any { matchesFilter(variable, it) } } } - return filteredVariables; } - fun getProcessInstances(perPage: Int, page: Int, stateIn: List, variables: List?): List { - if(!variables.isNullOrEmpty()) { - val filteredVariables = getVariables(stateIn, variables); + + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, variableFilterGroup: VariableFilterGroup?): List { + if (variableFilterGroup?.variables?.isNotEmpty() == true) { + val filteredVariables = getVariables(stateIn, variableFilterGroup); val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); return filteredProcessInstances; } @@ -49,9 +56,9 @@ class ProcessInstanceService( } } - fun countProcessInstances(stateIn: List, variables: List?): Long { - if(!variables.isNullOrEmpty()) { - val filteredVariables = getVariables(stateIn, variables); + fun countProcessInstances(stateIn: List, variableFilterGroup: VariableFilterGroup?): Long { + if (variableFilterGroup?.variables?.isNotEmpty() == true) { + val filteredVariables = getVariables(stateIn, variableFilterGroup); return filteredVariables.count().toLong(); } @@ -61,9 +68,9 @@ class ProcessInstanceService( } - fun getProcessInstances(perPage: Int, page: Int, stateIn: List, processDefinitionKey: Long, variables: List?): List { - if(!variables.isNullOrEmpty()) { - val filteredVariables = getVariables(stateIn, processDefinitionKey, variables); + fun getProcessInstances(perPage: Int, page: Int, stateIn: List, processDefinitionKey: Long, variableFilterGroup: VariableFilterGroup?): List { + if (variableFilterGroup?.variables?.isNotEmpty() == true) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variableFilterGroup); val filteredProcessInstances = processInstancesRepository.findByStateInAndKeyIn(stateIn, filteredVariables.map { it.processInstanceKey }, PageRequest.of(page, perPage)).toList(); return filteredProcessInstances; } @@ -72,9 +79,9 @@ class ProcessInstanceService( } } - fun countProcessInstances(stateIn: List, processDefinitionKey: Long, variables: List?): Long { - if(!variables.isNullOrEmpty()) { - val filteredVariables = getVariables(stateIn, processDefinitionKey, variables); + fun countProcessInstances(stateIn: List, processDefinitionKey: Long, variableFilterGroup: VariableFilterGroup?): Long { + if (variableFilterGroup?.variables?.isNotEmpty() == true) { + val filteredVariables = getVariables(stateIn, processDefinitionKey, variableFilterGroup); return filteredVariables.count().toLong(); } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt index 4b7af6c8..10679efc 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/query/ProcessInstanceQueryResolver.kt @@ -2,7 +2,7 @@ package io.zeebe.zeeqs.graphql.resolvers.query import io.zeebe.zeeqs.data.entity.ProcessInstance import io.zeebe.zeeqs.data.entity.ProcessInstanceState -import io.zeebe.zeeqs.data.entity.VariableFilter +import io.zeebe.zeeqs.data.entity.VariableFilterGroup import io.zeebe.zeeqs.data.repository.ProcessInstanceRepository import io.zeebe.zeeqs.data.service.ProcessInstanceService import io.zeebe.zeeqs.graphql.resolvers.connection.ProcessInstanceConnection @@ -23,11 +23,11 @@ class ProcessInstanceQueryResolver( @Argument perPage: Int, @Argument page: Int, @Argument stateIn: List, - @Argument variables: List? + @Argument variablesFilter: VariableFilterGroup? ): ProcessInstanceConnection { return ProcessInstanceConnection( - getItems = { processInstanceService.getProcessInstances(perPage, page, stateIn, variables) }, - getCount = { processInstanceService.countProcessInstances(stateIn, variables) } + getItems = { processInstanceService.getProcessInstances(perPage, page, stateIn, variablesFilter) }, + getCount = { processInstanceService.countProcessInstances(stateIn, variablesFilter) } ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt index 1a28c8eb..c57a7eba 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/ProcessResolver.kt @@ -30,7 +30,7 @@ class ProcessResolver( @Argument perPage: Int, @Argument page: Int, @Argument stateIn: List, - @Argument variables: List? + @Argument variablesFilter: VariableFilterGroup? ): ProcessInstanceConnection { return ProcessInstanceConnection( getItems = { @@ -39,14 +39,14 @@ class ProcessResolver( page, stateIn, process.key, - variables + variablesFilter ).toList() }, getCount = { processInstanceService.countProcessInstances( stateIn, process.key, - variables + variablesFilter ) } ) diff --git a/graphql-api/src/main/resources/graphql/Process.graphqls b/graphql-api/src/main/resources/graphql/Process.graphqls index 78c65ba6..0a9071eb 100644 --- a/graphql-api/src/main/resources/graphql/Process.graphqls +++ b/graphql-api/src/main/resources/graphql/Process.graphqls @@ -14,7 +14,7 @@ type Process { perPage: Int = 10, page: Int = 0, stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED] - variables: [VariableFilter] = null): ProcessInstanceConnection! + variablesFilter: VariableFilterGroup = null): ProcessInstanceConnection! # the scheduled timers of the timer start events of the process timers: [Timer!] # the opened message subscriptions of the message start events of the process diff --git a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls index 470fedec..de27d421 100644 --- a/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls +++ b/graphql-api/src/main/resources/graphql/ProcessInstance.graphqls @@ -74,7 +74,7 @@ type Query { perPage: Int = 10, page: Int = 0, stateIn: [ProcessInstanceState!] = [ACTIVATED, COMPLETED, TERMINATED], - variables: [VariableFilter] = null + variablesFilter: VariableFilterGroup = null ): ProcessInstanceConnection! } diff --git a/graphql-api/src/main/resources/graphql/Variable.graphqls b/graphql-api/src/main/resources/graphql/Variable.graphqls index a6d467ff..b2ff88a1 100644 --- a/graphql-api/src/main/resources/graphql/Variable.graphqls +++ b/graphql-api/src/main/resources/graphql/Variable.graphqls @@ -18,11 +18,20 @@ type VariableUpdate { input VariableFilter { name: String!, value: String!, - equalityOperation: EqualityOperation = EQUALS + comparisonOperation: ComparisonOperation! } -# The type of a variable value filter -enum EqualityOperation { +input VariableFilterGroup { + variables: [VariableFilter!] + filterOperation: FilterOperation = OR +} + +enum ComparisonOperation { EQUALS, CONTAINS } + +enum FilterOperation { + AND, + OR +} \ No newline at end of file From 08632d96366b5344f84e5ff068f650314c1ddc5d Mon Sep 17 00:00:00 2001 From: Hussain Mubaireek Date: Wed, 8 May 2024 10:42:23 +0300 Subject: [PATCH 6/6] feat: add extensionElements to return properties and ioMapping --- .../service/BpmnElementExtensionProperties.kt | 6 ---- .../zeeqs/data/service/BpmnElementInfo.kt | 4 +-- .../data/service/BpmnExtensionElements.kt | 20 +++++++++++++ .../zeeqs/data/service/ProcessService.kt | 30 +++++++++++++++---- .../io/zeebe/zeeqs/ProcessServiceTest.kt | 12 ++++---- .../resolvers/type/BpmnElementResolver.kt | 11 +++---- .../main/resources/graphql/Element.graphqls | 27 ++++++++++++----- 7 files changed, 75 insertions(+), 35 deletions(-) delete mode 100644 data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt create mode 100644 data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnExtensionElements.kt diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt deleted file mode 100644 index 5bc1f02e..00000000 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementExtensionProperties.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.zeebe.zeeqs.data.service - -data class BpmnElementExtensionProperties ( - val name: String? = null, - val value: String? = null, -) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt index 74520c5a..1271be1f 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnElementInfo.kt @@ -1,7 +1,5 @@ package io.zeebe.zeeqs.data.service -import io.camunda.zeebe.model.bpmn.instance.Documentation -import io.camunda.zeebe.model.bpmn.instance.zeebe.ZeebeProperty import io.zeebe.zeeqs.data.entity.BpmnElementType data class BpmnElementInfo( @@ -9,6 +7,6 @@ data class BpmnElementInfo( val elementName: String?, val elementType: BpmnElementType, val metadata: BpmnElementMetadata, - val extensionProperties: Collection?, + val extensionElements: BpmnExtensionElements?, val documentation: String? ) \ No newline at end of file diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnExtensionElements.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnExtensionElements.kt new file mode 100644 index 00000000..e216551a --- /dev/null +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/BpmnExtensionElements.kt @@ -0,0 +1,20 @@ +package io.zeebe.zeeqs.data.service + +data class BpmnExtensionProperty( + val name: String?, + val value: String? +) + +data class IoMapping ( + val source: String?, + val target: String? +) +data class ExtensionIoMapping( + val inputs: List? = emptyList(), + val outputs: List? = emptyList(), +) +data class BpmnExtensionElements( + val properties: List? = emptyList(), + val ioMapping: ExtensionIoMapping? = ExtensionIoMapping(), +) + diff --git a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt index 192a822e..49a2272c 100644 --- a/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt +++ b/data/src/main/kotlin/io/zeebe/zeeqs/data/service/ProcessService.kt @@ -27,7 +27,7 @@ class ProcessService(val processRepository: ProcessRepository) { elementName = flowElement.name, elementType = getBpmnElementType(flowElement), metadata = getMetadata(flowElement), - extensionProperties = getExtensionProperties(flowElement), + extensionElements = getExtensionElements(flowElement), documentation = getDocumentation(flowElement) ) }.orEmpty() + @@ -37,7 +37,7 @@ class ProcessService(val processRepository: ProcessRepository) { elementName = groupElement.category?.value ?: "", elementType = getBpmnElementType(groupElement), metadata = getMetadata(groupElement), - extensionProperties = getExtensionProperties(groupElement), + extensionElements = getExtensionElements(groupElement), documentation = getDocumentation(groupElement) ) }.orEmpty()) @@ -167,16 +167,34 @@ class ProcessService(val processRepository: ProcessRepository) { ) } - private fun getExtensionProperties(element: BaseElement): Collection? { - return element.extensionElements?.elementsQuery + private fun getExtensionElements(element: BaseElement): BpmnExtensionElements { + + val properties = element.extensionElements?.elementsQuery ?.filterByType(ZeebeProperties::class.java) ?.findSingleResult() ?.map { properties -> properties.properties?.map { property -> - BpmnElementExtensionProperties(name = property.name, value = property.value) + BpmnExtensionProperty(name = property.name, value = property.value) } } - ?.orElse(null) + ?.orElse(emptyList()) ?: emptyList() + + val ioMapping = element.extensionElements?.elementsQuery + ?.filterByType(ZeebeIoMapping::class.java) + ?.findSingleResult() + ?.map { mapping -> + ExtensionIoMapping( + inputs = mapping.inputs?.map { input -> + IoMapping(source = input.source, target = input.target) + } ?: emptyList(), + outputs = mapping.outputs?.map { output -> + IoMapping(source = output.source, target = output.target) + } ?: emptyList() + ) + } + ?.orElse(ExtensionIoMapping(inputs = emptyList(), outputs = emptyList())) ?: ExtensionIoMapping(inputs = emptyList(), outputs = emptyList()) + + return BpmnExtensionElements(properties = properties, ioMapping = ioMapping) } diff --git a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt index 2ca531f5..c6e0d7ee 100644 --- a/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt +++ b/data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt @@ -12,7 +12,6 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.TestConfiguration import java.time.Instant @@ -52,12 +51,12 @@ class ProcessServiceTest( // then assertThat(info) .isNotNull() - .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata(), null, ""))) - .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test"), null, ""))) + .contains(entry("s", BpmnElementInfo("s", "start", BpmnElementType.START_EVENT, BpmnElementMetadata(), BpmnExtensionElements(properties = emptyList(), ioMapping = ExtensionIoMapping()), ""))) + .contains(entry("t", BpmnElementInfo("t", "task", BpmnElementType.SERVICE_TASK, BpmnElementMetadata(jobType = "test"), BpmnExtensionElements(properties = emptyList(), ioMapping = ExtensionIoMapping()), ""))) .contains(entry("u", BpmnElementInfo("u", "userTask", BpmnElementType.USER_TASK, BpmnElementMetadata( - userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")), null, "")) + userTaskAssignmentDefinition = UserTaskAssignmentDefinition(assignee = "user1", candidateGroups = "group1")), BpmnExtensionElements(properties = emptyList(), ioMapping = ExtensionIoMapping()), "")) ) - .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata(), null, ""))) + .contains(entry("e", BpmnElementInfo("e", null, BpmnElementType.END_EVENT, BpmnElementMetadata(), BpmnExtensionElements(properties = emptyList(), ioMapping = ExtensionIoMapping()), ""))) } @Test @@ -91,7 +90,8 @@ class ProcessServiceTest( resource = """{"x":1}""" ) ), - null, "" + BpmnExtensionElements(properties = emptyList(), ioMapping = ExtensionIoMapping()), + "" ) ) } diff --git a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt index 1704a716..ae445c1b 100644 --- a/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt +++ b/graphql-api/src/main/kotlin/io/zeebe/zeeqs/graphql/resolvers/type/BpmnElementResolver.kt @@ -5,10 +5,7 @@ import io.zeebe.zeeqs.data.entity.ElementInstanceState import io.zeebe.zeeqs.data.entity.Process import io.zeebe.zeeqs.data.repository.ElementInstanceRepository import io.zeebe.zeeqs.data.repository.ProcessRepository -import io.zeebe.zeeqs.data.service.BpmnElementExtensionProperties -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.* import io.zeebe.zeeqs.graphql.resolvers.connection.ElementInstanceConnection import org.springframework.data.domain.PageRequest import org.springframework.data.repository.findByIdOrNull @@ -43,10 +40,10 @@ class BpmnElementResolver( } - @SchemaMapping(typeName = "BpmnElement", field = "extensionProperties") - fun extensionProperties(element: BpmnElement): Collection? { + @SchemaMapping(typeName = "BpmnElement", field = "extensionElements") + fun extensionElements(element: BpmnElement): BpmnExtensionElements? { return findElementInfo(element) - ?.extensionProperties + ?.extensionElements } diff --git a/graphql-api/src/main/resources/graphql/Element.graphqls b/graphql-api/src/main/resources/graphql/Element.graphqls index 3209d162..d6bb264f 100644 --- a/graphql-api/src/main/resources/graphql/Element.graphqls +++ b/graphql-api/src/main/resources/graphql/Element.graphqls @@ -11,7 +11,7 @@ type BpmnElement { metadata: BpmnElementMetadata! # extension properties of the BPMN element - extensionProperties: [BpmnElementExtensionProperties] + extensionElements: BpmnExtensionElements # documentation of the BPMN element documentation: String @@ -93,9 +93,22 @@ type UserTaskAssignmentDefinition { candidateGroups: String } -type BpmnElementExtensionProperties { - # the name of property - name: String - # the value of property - value: String -} \ No newline at end of file +type BpmnExtensionProperty { + name: String + value: String +} + +type IoMapping { + source: String + target: String +} + +type ExtensionIoMapping { + inputs: [IoMapping] + outputs: [IoMapping] +} + +type BpmnExtensionElements { + properties: [BpmnExtensionProperty] + ioMapping: ExtensionIoMapping +}