Skip to content

Commit

Permalink
Get variables of process instance (#313)
Browse files Browse the repository at this point in the history
* feat: get variables of element instance

* feat: get all global variables

* test: fix test cases with Spring
  • Loading branch information
saig0 authored Dec 14, 2022
1 parent 30bcda2 commit 5a911a1
Show file tree
Hide file tree
Showing 10 changed files with 622 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ interface VariableRepository : PagingAndSortingRepository<Variable, Long> {

@Transactional(readOnly = true)
fun findByProcessInstanceKey(processInstanceKey: Long): List<Variable>

@Transactional(readOnly = true)
fun findByScopeKey(scopeKey: Long): List<Variable>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.zeebe.zeeqs.data.service

import io.zeebe.zeeqs.data.entity.Variable
import io.zeebe.zeeqs.data.repository.ElementInstanceRepository
import io.zeebe.zeeqs.data.repository.VariableRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component

@Component
class VariableService(
private val variableRepository: VariableRepository,
private val elementInstanceRepository: ElementInstanceRepository) {

fun getVariables(elementInstanceKey: Long, localOnly: Boolean, shadowing: Boolean): List<Variable> {
val localVariables = variableRepository.findByScopeKey(scopeKey = elementInstanceKey)

if (localOnly) {
return localVariables
}

val localVariableNames = localVariables.map { it.name }

return elementInstanceRepository.findByIdOrNull(elementInstanceKey)
?.scopeKey
?.let { flowScopeKey ->
getVariables(
elementInstanceKey = flowScopeKey,
localOnly = false,
shadowing = shadowing)
}
?.filterNot { flowScopeVariable ->
shadowing && localVariableNames.contains(flowScopeVariable.name)
}
?.let { flowScopeVariables -> flowScopeVariables + localVariables }
?: localVariables
}

}
6 changes: 2 additions & 4 deletions data/src/test/kotlin/io/zeebe/zeeqs/ProcessServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import java.time.Instant
import javax.transaction.Transactional

@SpringBootTest
@TestConfiguration
@Transactional
class ProcessServiceTest(
@Autowired val processService: ProcessService,
@Autowired val processRepository: ProcessRepository
Expand Down Expand Up @@ -148,8 +150,4 @@ class ProcessServiceTest(
}
}


@SpringBootApplication
class TestConfiguration

}
7 changes: 7 additions & 0 deletions data/src/test/kotlin/io/zeebe/zeeqs/TestConfiguration.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.zeebe.zeeqs

import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class TestConfiguration {
}
226 changes: 226 additions & 0 deletions data/src/test/kotlin/io/zeebe/zeeqs/VariableServiceTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package io.zeebe.zeeqs

import io.zeebe.zeeqs.data.entity.BpmnElementType
import io.zeebe.zeeqs.data.entity.ElementInstance
import io.zeebe.zeeqs.data.entity.Variable
import io.zeebe.zeeqs.data.repository.ElementInstanceRepository
import io.zeebe.zeeqs.data.repository.VariableRepository
import io.zeebe.zeeqs.data.service.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import java.util.stream.LongStream

import org.assertj.core.api.Assertions.*
import org.springframework.beans.factory.annotation.Autowired
import javax.transaction.Transactional

@SpringBootTest
@TestConfiguration
@Transactional
class VariableServiceTest(
@Autowired val variableService: VariableService,
@Autowired val variableRepository: VariableRepository,
@Autowired val elementInstanceRepository: ElementInstanceRepository
) {

private val processInstanceKey = 10L
private val scope_1_1 = 11L
private val scope_1_2 = 12L
private val scope_1_3 = 13L
private val scope_2_1 = 21L

private val nextVariableKey = LongStream.range(1, 100).iterator()

@BeforeEach
fun `create variables`() {
createVariable(name = "global_1", value = "1", scopeKey = processInstanceKey)
createVariable(name = "global_2", value = "2", scopeKey = processInstanceKey)
createVariable(name = "var_1_1", value = "1_1", scopeKey = scope_1_1)
createVariable(name = "var_1_2", value = "1_2", scopeKey = scope_1_2)
createVariable(name = "var_1_1", value = "1_3", scopeKey = scope_1_3)
createVariable(name = "var_1_2", value = "1_3", scopeKey = scope_1_3)
createVariable(name = "var_1_3", value = "1_3", scopeKey = scope_1_3)
createVariable(name = "var_2_1", value = "2_1", scopeKey = scope_2_1)

createScope(key = processInstanceKey, scopeKey = null)
createScope(key = scope_1_1, scopeKey = processInstanceKey)
createScope(key = scope_1_2, scopeKey = scope_1_1)
createScope(key = scope_1_3, scopeKey = scope_1_2)
createScope(key = scope_2_1, scopeKey = processInstanceKey)
}

@Test
fun `get global variables`() {
// when/then
assertThat(
variableService.getVariables(
elementInstanceKey = processInstanceKey,
localOnly = true,
shadowing = false))
.hasSize(2)
.extracting(Variable::name, Variable::value)
.contains(
tuple("global_1", "1"),
tuple("global_2", "2")
)
}

@Test
fun `get local variables`() {
// when/then
assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_1,
localOnly = true,
shadowing = false))
.hasSize(1)
.extracting(Variable::name, Variable::value)
.contains(tuple("var_1_1", "1_1"))

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_2,
localOnly = true,
shadowing = false))
.hasSize(1)
.extracting(Variable::name, Variable::value)
.contains(tuple("var_1_2", "1_2"))

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_3,
localOnly = true,
shadowing = false))
.hasSize(3)
.extracting(Variable::name, Variable::value)
.contains(
tuple("var_1_1", "1_3"),
tuple("var_1_2", "1_3"),
tuple("var_1_3", "1_3")
)
}

