Skip to content

Commit

Permalink
feat(hub-common): hubSearchEvents switch to new POST events search route
Browse files Browse the repository at this point in the history
affects: @esri/hub-common
  • Loading branch information
velveetachef committed Jan 30, 2025
1 parent eab454b commit fa81971
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { unique } from "../../../util";
import { IFilter } from "../../types/IHubCatalog";

export const getUniquePredicateValuesByKey = <T>(
filters: IFilter[],
predicateKey: string
): T[] => {
const toPredicateValuesByKey = (a1: T[], filter: IFilter): T[] =>
filter.predicates.reduce<T[]>(
(a2, predicate) =>
Object.entries(predicate).reduce(
(a3, [key, val]) =>
key === predicateKey
? [...a3, ...(Array.isArray(val) ? val : [val])]
: a3,
a2
),
a1
);
return filters.reduce<T[]>(toPredicateValuesByKey, []).filter(unique);
};
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
import { IFilter } from "../../types/IHubCatalog";
import {
EventAccess,
EventAssociationEntityType,
EventAttendanceType,
EventStatus,
GetEventsParams,
ISearchEvents,
} from "../../../events/api/orval/api/orval-events";
import { getOptionalPredicateStringsByKey } from "./getOptionalPredicateStringsByKey";
import { getPredicateValuesByKey } from "./getPredicateValuesByKey";
import { getUniquePredicateValuesByKey } from "./getUniquePredicateValuesByKey";
import { IDateRange } from "../../types/types";
import { searchGroups } from "@esri/arcgis-rest-portal";
import { IHubRequestOptions } from "../../../types";
import { isUpdateGroup } from "../../../utils/is-update-group";

