Skip to content

Commit

Permalink
added flags/values global search (#40)
Browse files Browse the repository at this point in the history
* added flags/values global search

---------

Co-authored-by: d.maximchuk <[email protected]>
  • Loading branch information
briefausde and d.maximchuk authored Sep 17, 2024
1 parent 5e0e34e commit 206dac6
Show file tree
Hide file tree
Showing 15 changed files with 296 additions and 155 deletions.
20 changes: 18 additions & 2 deletions featureflags/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ async def root_flags(ctx: dict, options: dict) -> list:
return []

project_name = options.get("project_name")
flag_name = options.get("flag_name")
expr = select([Flag.id])

if project_name is not None:
Expand All @@ -114,6 +115,9 @@ async def root_flags(ctx: dict, options: dict) -> list:
)
)

if flag_name:
expr = expr.where(Flag.name.ilike(f"%{flag_name}%"))

return await exec_expression(ctx[GraphContext.DB_ENGINE], expr)


Expand Down Expand Up @@ -154,6 +158,7 @@ async def root_values(ctx: dict, options: dict) -> list:
return []

project_name = options.get("project_name")
value_name = options.get("value_name")
expr = select([Value.id])

if project_name is not None:
Expand All @@ -163,6 +168,11 @@ async def root_values(ctx: dict, options: dict) -> list:
)
)

if value_name:
expr = (
expr.where(Value.name.ilike(f"%{value_name}%"))
)

return await exec_expression(ctx[GraphContext.DB_ENGINE], expr)


