Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: exports via main button don't include orphans (#287) #288

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/providers/fileManifestState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type FileManifestState = {
isSummaryLoading: boolean;
requestParams?: URLSearchParams;
requestURL?: string;
setOfFormFacetNames: Set<FileFacet["name"]>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about renaming this to help indicate these are the selected set of form facet names (e.g. selectedFormFacetNames)?

summary?: AzulSummaryResponse;
};

Expand Down Expand Up @@ -283,6 +284,7 @@ function fileManifestReducer(
isEnabled: false,
requestParams: undefined,
requestURL: undefined,
setOfFormFacetNames: new Set(),
};
}
// Fetches file manifest.
Expand Down Expand Up @@ -336,6 +338,8 @@ function fileManifestReducer(
filters,
state.fileSummaryFacetName
);
// Update set of form facet names.
state.setOfFormFacetNames.add(payload.categoryKey);
return {
...state,
fileSummaryFilters,
Expand All @@ -358,6 +362,8 @@ function fileManifestReducer(
filters,
state.fileSummaryFacetName
);
// Update set of form facet names.
state.setOfFormFacetNames.add(payload);
return {
...state,
fileSummaryFilters,
Expand Down
12 changes: 2 additions & 10 deletions src/providers/fileManifestState/constants.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import {
FILE_MANIFEST_TYPE,
FILES_FACETS_STATUS,
} from "../../hooks/useFileManifest/common/entities";
import { FILES_FACETS_STATUS } from "../../hooks/useFileManifest/common/entities";
import { FileManifestState } from "../fileManifestState";

export const ENTITIES_FILE_MANIFEST_TYPES: FILE_MANIFEST_TYPE[] = [
FILE_MANIFEST_TYPE.BULK_DOWNLOAD,
FILE_MANIFEST_TYPE.DOWNLOAD_MANIFEST,
FILE_MANIFEST_TYPE.EXPORT_TO_TERRA,
];

export const FILE_MANIFEST_STATE: FileManifestState = {
fileManifestFormat: undefined,
fileManifestType: undefined,
Expand All @@ -27,5 +18,6 @@ export const FILE_MANIFEST_STATE: FileManifestState = {
isSummaryLoading: false,
requestParams: undefined,
requestURL: undefined,
setOfFormFacetNames: new Set(),
summary: undefined,
};
77 changes: 35 additions & 42 deletions src/providers/fileManifestState/utils.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,59 @@
import { Filters } from "../../common/entities";
import {
FileFacet,
FILES_FACETS_STATUS,
} from "../../hooks/useFileManifest/common/entities";
import { FILES_FACETS_STATUS } from "../../hooks/useFileManifest/common/entities";
import { findFacet } from "../../hooks/useFileManifest/common/utils";
import {
FileManifestState,
UpdateFileManifestPayload,
} from "../fileManifestState";
import { ENTITIES_FILE_MANIFEST_TYPES } from "./constants";

/**
* Generates the filters for a request URL based on the file manifest state.
* If all terms in a facet are selected, the corresponding facet is excluded.
* Determines if all form facet terms are fully selected.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be able to add comments here about the "no values selected" case? (It took me a while to get that the no values case was considered fully selected.) Let me know if this isn't clear!

* @param state - File manifest state.
* @returns filters for the request URL.
* @returns true if all form facet terms are fully selected.
*/
export function getRequestFilters(
state: FileManifestState
): Filters | undefined {
if (state.filesFacetsStatus !== FILES_FACETS_STATUS.COMPLETED) return;
// Determine if the filters are user-selected.
if (isFiltersUserSelected(state)) {
return state.filters;
}
for (const filter of state.filters) {
const facet = findFacet(state.filesFacets, filter.categoryKey);
if (!facet) return [filter]; // The entity identifier related filter will not have a corresponding term facet.
export function areAllFormFiltersSelected(state: FileManifestState): boolean {
const { filesFacets, filters, setOfFormFacetNames } = state;
for (const { categoryKey, value } of filters) {
if (setOfFormFacetNames.has(categoryKey)) {
const facet = findFacet(filesFacets, categoryKey);
if (!facet) continue;
if (value.length < facet.termCount) return false;
}
}
return true;
}

/**
* Returns true if filter values for each selected facet are partially selected.
* @param filters - Selected filters.
* @param filesFacets - Files facets.
* @returns true if the filters are partially selected.
* Filters out fully selected form facets from the filters.
* @param state - File manifest state.
* @returns filters.
*/
function isFiltersPartiallySelected(
filters: Filters,
filesFacets: FileFacet[]
): boolean {
for (const { categoryKey, value } of filters) {
const facet = findFacet(filesFacets, categoryKey);
if (!facet) continue; // Continue; the entity identifier related filter will not have a corresponding term facet.
if (value.length < facet.termCount) return true;
export function excludeFullySelectedFormFilters(
state: FileManifestState
): Filters {
const filters: Filters = [];
for (const filter of state.filters) {
if (state.setOfFormFacetNames.has(filter.categoryKey)) continue;
filters.push(filter);
}
return false;
return filters;
}

/**
* Returns true if the filters are user-selected, when:
* - The file manifest type is not set.
* - The file manifest type is an entity list related type.
* - Filter values for each selected facet are partially selected.
* Generates the filters for a request URL based on the file manifest state.
* If all terms in a facet are selected, the corresponding facet is excluded.
* @param state - File manifest state.
* @returns true if the filters are user-selected.
* @returns filters for the request URL.
*/
export function isFiltersUserSelected(state: FileManifestState): boolean {
if (!state.fileManifestType) return true;
if (ENTITIES_FILE_MANIFEST_TYPES.includes(state.fileManifestType))
return true;
return isFiltersPartiallySelected(state.filters, state.filesFacets);
export function getRequestFilters(
state: FileManifestState
): Filters | undefined {
if (state.filesFacetsStatus !== FILES_FACETS_STATUS.COMPLETED) return;
if (state.setOfFormFacetNames.size === 0) return;
// Form terms are partially selected; return filters.
if (!areAllFormFiltersSelected(state)) return state.filters;
// Form terms are fully selected; return filters excluding form filters.
return excludeFullySelectedFormFilters(state);
}

/**
Expand Down
120 changes: 45 additions & 75 deletions tests/fileManifestRequestFilters.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Filters, SelectedFilter } from "../src/common/entities";
import {
FILE_MANIFEST_TYPE,
FileFacet,
FILES_FACETS_STATUS,
} from "../src/hooks/useFileManifest/common/entities";
Expand All @@ -24,36 +23,39 @@ const FILE_MANIFEST_STATE_COMPLETED: FileManifestState = {
};

const FILES_FACETS: Partial<FileFacet>[] = [
{
name: "category01",
termCount: 2,
},
{
name: "category02",
termCount: 3,
},
{ name: "category01", termCount: 2 },
{ name: "category02", termCount: 3 },
{ name: "category03", termCount: 1 },
];

const FILTER_IDENTIFIER: SelectedFilter = {
categoryKey: "identifier",
const SELECTED_FILTER_ENTITY_LIST: SelectedFilter = {
categoryKey: "category03",
value: ["value05"],
};

const FILTERS_COMPLETE_SET: Filters = [
const SELECTED_FILTERS: Filters = [SELECTED_FILTER_ENTITY_LIST]; // Filters pre-selected i.e. entity list selected filters, or entity ID filter.

const SELECTED_FILTERS_FORM_COMPLETE_SET: Filters = [
{ categoryKey: "category01", value: ["value01", "value02"] },
{ categoryKey: "category02", value: ["value02", "value03", "value04"] },
];

const FILTERS_SUBSET: Filters = [
const SELECTED_FILTERS_FORM_SUBSET: Filters = [
{ categoryKey: "category01", value: ["value01"] },
{ categoryKey: "category02", value: ["value02", "value03", "value04"] },
];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a third test case here where one of the categories has no selected values, so that the "no values equals all values" case is exercised?


const FILE_MANIFEST_STATE_USER_SELECT_FILTERS: FileManifestState = {
...FILE_MANIFEST_STATE,
filesFacetsStatus: FILES_FACETS_STATUS.COMPLETED,
filters: FILTERS_SUBSET,
};
const FILTERS_COMPLETE_SET: Filters = [
...SELECTED_FILTERS,
...SELECTED_FILTERS_FORM_COMPLETE_SET,
];

const FILTERS_SUBSET: Filters = [
...SELECTED_FILTERS,
...SELECTED_FILTERS_FORM_SUBSET,
];

const FORM_FACET_NAMES: FileFacet["name"][] = ["category01", "category02"];

describe("fileManifestRequestFilters", () => {
describe("when filters and facets are undefined or empty", () => {
Expand All @@ -80,81 +82,49 @@ describe("fileManifestRequestFilters", () => {
});

test("returns request filters when status is COMPLETED", () => {
expect(FILE_MANIFEST_STATE_COMPLETED.fileManifestType).toBeUndefined();
expect(getRequestFilters(FILE_MANIFEST_STATE_COMPLETED)).toEqual([]);
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_COMPLETED,
setOfFormFacetNames: new Set(FORM_FACET_NAMES),
})
).toEqual(FILE_MANIFEST_STATE_COMPLETED.filters);
});
});

describe("when filters are user-selected", () => {
describe("and fileManifestType is undefined", () => {
test("returns user-selected filters", () => {
expect(
getRequestFilters(FILE_MANIFEST_STATE_USER_SELECT_FILTERS)
).toEqual(FILTERS_SUBSET);
});
});

describe("and fileManifestType is ENTITY LIST", () => {
test("returns user-selected filters for BULK_DOWNLOAD", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.BULK_DOWNLOAD,
})
).toEqual(FILTERS_SUBSET);
});

test("returns user-selected filters for DOWNLOAD_MANIFEST", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.DOWNLOAD_MANIFEST,
})
).toEqual(FILTERS_SUBSET);
});

test("returns user-selected filters for EXPORT_TO_TERRA", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.EXPORT_TO_TERRA,
})
).toEqual(FILTERS_SUBSET);
});
describe("when form terms are not-selected", () => {
test("returns undefined", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_COMPLETED,
filesFacets: FILES_FACETS as FileFacet[],
})
).toBeUndefined();
});
});

test("returns user-selected filters that are a subset of facet terms", () => {
describe("when form terms are partially-selected", () => {
test("returns complete set of user-selected filters", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.ENTITY_BULK_DOWNLOAD, // FILE_MANIFEST_TYPE is NOT ENTITY LIST.
...FILE_MANIFEST_STATE_COMPLETED,
filesFacets: FILES_FACETS as FileFacet[],
filters: FILTERS_SUBSET,
setOfFormFacetNames: new Set(FORM_FACET_NAMES),
})
).toEqual(FILTERS_SUBSET);
});
});

describe("when filters are NOT user-selected", () => {
test("returns undefined when all terms are included, and identifier filter is not defined", () => {
describe("when form terms are fully-selected", () => {
test("returns a subset of user-selected filters", () => {
expect(
getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.ENTITY_BULK_DOWNLOAD, // FILE_MANIFEST_TYPE is NOT ENTITY LIST.
...FILE_MANIFEST_STATE_COMPLETED,
filesFacets: FILES_FACETS as FileFacet[],
filters: FILTERS_COMPLETE_SET,
setOfFormFacetNames: new Set(FORM_FACET_NAMES),
})
).toBeUndefined();
});

test("returns identifier filter when all terms are included, and identifier filter is defined", () => {
const filters = getRequestFilters({
...FILE_MANIFEST_STATE_USER_SELECT_FILTERS,
fileManifestType: FILE_MANIFEST_TYPE.ENTITY_BULK_DOWNLOAD, // FILE_MANIFEST_TYPE is NOT ENTITY LIST.
filesFacets: FILES_FACETS as FileFacet[],
filters: [...FILTERS_COMPLETE_SET, FILTER_IDENTIFIER],
});
expect(filters?.length).toBe(1);
expect(filters?.[0].categoryKey).toBe(FILTER_IDENTIFIER.categoryKey);
).toEqual(SELECTED_FILTERS);
});
});
});
Loading