@Test
fun `get all variables`() {
// when/then
assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_1,
localOnly = false,
shadowing = false))
.hasSize(3)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_1", scope_1_1)
)

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_2,
localOnly = false,
shadowing = false))
.hasSize(4)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_1", scope_1_1),
tuple("var_1_2", "1_2", scope_1_2)
)

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_3,
localOnly = false,
shadowing = false))
.hasSize(7)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_1", scope_1_1),
tuple("var_1_2", "1_2", scope_1_2),
tuple("var_1_1", "1_3", scope_1_3),
tuple("var_1_2", "1_3", scope_1_3),
tuple("var_1_3", "1_3", scope_1_3)
)
}

@Test
fun `get all variables with shadowing`() {
// when/then
assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_1,
localOnly = false,
shadowing = true))
.hasSize(3)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_1", scope_1_1)
)

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_2,
localOnly = false,
shadowing = true))
.hasSize(4)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_1", scope_1_1),
tuple("var_1_2", "1_2", scope_1_2)
)

assertThat(
variableService.getVariables(
elementInstanceKey = scope_1_3,
localOnly = false,
shadowing = true))
.hasSize(5)
.extracting(Variable::name, Variable::value, Variable::scopeKey)
.contains(
tuple("global_1", "1", processInstanceKey),
tuple("global_2", "2", processInstanceKey),
tuple("var_1_1", "1_3", scope_1_3),
tuple("var_1_2", "1_3", scope_1_3),
tuple("var_1_3", "1_3", scope_1_3)
)
}

private fun createVariable(name: String, value: String, scopeKey: Long) {
variableRepository.save(
Variable(
name = name,
value = value,
scopeKey = scopeKey,
processInstanceKey = processInstanceKey,
key = nextVariableKey.next(),
position = 2L,
timestamp = 1L
)
)
}

