Skip to content

Commit

Permalink
Add tags and tickets fields to get cases request (#5564)
Browse files Browse the repository at this point in the history
* Add tags and tickets fields to get cases request

* Attempt to move include parameter in GET requests to Cases into database service.

* Adding tests

* Adding tests

* Add tags and ticket fields back to CaseReadMinimal

* Switch test from Incident to Case.

* remove duplicates

* Fix unused variable assignment

* Fixing tests

---------

Co-authored-by: kevgliss <[email protected]>
Co-authored-by: Kevin Glisson <[email protected]>
  • Loading branch information
3 people authored Dec 12, 2024
1 parent 1d152df commit ad45c42
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 35 deletions.
20 changes: 1 addition & 19 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,11 @@
"files.trimFinalNewlines": true,
"files.insertFinalNewline": true,
"vetur.format.enable": false,
"python.formatting.provider": "black",
"python.formatting.blackArgs": [
"--line-length",
"100"
],
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--ignore=E24,W504,E501",
"--verbose"
],
"python.testing.pytestEnabled": true,
"python.sortImports.args": [
"--settings-path",
"${workspaceFolder}/setup.cfg"
],
"python.linting.pylintArgs": [
"--rcfile",
"${workspaceFolder}/setup.cfg"
],
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "never"
}
},
"codeQL.githubDatabase.update": "never",
}
2 changes: 2 additions & 0 deletions src/dispatch/case/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ class CaseReadMinimal(CaseBase):
project: ProjectRead
reporter: Optional[ParticipantReadMinimal]
reported_at: Optional[datetime] = None
tags: Optional[List[TagRead]] = []
ticket: Optional[TicketRead] = None
total_cost: float | None
triage_at: Optional[datetime] = None

Expand Down
1 change: 1 addition & 0 deletions src/dispatch/case/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def get_cases(
expand: bool = Query(default=False),
):
"""Retrieves all cases."""
common["include_keys"] = include
pagination = search_filter_sort_paginate(model="Case", **common)

if expand:
Expand Down
33 changes: 23 additions & 10 deletions src/dispatch/database/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,7 @@ def apply_filters(query, filter_spec, model_cls=None, do_auto_join=True):
return query


def apply_filter_specific_joins(model: Base, filter_spec: dict, query: orm.query):
"""Applies any model specific implicitly joins."""
def get_model_map(filters: dict) -> dict:
# this is required because by default sqlalchemy-filter's auto-join
# knows nothing about how to join many-many relationships.
model_map = {
Expand Down Expand Up @@ -371,19 +370,21 @@ def apply_filter_specific_joins(model: Base, filter_spec: dict, query: orm.query
(SignalInstance, "EntityType"): (SignalInstance.entities, True),
(Tag, "TagType"): (Tag.tag_type, False),
}
filters = build_filters(filter_spec)

# Replace mapping if looking for commander
if "Commander" in str(filter_spec):
if "Commander" in filters:
model_map.update({(Incident, "IndividualContact"): (Incident.commander, True)})
if "Assignee" in str(filter_spec):
if "Assignee" in filters:
model_map.update({(Case, "IndividualContact"): (Case.assignee, True)})
return model_map

filter_models = get_named_models(filters)

def apply_model_specific_joins(model: Base, models: List[str], query: orm.query):
model_map = get_model_map(models)
joined_models = []
for filter_model in filter_models:
if model_map.get((model, filter_model)):
joined_model, is_outer = model_map[(model, filter_model)]

for include_model in models:
if model_map.get((model, include_model)):
joined_model, is_outer = model_map[(model, include_model)]
try:
if joined_model not in joined_models:
query = query.join(joined_model, isouter=is_outer)
Expand All @@ -394,6 +395,14 @@ def apply_filter_specific_joins(model: Base, filter_spec: dict, query: orm.query
return query


def apply_filter_specific_joins(model: Base, filter_spec: dict, query: orm.query):
"""Applies any model specific implicitly joins."""
filters = build_filters(filter_spec)
filter_models = get_named_models(filters)

return apply_model_specific_joins(model, filter_models, query)


def composite_search(*, db_session, query_str: str, models: List[Base], current_user: DispatchUser):
"""Perform a multi-table search based on the supplied query."""
s = CompositeSearch(db_session, models)
Expand Down Expand Up @@ -537,6 +546,7 @@ def search_filter_sort_paginate(
model,
query_str: str = None,
filter_spec: str | dict | None = None,
include_keys: List[str] = None,
page: int = 1,
items_per_page: int = 5,
sort_by: List[str] = None,
Expand Down Expand Up @@ -574,6 +584,9 @@ def search_filter_sort_paginate(
else:
query = apply_filters(query, filter_spec, model_cls)

if include_keys:
query = apply_model_specific_joins(model_cls, include_keys, query)

if model == "Incident":
query = query.intersect(query_restricted)
for filter in tag_all_filters:
Expand Down
42 changes: 37 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from sqlalchemy_utils import drop_database, database_exists
from starlette.config import environ
from starlette.testclient import TestClient
from fastapi.testclient import TestClient

# set test config
environ["DATABASE_CREDENTIALS"] = "postgres:dispatch"
Expand All @@ -19,6 +19,7 @@
from dispatch import config
from dispatch.database.core import engine
from dispatch.database.manage import init_database
from dispatch.enums import Visibility, UserRoles

from .database import Session
from .factories import (
Expand All @@ -32,6 +33,7 @@
ConversationFactory,
DefinitionFactory,
DispatchUserFactory,
DispatchUserOrganizationFactory,
DocumentFactory,
EmailTemplateFactory,
EntityFactory,
Expand Down Expand Up @@ -98,13 +100,13 @@ def pytest_runtest_makereport(item, call):
@pytest.fixture(scope="session")
def testapp():
# we only want to use test plugins so unregister everybody else
from dispatch.main import app
from dispatch.main import api
from dispatch.plugins.base import plugins, unregister

for p in plugins.all():
unregister(p)
unregister(p.__class__)

yield app
yield api


@pytest.fixture(scope="session")
Expand Down Expand Up @@ -137,7 +139,7 @@ def session(db):


@pytest.fixture(scope="function")
def client(testapp, session, client):
def client(testapp, session):
yield TestClient(testapp)


Expand Down Expand Up @@ -272,6 +274,18 @@ def user(session):
return DispatchUserFactory()


@pytest.fixture
def admin_user(session):
# we need to create a new user with the admin role
user = DispatchUserFactory()
organization = OrganizationFactory()
DispatchUserOrganizationFactory(
dispatch_user=user, organization=organization, role=UserRoles.admin
)

return user


@pytest.fixture
def tag(session):
return TagFactory()
Expand Down Expand Up @@ -532,6 +546,24 @@ def incident(session):
return IncidentFactory()


@pytest.fixture()
def incidents(session):
return [
IncidentFactory(
title="Test Incident 1",
description="Description 1",
visibility=Visibility.open,
tags=[TagFactory()],
),
IncidentFactory(
title="Test Incident 2", description="Description 2", visibility=Visibility.restricted
),
IncidentFactory(
title="Another Incident", description="Description 3", visibility=Visibility.open
),
]


@pytest.fixture
def participant_activity(session):
return ParticipantActivityFactory()
Expand Down
Loading

0 comments on commit ad45c42

Please sign in to comment.