mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-01-27 02:05:17 +00:00
added ability to filter process instances by process initiator
This commit is contained in:
parent
58724ef408
commit
98c3a9a511
10
spiffworkflow-backend/bin/get_routes
Executable file
10
spiffworkflow-backend/bin/get_routes
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function error_handler() {
|
||||
>&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}."
|
||||
exit "$2"
|
||||
}
|
||||
trap 'error_handler ${LINENO} $?' ERR
|
||||
set -o errtrace -o errexit -o nounset -o pipefail
|
||||
|
||||
grep -E '^ +\/' src/spiffworkflow_backend/api.yml | sort
|
@ -1414,11 +1414,34 @@ paths:
|
||||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
|
||||
/users/search:
|
||||
parameters:
|
||||
- name: username_prefix
|
||||
in: query
|
||||
required: true
|
||||
description: The prefix of the user
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
tags:
|
||||
- Users
|
||||
operationId: spiffworkflow_backend.routes.users_controller.user_search
|
||||
summary: Returns a list of users that the search param
|
||||
responses:
|
||||
"200":
|
||||
description: list of users
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/User"
|
||||
|
||||
/user-groups/for-current-user:
|
||||
get:
|
||||
tags:
|
||||
- Process Instances
|
||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.user_group_list_for_current_user
|
||||
- User Groups
|
||||
operationId: spiffworkflow_backend.routes.users_controller.user_group_list_for_current_user
|
||||
summary: Group identifiers for current logged in user
|
||||
responses:
|
||||
"200":
|
||||
|
@ -1,6 +1,8 @@
|
||||
"""User."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import jwt
|
||||
import marshmallow
|
||||
from flask import current_app
|
||||
@ -16,15 +18,18 @@ class UserNotFoundError(Exception):
|
||||
"""UserNotFoundError."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class UserModel(SpiffworkflowBaseDBModel):
|
||||
"""UserModel."""
|
||||
|
||||
__tablename__ = "user"
|
||||
__table_args__ = (db.UniqueConstraint("service", "service_id", name="service_key"),)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(
|
||||
|
||||
id: int = db.Column(db.Integer, primary_key=True)
|
||||
username: str = db.Column(
|
||||
db.String(255), nullable=False, unique=True
|
||||
) # should always be a unique value
|
||||
)
|
||||
|
||||
service = db.Column(
|
||||
db.String(255), nullable=False, unique=False
|
||||
) # not 'openid' -- google, aws
|
||||
|
@ -71,14 +71,6 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R
|
||||
return make_response(jsonify({"results": response_dict}), 200)
|
||||
|
||||
|
||||
def user_group_list_for_current_user() -> flask.wrappers.Response:
|
||||
"""User_group_list_for_current_user."""
|
||||
groups = g.user.groups
|
||||
# TODO: filter out the default group and have a way to know what is the default group
|
||||
group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"]
|
||||
return make_response(jsonify(sorted(group_identifiers)), 200)
|
||||
|
||||
|
||||
def process_list() -> Any:
|
||||
"""Returns a list of all known processes.
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
"""users_controller."""
|
||||
|
||||
import flask
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from flask import make_response
|
||||
from flask import jsonify
|
||||
from flask import g
|
||||
|
||||
|
||||
def user_search(username_prefix: str) -> flask.wrappers.Response:
|
||||
"""User_search."""
|
||||
found_users = UserModel.query.filter(UserModel.username.like(f"{username_prefix}%")).all() # type: ignore
|
||||
|
||||
response_json = {
|
||||
"users": found_users,
|
||||
"username_prefix": username_prefix,
|
||||
}
|
||||
return make_response(jsonify(response_json), 200)
|
||||
|
||||
|
||||
def user_group_list_for_current_user() -> flask.wrappers.Response:
|
||||
"""User_group_list_for_current_user."""
|
||||
groups = g.user.groups
|
||||
# TODO: filter out the default group and have a way to know what is the default group
|
||||
group_identifiers = [i.identifier for i in groups if i.identifier != "everybody"]
|
||||
return make_response(jsonify(sorted(group_identifiers)), 200)
|
@ -0,0 +1,41 @@
|
||||
"""test_users_controller."""
|
||||
|
||||
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
|
||||
from spiffworkflow_backend.models.user import UserModel
|
||||
from flask.testing import FlaskClient
|
||||
from flask.app import Flask
|
||||
|
||||
|
||||
class TestUsersController(BaseTest):
|
||||
"""TestUsersController."""
|
||||
|
||||
def test_user_search_returns_a_user(
|
||||
self,
|
||||
app: Flask,
|
||||
client: FlaskClient,
|
||||
with_db_and_bpmn_file_cleanup: None,
|
||||
with_super_admin_user: UserModel,
|
||||
) -> None:
|
||||
"""Test_user_search_returns_a_user."""
|
||||
self.find_or_create_user(username="aa")
|
||||
self.find_or_create_user(username="ab")
|
||||
self.find_or_create_user(username="abc")
|
||||
self.find_or_create_user(username="ac")
|
||||
|
||||
self._assert_search_has_count(client, with_super_admin_user, 'aa', 1)
|
||||
self._assert_search_has_count(client, with_super_admin_user, 'ab', 2)
|
||||
self._assert_search_has_count(client, with_super_admin_user, 'ac', 1)
|
||||
self._assert_search_has_count(client, with_super_admin_user, 'ad', 0)
|
||||
self._assert_search_has_count(client, with_super_admin_user, 'a', 4)
|
||||
|
||||
def _assert_search_has_count(self, client: FlaskClient, with_super_admin_user: UserModel, username_prefix: str, expected_count: int) -> None:
|
||||
"""_assert_search_has_count."""
|
||||
response = client.get(
|
||||
f"/v1.0/users/search?username_prefix={username_prefix}",
|
||||
headers=self.logged_in_headers(with_super_admin_user),
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json
|
||||
assert response.json['users'] is not None
|
||||
assert response.json['username_prefix'] == username_prefix
|
||||
assert len(response.json['users']) == expected_count
|
@ -1,4 +1,4 @@
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Link,
|
||||
useNavigate,
|
||||
@ -60,6 +60,7 @@ import {
|
||||
ReportColumnForEditing,
|
||||
ReportMetadata,
|
||||
ReportFilter,
|
||||
User,
|
||||
} from '../interfaces';
|
||||
import ProcessModelSearch from './ProcessModelSearch';
|
||||
import ProcessInstanceReportSearch from './ProcessInstanceReportSearch';
|
||||
@ -134,10 +135,14 @@ export default function ProcessInstanceListTable({
|
||||
|
||||
const [errorObject, setErrorObject] = (useContext as any)(ErrorContext);
|
||||
|
||||
const processInstancePathPrefix =
|
||||
const processInstanceListPathPrefix =
|
||||
variant === 'all'
|
||||
? '/admin/process-instances/all'
|
||||
: '/admin/process-instances/for-me';
|
||||
const processInstanceShowPathPrefix =
|
||||
variant === 'all'
|
||||
? '/admin/process-instances'
|
||||
: '/admin/process-instances/for-me';
|
||||
|
||||
const [processStatusAllOptions, setProcessStatusAllOptions] = useState<any[]>(
|
||||
[]
|
||||
@ -164,6 +169,12 @@ export default function ProcessInstanceListTable({
|
||||
useState<ReportColumnForEditing | null>(null);
|
||||
const [reportColumnFormMode, setReportColumnFormMode] = useState<string>('');
|
||||
|
||||
const [processInstanceInitiatorOptions, setProcessInstanceInitiatorOptions] =
|
||||
useState<string[]>([]);
|
||||
const [processInitiatorSelection, setProcessInitiatorSelection] =
|
||||
useState<User | null>(null);
|
||||
const lastRequestedInitatorSearchTerm = useRef<string>();
|
||||
|
||||
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
|
||||
return {
|
||||
start_from: [setStartFromDate, setStartFromTime],
|
||||
@ -529,7 +540,7 @@ export default function ProcessInstanceListTable({
|
||||
|
||||
setErrorObject(null);
|
||||
setProcessInstanceReportJustSaved(null);
|
||||
navigate(`${processInstancePathPrefix}?${queryParamString}`);
|
||||
navigate(`${processInstanceListPathPrefix}?${queryParamString}`);
|
||||
};
|
||||
|
||||
const dateComponent = (
|
||||
@ -628,7 +639,7 @@ export default function ProcessInstanceListTable({
|
||||
|
||||
setErrorObject(null);
|
||||
setProcessInstanceReportJustSaved(mode || null);
|
||||
navigate(`${processInstancePathPrefix}${queryParamString}`);
|
||||
navigate(`${processInstanceListPathPrefix}${queryParamString}`);
|
||||
};
|
||||
|
||||
const reportColumns = () => {
|
||||
@ -976,6 +987,22 @@ export default function ProcessInstanceListTable({
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleProcessInstanceInitiatorSearchResult = (result: any) => {
|
||||
if (lastRequestedInitatorSearchTerm.current === result.username_prefix) {
|
||||
setProcessInstanceInitiatorOptions(result.users);
|
||||
}
|
||||
};
|
||||
|
||||
const searchForProcessInitiator = (inputText: string) => {
|
||||
if (inputText) {
|
||||
lastRequestedInitatorSearchTerm.current = inputText;
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/users/search?username_prefix=${inputText}`,
|
||||
successCallback: handleProcessInstanceInitiatorSearchResult,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const filterOptions = () => {
|
||||
if (!showFilterOptions) {
|
||||
return null;
|
||||
@ -1008,7 +1035,27 @@ export default function ProcessInstanceListTable({
|
||||
selectedItem={processModelSelection}
|
||||
/>
|
||||
</Column>
|
||||
<Column md={8}>{processStatusSearch()}</Column>
|
||||
<Column md={4}>
|
||||
<ComboBox
|
||||
onInputChange={searchForProcessInitiator}
|
||||
onChange={(event: any) => {
|
||||
setProcessInitiatorSelection(event.selectedItem);
|
||||
}}
|
||||
id="process-instance-initiator-search"
|
||||
data-qa="process-instance-initiator-search"
|
||||
items={processInstanceInitiatorOptions}
|
||||
itemToString={(processInstanceInitatorOption: User) => {
|
||||
if (processInstanceInitatorOption) {
|
||||
return processInstanceInitatorOption.username;
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
placeholder="Process Initiator"
|
||||
titleText="PROC"
|
||||
selectedItem={processInitiatorSelection}
|
||||
/>
|
||||
</Column>
|
||||
<Column md={4}>{processStatusSearch()}</Column>
|
||||
</Grid>
|
||||
<Grid fullWidth className="with-bottom-margin">
|
||||
<Column md={4}>
|
||||
@ -1114,7 +1161,7 @@ export default function ProcessInstanceListTable({
|
||||
return (
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`${processInstancePathPrefix}/${modifiedProcessModelId}/${id}`}
|
||||
to={`${processInstanceShowPathPrefix}/${modifiedProcessModelId}/${id}`}
|
||||
title={`View process instance ${id}`}
|
||||
>
|
||||
<span data-qa="paginated-entity-id">{id}</span>
|
||||
|
@ -1,3 +1,8 @@
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface Secret {
|
||||
id: number;
|
||||
key: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user