/**
* Builds a Partial<GetEventsParams> given an Array of IFilter objects
* Builds a Partial<ISearchEvents> given an Array of IFilter objects
* @param filters An Array of IFilter
* @returns a Partial<GetEventsParams> for the given Array of IFilter objects
* @returns a Partial<ISearchEvents> for the given Array of IFilter objects
*/
export async function processFilters(
filters: IFilter[],
requestOptions: IHubRequestOptions
): Promise<Partial<GetEventsParams>> {
const processedFilters: Partial<GetEventsParams> = {};
const access = getOptionalPredicateStringsByKey(filters, "access");
): Promise<Partial<ISearchEvents>> {
const processedFilters: Partial<ISearchEvents> = {};
// todo: <enums or strings?> (API is upper casing each value in the array, but throws off the typing here)
const access = getUniquePredicateValuesByKey<EventAccess>(filters, "access");
if (access?.length) {
processedFilters.access = access;
}
const canEdit = getPredicateValuesByKey<boolean>(filters, "canEdit");
if (canEdit.length) {
processedFilters.canEdit = canEdit[0].toString();
processedFilters.canEdit = canEdit[0];
}
const entityIds = getOptionalPredicateStringsByKey(filters, "entityId");
const entityIds = getUniquePredicateValuesByKey<string>(filters, "entityId");
if (entityIds?.length) {
processedFilters.entityIds = entityIds;
}
const entityTypes = getOptionalPredicateStringsByKey(filters, "entityType");
// todo: <enums or strings?> (NO API conversion here)
const entityTypes = getUniquePredicateValuesByKey<EventAssociationEntityType>(
filters,
"entityType"
);
if (entityTypes?.length) {
processedFilters.entityTypes = entityTypes;
}
const eventIds = getOptionalPredicateStringsByKey(filters, "id");
const eventIds = getUniquePredicateValuesByKey<string>(filters, "id");
if (eventIds?.length) {
processedFilters.eventIds = eventIds;
}
Expand All @@ -48,30 +56,33 @@ export async function processFilters(
if (orgId.length) {
processedFilters.orgId = orgId[0];
}
const categories = getOptionalPredicateStringsByKey(filters, "categories");
const categories = getUniquePredicateValuesByKey<string>(
filters,
"categories"
);
if (categories?.length) {
processedFilters.categories = categories;
}
const tags = getOptionalPredicateStringsByKey(filters, "tags");
const tags = getUniquePredicateValuesByKey<string>(filters, "tags");
if (tags?.length) {
processedFilters.tags = tags;
}
const groupIds = getOptionalPredicateStringsByKey(filters, "group");
const groupIds = getUniquePredicateValuesByKey<string>(filters, "group");
// if a group was provided, we prioritize that over individual readGroupId or editGroupId
// filters to prevent collisions
if (groupIds?.length) {
// We are explicitly sending groupIds to sharedToGroups
processedFilters.sharedToGroups = groupIds;
} else {
// individual readGroupId & editGroupId filters
const readGroupIds = getOptionalPredicateStringsByKey(
const readGroupIds = getUniquePredicateValuesByKey<string>(
filters,
"readGroupId"
);
if (readGroupIds?.length) {
processedFilters.readGroups = readGroupIds;
}
const editGroupIds = getOptionalPredicateStringsByKey(
const editGroupIds = getUniquePredicateValuesByKey<string>(
filters,
"editGroupId"
);
Expand All @@ -81,7 +92,10 @@ export async function processFilters(
}
// NOTE: previously notGroup was an inverse of group, but now they are subtly different
// We do not yet have an inverse of sharedToGroups.
const notGroupIds = getPredicateValuesByKey<string>(filters, "notGroup");
const notGroupIds = getUniquePredicateValuesByKey<string>(
filters,
"notGroup"
);
// if a notGroup was provided, we prioritize that over individual notReadGroupId or notEditGroupId
// filters to prevent collisions
if (notGroupIds.length) {
Expand All @@ -100,45 +114,46 @@ export async function processFilters(
{ notReadGroupIds: [], notEditGroupIds: [] }
);
if (notReadGroupIds.length) {
processedFilters.withoutReadGroups = notReadGroupIds.join(",");
processedFilters.withoutReadGroups = notReadGroupIds;
}
if (notEditGroupIds.length) {
processedFilters.withoutEditGroups = notEditGroupIds.join(",");
processedFilters.withoutEditGroups = notEditGroupIds;
}
} else {
// individual notReadGroupId & notEditGroupId filters
const notReadGroupIds = getOptionalPredicateStringsByKey(
const notReadGroupIds = getUniquePredicateValuesByKey<string>(
filters,
"notReadGroupId"
);
if (notReadGroupIds?.length) {
processedFilters.withoutReadGroups = notReadGroupIds;
}
const notEditGroupIds = getOptionalPredicateStringsByKey(
const notEditGroupIds = getUniquePredicateValuesByKey<string>(
filters,
"notEditGroupId"
);
if (notEditGroupIds?.length) {
processedFilters.withoutEditGroups = notEditGroupIds;
}
}
const attendanceType = getOptionalPredicateStringsByKey(
// todo: <enums or strings?> (API is upper casing each value in the array, but throws off the typing here)
const attendanceType = getUniquePredicateValuesByKey<EventAttendanceType>(
filters,
"attendanceType"
);
if (attendanceType?.length) {
processedFilters.attendanceTypes = attendanceType;
}
const createdByIds = getOptionalPredicateStringsByKey(filters, "owner");
const createdByIds = getUniquePredicateValuesByKey<string>(filters, "owner");
if (createdByIds?.length) {
processedFilters.createdByIds = createdByIds;
}
const status = getOptionalPredicateStringsByKey(filters, "status");
// todo: <enums or strings?> (API is upper casing each value in the array, but throws off the typing here)
const status = getUniquePredicateValuesByKey<EventStatus>(filters, "status");
processedFilters.status = status?.length
? status
: [EventStatus.PLANNED, EventStatus.CANCELED]
.map((val) => val.toLowerCase())
.join(",");
: [EventStatus.PLANNED, EventStatus.CANCELED];

const startDateRange = getPredicateValuesByKey<IDateRange<string | number>>(
filters,
"startDateRange"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { IHubSearchOptions } from "../../types/IHubSearchOptions";
import {
EventSort,
GetEventsParams,
ISearchEvents,
EventSortOrder,
} from "../../../events/api/orval/api/orval-events";

/**
* Builds a Partial<GetEventsParams> for the given IHubSearchOptions
* Builds a Partial<ISearchEvents> for the given IHubSearchOptions
* @param options An IHubSearchOptions object
* @returns a Partial<GetEventsParams> for the given IHubSearchOptions
* @returns a Partial<ISearchEvents> for the given IHubSearchOptions
*/
export function processOptions(
options: IHubSearchOptions
): Partial<GetEventsParams> {
const processedOptions: Partial<GetEventsParams> = {};
): Partial<ISearchEvents> {
const processedOptions: Partial<ISearchEvents> = {};
if (options.num > 0) {
processedOptions.num = options.num.toString();
processedOptions.num = options.num;
}
if (options.start > 1) {
processedOptions.start = options.start.toString();
processedOptions.start = options.start;
}
if (options.sortField === "modified") {
processedOptions.sortBy = EventSort.updatedAt;
Expand Down
16 changes: 10 additions & 6 deletions packages/common/src/search/_internal/hubSearchEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ import { IQuery } from "../types/IHubCatalog";
import { IHubSearchOptions } from "../types/IHubSearchOptions";
import { IHubSearchResponse } from "../types/IHubSearchResponse";
import { IHubSearchResult } from "../types/IHubSearchResult";
import { getEvents } from "../../events/api/events";
import { GetEventsParams } from "../../events/api/orval/api/orval-events";
import { searchEvents } from "../../events/api/events";
import {
GetEventsInclude,
GetEventsParams,
ISearchEvents,
} from "../../events/api/orval/api/orval-events";
import { eventToSearchResult } from "./hubEventsHelpers/eventToSearchResult";
import { processOptions } from "./hubEventsHelpers/processOptions";
import { processFilters } from "./hubEventsHelpers/processFilters";

/**
* Searches for events against the Events 3 API using the given `query` and `options`.
* Currently supported filters include:
* - access: 'public' | 'private' | 'org' | Array<'public' | 'org' | 'access'>;
* - access: 'public' | 'private' | 'org' | Array<'public' | 'org' | 'private'>;
* - canEdit: boolean
* - entityId: string | string[];
* - entityType: string | string[];
Expand Down Expand Up @@ -54,12 +58,12 @@ export async function hubSearchEvents(
options.requestOptions
);
const processedOptions = processOptions(options);
const data: GetEventsParams = {
const data: ISearchEvents = {
...processedFilters,
...processedOptions,
include: "creator,location",
include: [GetEventsInclude.creator, GetEventsInclude.location],
};
const { items, nextStart, total } = await getEvents({
const { items, nextStart, total } = await searchEvents({
...options.requestOptions,
data,
});
Expand Down
Loading

0 comments on commit fa81971

Please sign in to comment.