private fun createScope(key: Long, scopeKey: Long?) {
elementInstanceRepository.save(ElementInstance(
key = key,
position = 1,
elementId = "",
bpmnElementType = BpmnElementType.UNSPECIFIED,
processInstanceKey = processInstanceKey,
processDefinitionKey = 1L,
scopeKey = scopeKey
))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.zeebe.zeeqs.data.entity.*
import io.zeebe.zeeqs.data.repository.*
import io.zeebe.zeeqs.graphql.resolvers.type.BpmnElement
import io.zeebe.zeeqs.data.service.ProcessService
import io.zeebe.zeeqs.data.service.VariableService
import io.zeebe.zeeqs.graphql.resolvers.type.ResolverExtension
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component
Expand All @@ -17,7 +18,8 @@ class ElementInstanceResolver(
val elementInstanceStateTransitionRepository: ElementInstanceStateTransitionRepository,
val timerRepository: TimerRepository,
val processService: ProcessService,
val messageSubscriptionRepository: MessageSubscriptionRepository
val messageSubscriptionRepository: MessageSubscriptionRepository,
val variableService: VariableService
) : GraphQLResolver<ElementInstance> {

fun startTime(elementInstance: ElementInstance, zoneId: String): String? {
Expand Down Expand Up @@ -67,4 +69,12 @@ class ElementInstanceResolver(
)
}

fun variables(elementInstance: ElementInstance, localOnly: Boolean, shadowing: Boolean): List<Variable> {
return variableService.getVariables(
elementInstanceKey = elementInstance.key,
localOnly = localOnly,
shadowing = shadowing
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.zeebe.zeeqs.data.resolvers
import graphql.kickstart.tools.GraphQLResolver
import io.zeebe.zeeqs.data.entity.*
import io.zeebe.zeeqs.data.repository.*
import io.zeebe.zeeqs.data.service.VariableService
import io.zeebe.zeeqs.graphql.resolvers.connection.UserTaskConnection
import io.zeebe.zeeqs.graphql.resolvers.type.ResolverExtension.timestampToString
import org.springframework.data.domain.PageRequest
Expand Down Expand Up @@ -31,8 +32,12 @@ class ProcessInstanceResolver(
return processInstance.endTime?.let { timestampToString(it, zoneId) }
}

fun variables(processInstance: ProcessInstance): List<Variable> {
return variableRepository.findByProcessInstanceKey(processInstance.key)
fun variables(processInstance: ProcessInstance, globalOnly: Boolean): List<Variable> {
return if (globalOnly) {
variableRepository.findByScopeKey(scopeKey = processInstance.key)
} else {
variableRepository.findByProcessInstanceKey(processInstanceKey = processInstance.key)
}
}

fun jobs(processInstance: ProcessInstance, stateIn: List<JobState>, jobTypeIn: List<String>): List<Job> {
Expand Down
10 changes: 10 additions & 0 deletions graphql-api/src/main/resources/graphql/ElementInstance.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ type ElementInstance {
messageSubscriptions: [MessageSubscription!]

element: BpmnElement!

# The variables in the scope of the element instance.
variables(
# If true, it returns only the local variables of the element instance.
# If false, it includes the variables from the flow scopes.
localOnly: Boolean = true,
# If a variable with the same name is set in multiple scopes and shadowing is true, it returns only the variable of the nearest scope from the element instance.
# If false, it returns all variables, including variables with the same name.
shadowing: Boolean = true
): [Variable!]!
}

type ElementInstanceStateTransition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ type ProcessInstance {
# the process the instance is created of
process: Process
# the created variables related to the process instance
variables: [Variable!]
variables(
# If true, it returns only the global variables of the process instance.
# If false, it includes the local variables from the containing scopes.
globalOnly: Boolean = false
): [Variable!]
# the created jobs related to the process instance
jobs(
stateIn: [JobState!] = [ACTIVATABLE, FAILED, COMPLETED, CANCELED, ERROR_THROWN]
Expand Down
Loading

0 comments on commit 5a911a1

Please sign in to comment.