@ApiOperation(value = "List of process definitions", tags = { "Process Definitions" }, nickname = "listProcessDefinitions")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "version", dataType = "integer", value = "Only return process definitions with the given version.", paramType = "query"),
            @ApiImplicitParam(name = "name", dataType = "string", value = "Only return process definitions with the given name.", paramType = "query"),
            @ApiImplicitParam(name = "nameLike", dataType = "string", value = "Only return process definitions with a name like the given name.", paramType = "query"),
            @ApiImplicitParam(name = "nameLikeIgnoreCase", dataType = "string", value = "Only return process definitions with a name like the given name ignoring case.", paramType = "query"),
            @ApiImplicitParam(name = "key", dataType = "string", value = "Only return process definitions with the given key.", paramType = "query"),
            @ApiImplicitParam(name = "keyLike", dataType = "string", value = "Only return process definitions with a name like the given key.", paramType = "query"),
            @ApiImplicitParam(name = "resourceName", dataType = "string", value = "Only return process definitions with the given resource name.", paramType = "query"),
            @ApiImplicitParam(name = "resourceNameLike", dataType = "string", value = "Only return process definitions with a name like the given resource name.", paramType = "query"),
            @ApiImplicitParam(name = "category", dataType = "string", value = "Only return process definitions with the given category.", paramType = "query"),
            @ApiImplicitParam(name = "categoryLike", dataType = "string", value = "Only return process definitions with a category like the given name.", paramType = "query"),
            @ApiImplicitParam(name = "categoryNotEquals", dataType = "string", value = "Only return process definitions which do not have the given category.", paramType = "query"),
            @ApiImplicitParam(name = "deploymentId", dataType = "string", value = "Only return process definitions which are part of a deployment with the given deployment id.", paramType = "query"),
            @ApiImplicitParam(name = "parentDeploymentId", dataType = "string", value = "Only return process definitions which are part of a deployment with the given parent deployment id.", paramType = "query"),
            @ApiImplicitParam(name = "startableByUser", dataType = "string", value = "Only return process definitions which are part of a deployment with the given id.", paramType = "query"),
            @ApiImplicitParam(name = "latest", dataType = "boolean", value = "Only return the latest process definition versions. Can only be used together with key and keyLike parameters, using any other parameter will result in a 400-response.", paramType = "query"),
            @ApiImplicitParam(name = "suspended", dataType = "boolean", value = "If true, only returns process definitions which are suspended. If false, only active process definitions (which are not suspended) are returned.", paramType = "query"),
            @ApiImplicitParam(name = "sort", dataType = "string", value = "Property to sort on, to be used together with the order.", allowableValues = "name,id,key,category,deploymentId,version", paramType = "query"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Indicates request was successful and the process-definitions are returned"),
            @ApiResponse(code = 400, message = "Indicates a parameter was passed in the wrong format or that latest is used with other parameters other than key and keyLike. The status-message contains additional information.")
    })
    @GetMapping(value = "/repository/process-definitions", produces = "application/json")
    public DataResponse<ProcessDefinitionResponse> getProcessDefinitions(@ApiParam(hidden = true) @RequestParam Map<String, String> allRequestParams) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

        // Populate filter-parameters
        if (allRequestParams.containsKey("category")) {
            processDefinitionQuery.processDefinitionCategory(allRequestParams.get("category"));
        }
        if (allRequestParams.containsKey("categoryLike")) {
            processDefinitionQuery.processDefinitionCategoryLike(allRequestParams.get("categoryLike"));
        }
        if (allRequestParams.containsKey("categoryNotEquals")) {
            processDefinitionQuery.processDefinitionCategoryNotEquals(allRequestParams.get("categoryNotEquals"));
        }
        if (allRequestParams.containsKey("key")) {
            processDefinitionQuery.processDefinitionKey(allRequestParams.get("key"));
        }
        if (allRequestParams.containsKey("keyLike")) {
            processDefinitionQuery.processDefinitionKeyLike(allRequestParams.get("keyLike"));
        }
        if (allRequestParams.containsKey("name")) {
            processDefinitionQuery.processDefinitionName(allRequestParams.get("name"));
        }
        if (allRequestParams.containsKey("nameLike")) {
            processDefinitionQuery.processDefinitionNameLike(allRequestParams.get("nameLike"));
        }
        if (allRequestParams.containsKey("nameLikeIgnoreCase")) {
            processDefinitionQuery.processDefinitionNameLikeIgnoreCase(allRequestParams.get("nameLikeIgnoreCase"));
        }
        if (allRequestParams.containsKey("resourceName")) {
            processDefinitionQuery.processDefinitionResourceName(allRequestParams.get("resourceName"));
        }
        if (allRequestParams.containsKey("resourceNameLike")) {
            processDefinitionQuery.processDefinitionResourceNameLike(allRequestParams.get("resourceNameLike"));
        }
        if (allRequestParams.containsKey("version")) {
            processDefinitionQuery.processDefinitionVersion(Integer.valueOf(allRequestParams.get("version")));
        }
        if (allRequestParams.containsKey("suspended")) {
            if (Boolean.parseBoolean(allRequestParams.get("suspended"))) {
                processDefinitionQuery.suspended();
            } else {
                processDefinitionQuery.active();
            }
        }
        if (allRequestParams.containsKey("latest")) {
            if (Boolean.parseBoolean(allRequestParams.get("latest"))) {
                processDefinitionQuery.latestVersion();
            }
        }
        if (allRequestParams.containsKey("deploymentId")) {
            processDefinitionQuery.deploymentId(allRequestParams.get("deploymentId"));
        }
        if (allRequestParams.containsKey("parentDeploymentId")) {
            processDefinitionQuery.parentDeploymentId(allRequestParams.get("parentDeploymentId"));
        }
        if (allRequestParams.containsKey("startableByUser")) {
            processDefinitionQuery.startableByUser(allRequestParams.get("startableByUser"));
        }
        if (allRequestParams.containsKey("tenantId")) {
            processDefinitionQuery.processDefinitionTenantId(allRequestParams.get("tenantId"));
        }
        if (allRequestParams.containsKey("tenantIdLike")) {
            processDefinitionQuery.processDefinitionTenantIdLike(allRequestParams.get("tenantIdLike"));
        }
        
        if (restApiInterceptor != null) {
            restApiInterceptor.accessProcessDefinitionsWithQuery(processDefinitionQuery);
        }

        return paginateList(allRequestParams, processDefinitionQuery, "name", properties, restResponseFactory::createProcessDefinitionResponseList);
    }