Expand Down Expand Up @@ -616,7 +626,10 @@ async def get_value_last_action_timestamp(
Sequence["Flag"],
root_flags,
requires=None,
options=[Option("project_name", Optional[String], default=None)],
options=[
Option("project_name", Optional[String], default=None),
Option("flag_name", Optional[String], default=None),
],
),
Link(
"flags_by_ids",
Expand All @@ -637,7 +650,10 @@ async def get_value_last_action_timestamp(
Sequence["Value"],
root_values,
requires=None,
options=[Option("project_name", Optional[String], default=None)],
options=[
Option("project_name", Optional[String], default=None),
Option("value_name", Optional[String], default=None),
],
),
Link(
"values_by_ids",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sqlalchemy as sa

from alembic import op



revision = 'a327a3ea7a5f'
down_revision = '4d42cf3d11de'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_index('flag_name_idx', 'flag', ['name'], unique=False)
op.create_index('value_name_idx', 'value', ['name'], unique=False)
# ### end Alembic commands ###

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index('value_name_idx', table_name='value')
op.drop_index('flag_name_idx', table_name='flag')
# ### end Alembic commands ###
2 changes: 2 additions & 0 deletions featureflags/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class Flag(Base):
__table_args__ = (
UniqueConstraint(project, name),
Index("flag_project_name_idx", project, name),
Index("flag_name_idx", name),
)


Expand Down Expand Up @@ -213,6 +214,7 @@ class Value(Base):
__table_args__ = (
UniqueConstraint(project, name),
Index("value_project_name_idx", project, name),
Index("value_name_idx", name),
)


Expand Down
53 changes: 51 additions & 2 deletions ui/src/Base.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,66 @@
import { useEffect, useRef, useState } from 'react'
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
import { Layout, Typography, Space, Button, Row, Col } from 'antd';
import { Layout, Typography, Space, Button, Row, Col, Input } from 'antd';
const { Header } = Layout;
const { Link } = Typography;

import { Logo } from './components/Logo';
import './Base.less';
import { useAuth, useSignOut } from './hooks';
import { CenteredSpinner } from './components/Spinner';
import { SearchOutlined } from "@ant-design/icons";


function Base({ children }) {
const location = useLocation();
const {auth, loading} = useAuth();
const [signOut] = useSignOut();
const [inputValue, setInputValue] = useState('');

if (!loading && !auth.authenticated && location.pathname !== '/sign-in') {
return <Navigate to='sign-in' replace />
}

const navigate = useNavigate();
const queryParams = new URLSearchParams(location.search);
const tab = queryParams.get('tab') || "flags";

const setTabToUrl = (name) => {
queryParams.set('tab', name);
navigate(`/?${queryParams.toString()}`);
}

const handleSearchTermChange = (e) => {
const value = e.target.value;
setInputValue(value);

if (value === '') {
queryParams.delete('term');
navigate(`/?${queryParams.toString()}`);
}
};

const setSearchTermToUrl = (e) => {
const value = e.target.value;
queryParams.set('term', value);
navigate(`/?${queryParams.toString()}`);
};

const inputRef = useRef(null);

useEffect(() => {
const handleKeyDown = (event) => {
if (event.key === '/') {
event.preventDefault();
inputRef.current.focus();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);

return (
<Layout
style={{
Expand All @@ -47,7 +82,21 @@ function Base({ children }) {

</Space>
</Col>
<Col span={6} offset={14}>
<Col span={4} offset={10}>
{tab && (
<Input
ref={inputRef}
prefix={<SearchOutlined/>}
value={inputValue || queryParams.get('term')}
size="middle"
allowClear
placeholder={`${tab} global search`}
onChange={handleSearchTermChange}
onPressEnter={setSearchTermToUrl}
/>
)}
</Col>
<Col span={6} offset={0}>
<Button
type="link"
onClick={() => setTabToUrl("flags")}
Expand Down
14 changes: 8 additions & 6 deletions ui/src/Dashboard/Check.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import './Check.less';
import { Operators, TYPES, Type } from './constants';
import {
useChecks,
useProject
useProjectsMap,
} from './context';

const { Option } = Select;
Expand All @@ -26,8 +26,9 @@ const defaultInputProps = {
size: "middle"
}

const CheckInput = ({ conditionId, check }) => {
const project = useProject();
const CheckInput = ({ conditionId, check, projectName }) => {
const projectsMap = useProjectsMap();
const project = projectsMap[projectName];
const {
setValueString,
setValueNumber,
Expand Down Expand Up @@ -80,8 +81,9 @@ const CheckInput = ({ conditionId, check }) => {
/>;
}

export const Check = ({ conditionId, check, onDeleteCheck }) => {
const project = useProject();
export const Check = ({ conditionId, check, onDeleteCheck, projectName }) => {
const projectsMap = useProjectsMap();
const project = projectsMap[projectName];
const { setVariable, setOperator } = useChecks();

const onVariableOptionChange = (value) => {
Expand Down Expand Up @@ -129,7 +131,7 @@ export const Check = ({ conditionId, check, onDeleteCheck }) => {
<Option key={id} value={id}>{title}</Option>
))}
</Select>
<CheckInput conditionId={conditionId} check={check}/>
<CheckInput conditionId={conditionId} check={check} projectName={projectName} />
</Space>
</Col>
</Row>
Expand Down
4 changes: 3 additions & 1 deletion ui/src/Dashboard/Conditions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { Check } from './Check';


const Condition = ({ onRemove, condition }) => {
const Condition = ({ onRemove, condition, projectName }) => {
const { addCheck, deleteCheck } = useFlagCtx();
const { checks } = useChecks();

Expand All @@ -32,6 +32,7 @@ const Condition = ({ onRemove, condition }) => {
conditionId={condition.id}
check={checks[checkId]}
onDeleteCheck={() => deleteCheck(condition.id, checkId)}
projectName={projectName}
/>
)
})}
Expand Down Expand Up @@ -69,6 +70,7 @@ export const Conditions = () => {
key={conditionId}
condition={conditions[conditionId]}
onRemove={() => deleteCondition(conditions[conditionId])}
projectName={flag.projectName}
/>
)
})}
Expand Down
13 changes: 7 additions & 6 deletions ui/src/Dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ function Dashboard({ projects }) {
const queryParams = new URLSearchParams(location.search);
const projectFromQuery = queryParams.get('project');
const tab = queryParams.get('tab');
const searchTerm = queryParams.get('term');

const [menuItems, setMenuItems] = useState([]);
const [selected, setSelected] = useState('');
const [searchOptions, setSearchOptions] = useState([]);
const [projectMap, setProjectMap] = useState({});
const [projectsMap, setProjectsMap] = useState({});

const setProjectToUrl = (project) => {
queryParams.set('project', project);
Expand All @@ -80,7 +81,7 @@ function Dashboard({ projects }) {
return acc;
}, {});

setProjectMap(_projectsMap);
setProjectsMap(_projectsMap);
}, [projects]);

const onSearch = (searchText) => {
Expand Down Expand Up @@ -152,14 +153,14 @@ function Dashboard({ projects }) {
>
{
tab === "values" ? (
!!selected ? (
<ValuesContainer project={projectMap[selected]} />
!!selected || !!searchTerm ? (
<ValuesContainer projectName={!!selected ? projectsMap[selected].name : null} searchTerm={searchTerm} projectsMap={projectsMap} />
) : (
<div>No Values</div>
)
) : (
!!selected ? (
<FlagsContainer project={projectMap[selected]} />
!!selected || !!searchTerm ? (
<FlagsContainer projectName={!!selected ? projectsMap[selected].name : null} searchTerm={searchTerm} projectsMap={projectsMap} />
) : (
<div>No Flags</div>
)
Expand Down
Loading

0 comments on commit 206dac6

Please sign in to comment.