Skip to content

Commit

Permalink
Merge pull request #4726 from NCI-Agency/AB-1073-add-search-filter-on…
Browse files Browse the repository at this point in the history
…-person-position-type

Add advanced search filter on people's current position type
  • Loading branch information
gjvoosten authored Apr 17, 2024
2 parents 5674031 + 9ea9fd4 commit b81af8e
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 32 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ dependencies {
testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.9'

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2'

// Avoid "ERROR StatusLogger Log4j2 could not find a logging implementation.
Expand Down
27 changes: 20 additions & 7 deletions client/src/components/SearchFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ import LOCATIONS_ICON from "resources/locations.png"
import PEOPLE_ICON from "resources/people.png"
import POSITIONS_ICON from "resources/positions.png"
import TASKS_ICON from "resources/tasks.png"
import {
POSITION_POSITION_TYPE_FILTER_KEY,
RECURSE_STRATEGY
} from "searchUtils"
import { RECURSE_STRATEGY } from "searchUtils"
import Settings from "settings"

export const SearchQueryPropType = PropTypes.shape({
Expand Down Expand Up @@ -407,6 +404,23 @@ export const searchFilters = function(includeAdminFilters) {
labels: ["Yes", "No"]
}
},
"Holding Position As": {
component: SelectFilter,
deserializer: deserializeSelectFilter,
props: {
queryKey: "positionType",
options: [
Position.TYPE.REGULAR,
Position.TYPE.SUPERUSER,
Position.TYPE.ADMINISTRATOR
],
labels: [
Settings.fields.regular.position.name,
Settings.fields.superuser.position.name,
Settings.fields.administrator.position.name
]
}
},
"Pending Verification": {
component: RadioButtonFilter,
deserializer: deserializeSelectFilter,
Expand Down Expand Up @@ -458,7 +472,7 @@ export const searchFilters = function(includeAdminFilters) {

filters[SEARCH_OBJECT_TYPES.POSITIONS] = {
filters: {
[POSITION_POSITION_TYPE_FILTER_KEY]: {
Type: {
component: SelectFilter,
dictProps: Settings.fields.position.type,
deserializer: deserializeSelectFilter,
Expand All @@ -473,8 +487,7 @@ export const searchFilters = function(includeAdminFilters) {
Settings.fields.regular.position.name,
Settings.fields.superuser.position.name,
Settings.fields.administrator.position.name
],
isPositionTypeFilter: true
]
}
},
"Within Organization": {
Expand Down
4 changes: 1 addition & 3 deletions client/src/components/advancedSearch/SelectFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const SelectFilter = ({
queryKey,
value: inputValue,
onChange,
isPositionTypeFilter,
options,
labels
}) => {
Expand Down Expand Up @@ -57,8 +56,7 @@ SelectFilter.propTypes = {
})
]),
onChange: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
asFormField: PropTypes.bool,
isPositionTypeFilter: PropTypes.bool
asFormField: PropTypes.bool
}
SelectFilter.defaultProps = {
asFormField: true
Expand Down
2 changes: 0 additions & 2 deletions client/src/searchUtils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const POSITION_POSITION_TYPE_FILTER_KEY = "Position Type"

export const RECURSE_STRATEGY = {
NONE: "NONE",
CHILDREN: "CHILDREN",
Expand Down
40 changes: 21 additions & 19 deletions client/tests/webdriver/baseSpecs/advancedSearch.spec.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import { expect } from "chai"
import _isEmpty from "lodash/isEmpty"
import AdvancedSearch from "../pages/advancedSearch"
import Home from "../pages/home.page"

const ANET_OBJECT_TYPES = {
Reports: {
sampleFilter: "Author"
sampleFilters: ["Author"]
},
People: {
sampleFilter: "Organization"
sampleFilters: ["Within Organization", "Holding Position As"]
},
Organizations: {
sampleFilter: "Within Organization"
sampleFilters: ["Within Organization"]
},
Positions: {
sampleFilter: "Type"
sampleFilters: ["Type"]
},
Locations: {
sampleFilter: "Type"
sampleFilters: ["Type"]
},
"Objective / Efforts": {
sampleFilter: "Organization"
sampleFilters: ["Within Organization"]
},
"Authorization Groups": {
sampleFilter: undefined
sampleFilters: []
}
}
const COMMON_FILTER_TEXT = "Status"
const ALL_COMMON_FILTERS = [COMMON_FILTER_TEXT, "Subscribed"]
const ALL_COMMON_FILTERS = [COMMON_FILTER_TEXT, "Subscribed", "With Email"]

const PERSON_DEFAULT_FILTER = "Pending Verification"
const PERSON_INDEX = 1
Expand Down Expand Up @@ -107,9 +108,9 @@ describe("When using advanced search", () => {
const buttons = await AdvancedSearch.getAnetObjectSearchToggleButtons()
for (const [i, button] of buttons.entries()) {
await button.click()
const sampleFilter =
ANET_OBJECT_TYPES[await getObjectType(i)].sampleFilter
if (sampleFilter) {
const sampleFilters =
ANET_OBJECT_TYPES[await getObjectType(i)].sampleFilters
if (!_isEmpty(sampleFilters)) {
await (await AdvancedSearch.getAddFilterButtonText()).waitForExist()
await (await AdvancedSearch.getAddFilterButtonText()).waitForDisplayed()
expect(
Expand All @@ -123,17 +124,18 @@ describe("When using advanced search", () => {
const buttons = await AdvancedSearch.getAnetObjectSearchToggleButtons()
for (const [i, button] of buttons.entries()) {
await button.click()
const sampleFilter =
ANET_OBJECT_TYPES[await getObjectType(i)].sampleFilter
if (sampleFilter) {
await (await AdvancedSearch.getAddFilterButtonText()).waitForExist()
await (await AdvancedSearch.getAddFilterButtonText()).waitForDisplayed()
const sampleFilters =
ANET_OBJECT_TYPES[await getObjectType(i)].sampleFilters
if (!_isEmpty(sampleFilters)) {
await (await AdvancedSearch.getAddFilterButton()).click()
await (await AdvancedSearch.getAddFilterPopover()).waitForExist()
await (await AdvancedSearch.getAddFilterPopover()).waitForDisplayed()
expect(
await (await AdvancedSearch.getAddFilterPopover()).getText()
).to.match(new RegExp(sampleFilter))
for (const sampleFilter of sampleFilters) {
expect(
await (await AdvancedSearch.getAddFilterPopover()).getText()
).to.match(new RegExp(sampleFilter))
await (await AdvancedSearch.getSearchFilter(sampleFilter)).click()
}
}
}
})
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/mil/dds/anet/beans/search/PersonSearchQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.leangen.graphql.annotations.GraphQLInputField;
import io.leangen.graphql.annotations.GraphQLQuery;
import java.time.Instant;
import java.util.List;
import mil.dds.anet.beans.Position.PositionType;

public class PersonSearchQuery extends SubscribableObjectSearchQuery<PersonSearchSortBy> {

Expand Down Expand Up @@ -45,6 +47,11 @@ public class PersonSearchQuery extends SubscribableObjectSearchQuery<PersonSearc
@GraphQLInputField
Boolean hasBiography;

// Find people whose current position is of the given type
@GraphQLQuery
@GraphQLInputField
List<PositionType> positionType;

public PersonSearchQuery() {
super(PersonSearchSortBy.NAME);
this.setPageSize(100);
Expand Down Expand Up @@ -131,6 +138,14 @@ public void setHasBiography(Boolean hasBiography) {
this.hasBiography = hasBiography;
}

public List<PositionType> getPositionType() {
return positionType;
}

public void setPositionType(List<PositionType> positionType) {
this.positionType = positionType;
}

@Override
public PersonSearchQuery clone() throws CloneNotSupportedException {
return (PersonSearchQuery) super.clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import mil.dds.anet.database.mappers.PersonMapper;
import mil.dds.anet.search.AbstractSearchQueryBuilder.Comparison;
import mil.dds.anet.utils.DaoUtils;
import mil.dds.anet.utils.Utils;
import ru.vyarus.guicey.jdbi3.tx.InTransaction;

public abstract class AbstractPersonSearcher extends AbstractSearcher<Person, PersonSearchQuery>
Expand Down Expand Up @@ -49,7 +50,7 @@ protected void buildQuery(Set<String> subFields, PersonSearchQuery query) {
qb.addFromClause("people");

if (query.getOrgUuid() != null || query.getLocationUuid() != null
|| query.getMatchPositionName()) {
|| query.getMatchPositionName() || !Utils.isEmptyOrNull(query.getPositionType())) {
qb.addFromClause("LEFT JOIN positions ON people.uuid = positions.\"currentPersonUuid\"");
}

Expand Down Expand Up @@ -92,6 +93,8 @@ protected void buildQuery(Set<String> subFields, PersonSearchQuery query) {
}
}

qb.addInClause("types", "positions.type", query.getPositionType());

if (Boolean.TRUE.equals(query.isInMyReports())) {
qb.addSelectClause("\"inMyReports\".max AS \"inMyReports_max\"");
qb.addFromClause("JOIN ("
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/mil/dds/anet/test/resources/PersonResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import mil.dds.anet.test.utils.UtilsTest;
import mil.dds.anet.utils.DaoUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

public class PersonResourceTest extends AbstractResourceTest {

Expand Down Expand Up @@ -317,6 +319,19 @@ void searchPerson() {
assertThat(searchResults.getList()).isNotEmpty();
}

@ParameterizedTest
@EnumSource(value = PositionType.class, names = {"_PLACEHOLDER_1_"},
mode = EnumSource.Mode.EXCLUDE)
void searchUsersByPositionType(PositionType positionType) {
final PersonSearchQueryInput query = PersonSearchQueryInput.builder().withPageSize(0)
.withPositionType(List.of(positionType)).build();
final AnetBeanList_Person people =
withCredentials(adminUser, t -> queryExecutor.personList(getListFields(FIELDS), query));
assertThat(people).isNotNull();
assertThat(people.getList()).isNotEmpty()
.allMatch(p -> p.getPosition().getType() == positionType);
}

@Test
void testInactivatePerson() {
final OrganizationSearchQueryInput query =
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/anet.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ input PersonSearchQueryInput {
pageNum: Int
pageSize: Int
pendingVerification: Boolean
positionType: [PositionType]
rank: String
sortBy: PersonSearchSortBy
sortOrder: SortOrder
Expand Down

0 comments on commit b81af8e

Please sign in to comment.