@Test
    public void testGetProcessDefinitions() throws Exception {

        try {
            Deployment firstDeployment = repositoryService.createDeployment()
                    .name("Deployment 1")
                    .parentDeploymentId("parent1")
                    .addClasspathResource("org/flowable/rest/service/api/repository/oneTaskProcess.bpmn20.xml")
                    .deploy();

            Deployment secondDeployment = repositoryService.createDeployment().name("Deployment 2")
                    .parentDeploymentId("parent2")
                    .addClasspathResource("org/flowable/rest/service/api/repository/oneTaskProcess.bpmn20.xml")
                    .addClasspathResource("org/flowable/rest/service/api/repository/twoTaskProcess.bpmn20.xml").deploy();

            Deployment thirdDeployment = repositoryService.createDeployment().name("Deployment 3").addClasspathResource("org/flowable/rest/service/api/repository/oneTaskProcessWithDi.bpmn20.xml").deploy();

            ProcessDefinition oneTaskProcess = repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").deploymentId(firstDeployment.getId()).singleResult();

            ProcessDefinition latestOneTaskProcess = repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcess").deploymentId(secondDeployment.getId()).singleResult();

            ProcessDefinition twoTaskprocess = repositoryService.createProcessDefinitionQuery().processDefinitionKey("twoTaskProcess").deploymentId(secondDeployment.getId()).singleResult();

            ProcessDefinition oneTaskWithDiProcess = repositoryService.createProcessDefinitionQuery().processDefinitionKey("oneTaskProcessWithDi").deploymentId(thirdDeployment.getId()).singleResult();

            // Test parameterless call
            String baseUrl = RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_COLLECTION);
            assertResultsPresentInDataResponse(baseUrl, oneTaskProcess.getId(), twoTaskprocess.getId(), latestOneTaskProcess.getId(), oneTaskWithDiProcess.getId());

            // Verify ACT-2141 Persistent isGraphicalNotation flag for process definitions
            CloseableHttpResponse response = executeRequest(new HttpGet(SERVER_URL_PREFIX + baseUrl), HttpStatus.SC_OK);
            JsonNode dataNode = objectMapper.readTree(response.getEntity().getContent()).get("data");
            closeResponse(response);
            for (int i = 0; i < dataNode.size(); i++) {
                JsonNode processDefinitionJson = dataNode.get(i);

                String key = processDefinitionJson.get("key").asText();
                JsonNode graphicalNotationNode = processDefinitionJson.get("graphicalNotationDefined");
                if ("oneTaskProcessWithDi".equals(key)) {
                    assertThat(graphicalNotationNode.asBoolean()).isTrue();
                } else {
                    assertThat(graphicalNotationNode.asBoolean()).isFalse();
                }

            }

            // Verify

            // Test name filtering
            String url = baseUrl + "?name=" + encode("The Two Task Process");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test nameLike filtering
            url = baseUrl + "?nameLike=" + encode("The Two%");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test nameLikeIgnoreCase filtering
            url = baseUrl + "?nameLikeIgnoreCase=" + encode("the Two%");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test key filtering
            url = baseUrl + "?key=twoTaskProcess";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test returning multiple versions for the same key
            url = baseUrl + "?key=oneTaskProcess";
            assertResultsPresentInDataResponse(url, oneTaskProcess.getId(), latestOneTaskProcess.getId());

            // Test keyLike filtering
            url = baseUrl + "?keyLike=" + encode("two%");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test category filtering
            url = baseUrl + "?category=TwoTaskCategory";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test categoryLike filtering
            url = baseUrl + "?categoryLike=" + encode("Two%");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test categoryNotEquals filtering
            url = baseUrl + "?categoryNotEquals=OneTaskCategory";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId(), oneTaskWithDiProcess.getId());

            // Test resourceName filtering
            url = baseUrl + "?resourceName=org/flowable/rest/service/api/repository/twoTaskProcess.bpmn20.xml";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test resourceNameLike filtering
            url = baseUrl + "?resourceNameLike=" + encode("%twoTaskProcess%");
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test version filtering
            url = baseUrl + "?version=2";
            assertResultsPresentInDataResponse(url, latestOneTaskProcess.getId());

            // Test latest filtering
            url = baseUrl + "?latest=true";
            assertResultsPresentInDataResponse(url, latestOneTaskProcess.getId(), twoTaskprocess.getId(), oneTaskWithDiProcess.getId());
            url = baseUrl + "?latest=false";
            assertResultsPresentInDataResponse(url, oneTaskProcess.getId(), twoTaskprocess.getId(), latestOneTaskProcess.getId(), oneTaskWithDiProcess.getId());

            // Test deploymentId
            url = baseUrl + "?deploymentId=" + secondDeployment.getId();
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId(), latestOneTaskProcess.getId());

            // Test parentDeploymentId
            url = baseUrl + "?parentDeploymentId=parent2";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId(), latestOneTaskProcess.getId());

            // Test parentDeploymentId
            url = baseUrl + "?parentDeploymentId=parent1";
            assertResultsPresentInDataResponse(url, oneTaskProcess.getId());

            // Test startableByUser
            url = baseUrl + "?startableByUser=kermit";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            // Test suspended
            repositoryService.suspendProcessDefinitionById(twoTaskprocess.getId());

            url = baseUrl + "?suspended=true";
            assertResultsPresentInDataResponse(url, twoTaskprocess.getId());

            url = baseUrl + "?suspended=false";
            assertResultsPresentInDataResponse(url, latestOneTaskProcess.getId(), oneTaskProcess.getId(), oneTaskWithDiProcess.getId());

        } finally {
            // Always cleanup any created deployments, even if the test failed
            List<Deployment> deployments = repositoryService.createDeploymentQuery().list();
            for (Deployment deployment : deployments) {
                repositoryService.deleteDeployment(deployment.getId(), true);
            }
        }
    }