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

V4.1/Accessibility alerts #578

Merged
merged 20 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
80 changes: 44 additions & 36 deletions common/api/alerts.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,82 @@
import type { AlertsResponse, OldAlert } from '../types/alerts';
import type { LineShort } from '../types/lines';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { getStationKeysFromStations } from '../utils/stations';
import { apiFetch } from './utils/fetch';

const alertsAPIConfig = {
activity: 'BOARD,EXIT,RIDE',
};

const accessibilityAlertsAPIConfig = {
activity: 'USING_ESCALATOR,USING_WHEELCHAIR',
};

export const fetchAlerts = async (
route: LineShort,
line: LineShort,
busRoute?: string
): Promise<AlertsResponse[]> => {
if (route === 'Bus' && busRoute) {
if (line === 'Bus' && busRoute) {
return fetchAlertsForBus(busRoute);
}
return fetchAlertsForLine(route);
return fetchAlertsForLine(line);
};

const fetchAlertsForLine = async (route: LineShort): Promise<AlertsResponse[]> => {
const url = new URL(`${APP_DATA_BASE_PATH}/api/alerts`, window.location.origin);
const fetchAlertsForLine = async (line: LineShort): Promise<AlertsResponse[]> => {
const options = { ...alertsAPIConfig };
if (route === 'Green') {
if (line === 'Green') {
// route_type 0 is light rail (green line & Mattapan)
options['route_type'] = '0';
} else {
options['route_type'] = '1';
options['route'] = route;
options['route'] = line;
}
Object.entries(options).forEach(([key, value]) => {
url.searchParams.append(key, value.toString());

return await apiFetch({
path: '/api/alerts',
options,
errorMessage: `Failed to fetch alerts for line ${line}`,
});
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error('Failed to fetch alerts');
}
return await response.json();
};

const fetchAlertsForBus = async (busRoute: string): Promise<AlertsResponse[]> => {
const url = new URL(`${APP_DATA_BASE_PATH}/api/alerts`, window.location.origin);
const options = { ...alertsAPIConfig };
const options = { ...alertsAPIConfig, route: busRoute };
options['route_type'] = '3';
options['route'] = busRoute;
Object.entries(options).forEach(([key, value]) => {
url.searchParams.append(key, value.toString());

return await apiFetch({
path: '/api/alerts',
options,
errorMessage: `Failed to fetch alerts for bus route ${busRoute}`,
});
};

export const fetchAccessibilityAlertsForLine = async (
line: LineShort
): Promise<AlertsResponse[]> => {
const stationKeys = getStationKeysFromStations(line);
const options = { ...accessibilityAlertsAPIConfig, stop: stationKeys.join(',') };
Copy link
Contributor

Choose a reason for hiding this comment

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

Also not a comment specific to this PR but I think we ought to have a utility function to handle this request setup for us, as we (myself included) have been duplicating it across these fns.

Copy link
Contributor

Choose a reason for hiding this comment

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

Added!


return await apiFetch({
path: '/api/alerts',
options,
errorMessage: 'Failed to fetch accessibility alerts',
});
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error('Failed to fetch alerts');
}
return await response.json();
};

export const fetchHistoricalAlerts = async (
date: string | undefined,
route: LineShort,
line: LineShort,
busRoute?: string
): Promise<OldAlert[]> => {
const url = new URL(`${APP_DATA_BASE_PATH}/api/alerts/${date}`, window.location.origin);
const options = { route: '' };
if (route === 'Bus' && busRoute) {
if (line === 'Bus' && busRoute) {
options['route'] = busRoute;
} else {
options['route'] = route;
options['route'] = line;
}
Object.entries(options).forEach(([key, value]) => {
url.searchParams.append(key, value.toString());

return await apiFetch({
path: `/api/alerts/${date}`,
options,
errorMessage: 'Failed to fetch historical alerts',
});
const response = await fetch(url.toString());
if (!response.ok) {
throw new Error('Failed to fetch alerts');
}
return await response.json();
};
17 changes: 17 additions & 0 deletions common/api/facilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { FacilitiesResponse } from '../types/facilities';
import type { LineShort } from '../types/lines';
import { getStationKeysFromStations } from '../utils/stations';
import { apiFetch } from './utils/fetch';

export const fetchAllElevatorsAndEscalators = async (
line: LineShort
): Promise<FacilitiesResponse> => {
const stationKeys = getStationKeysFromStations(line);
const options = { type: 'ESCALATOR,ELEVATOR', stop: stationKeys.join(',') };

return await apiFetch({
path: '/api/facilities',
options,
errorMessage: 'Failed to fetch elevators and escalators',
});
};
18 changes: 12 additions & 6 deletions common/api/hooks/alerts.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import { fetchAlerts, fetchHistoricalAlerts } from '../alerts';
import { fetchAlerts, fetchHistoricalAlerts, fetchAccessibilityAlertsForLine } from '../alerts';
import type { LineShort } from '../../types/lines';
import { FIVE_MINUTES, ONE_MINUTE } from '../../constants/time';
import type { AlertsResponse } from '../../types/alerts';

export const useHistoricalAlertsData = (
date: string | undefined,
route: LineShort,
line: LineShort,
busRoute?: string
) => {
return useQuery(
['alerts', date, route, busRoute],
() => fetchHistoricalAlerts(date, route, busRoute),
['alerts', date, line, busRoute],
() => fetchHistoricalAlerts(date, line, busRoute),
{
staleTime: FIVE_MINUTES,
enabled: date !== undefined,
Expand All @@ -21,10 +21,16 @@ export const useHistoricalAlertsData = (
};

export const useAlertsData = (
route: LineShort,
line: LineShort,
busRoute?: string
): UseQueryResult<AlertsResponse[]> => {
return useQuery(['alerts', route, busRoute], () => fetchAlerts(route, busRoute), {
return useQuery(['alerts', line, busRoute], () => fetchAlerts(line, busRoute), {
staleTime: ONE_MINUTE,
});
};

export const useAccessibilityAlertsData = (line: LineShort) => {
return useQuery(['accessibilityAlerts', line], () => fetchAccessibilityAlertsForLine(line), {
staleTime: ONE_MINUTE,
});
};
7 changes: 7 additions & 0 deletions common/api/hooks/facilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { fetchAllElevatorsAndEscalators } from '../facilities';
import type { LineShort } from '../../types/lines';

export const useElevatorsAndEscalators = (line: LineShort) => {

Check warning on line 5 in common/api/hooks/facilities.ts

View workflow job for this annotation

GitHub Actions / frontend (18, 3.10)

exported declaration 'useElevatorsAndEscalators' not used within other modules
return useQuery(['elevAndEsc', line], () => fetchAllElevatorsAndEscalators(line));
};
4 changes: 2 additions & 2 deletions common/api/hooks/ridership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { FetchRidershipOptions } from '../../types/api';
import { fetchLandingRidership, fetchRidership } from '../ridership';
import { ONE_HOUR } from '../../constants/time';

export const useRidershipData = (params: FetchRidershipOptions, enabled?: boolean) => {
return useQuery(['trips', params], () => fetchRidership(params), {
export const useRidershipData = (options: FetchRidershipOptions, enabled?: boolean) => {
return useQuery(['trips', options], () => fetchRidership(options), {
enabled: enabled,
staleTime: ONE_HOUR,
});
Expand Down
4 changes: 2 additions & 2 deletions common/api/hooks/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { FetchScheduledServiceOptions } from '../../types/api';
import { ONE_HOUR } from '../../constants/time';
import { fetchScheduledService } from '../service';

export const useScheduledService = (params: FetchScheduledServiceOptions, enabled?: boolean) => {
return useQuery(['scheduledservice', params], () => fetchScheduledService(params), {
export const useScheduledService = (options: FetchScheduledServiceOptions, enabled?: boolean) => {
return useQuery(['scheduledservice', options], () => fetchScheduledService(options), {
enabled: enabled,
staleTime: ONE_HOUR,
});
Expand Down
4 changes: 2 additions & 2 deletions common/api/hooks/speed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { fetchSpeeds } from '../speed';
import type { FetchSpeedsOptions } from '../../types/api';
import { FIVE_MINUTES } from '../../constants/time';

export const useSpeedData = (params: FetchSpeedsOptions, enabled?: boolean) => {
return useQuery(['speed', params], () => fetchSpeeds(params), {
export const useSpeedData = (options: FetchSpeedsOptions, enabled?: boolean) => {
return useQuery(['speed', options], () => fetchSpeeds(options), {
enabled: enabled,
staleTime: FIVE_MINUTES,
});
Expand Down
4 changes: 2 additions & 2 deletions common/api/hooks/tripmetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { FIVE_MINUTES } from '../../constants/time';
import { fetchActualTripsByLine, fetchLandingTripMetrics } from '../tripmetrics';

export const useDeliveredTripMetrics = (
params: FetchDeliveredTripMetricsOptions,
options: FetchDeliveredTripMetricsOptions,
enabled?: boolean
) => {
return useQuery(['actualTrips', params], () => fetchActualTripsByLine(params), {
return useQuery(['actualTrips', options], () => fetchActualTripsByLine(options), {
enabled: enabled,
staleTime: FIVE_MINUTES,
});
Expand Down
17 changes: 8 additions & 9 deletions common/api/ridership.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import { FetchRidershipParams } from '../types/api';
import type { FetchRidershipOptions } from '../types/api';
import type { RidershipCount } from '../types/dataPoints';
import { type Line } from '../types/lines';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { apiFetch } from './utils/fetch';

export const fetchRidership = async (
params: FetchRidershipOptions
options: FetchRidershipOptions
): Promise<RidershipCount[] | undefined> => {
if (!params[FetchRidershipParams.lineId]) return undefined;
const url = new URL(`${APP_DATA_BASE_PATH}/api/ridership`, window.location.origin);
Object.keys(params).forEach((paramKey) => {
url.searchParams.append(paramKey, params[paramKey]);
if (!options[FetchRidershipParams.lineId]) return undefined;

return await apiFetch({
path: '/api/ridership',
options,
errorMessage: 'Failed to fetch ridership counts',
});
const response = await fetch(url.toString());
if (!response.ok) throw new Error('Failed to fetch ridership counts');
return await response.json();
};

export const fetchLandingRidership = async (): Promise<{ [key in Line]: RidershipCount[] }> => {
Expand Down
18 changes: 8 additions & 10 deletions common/api/service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import type { FetchScheduledServiceOptions } from '../types/api';
import { FetchScheduledServiceParams } from '../types/api';
import type { ScheduledService } from '../types/dataPoints';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { apiFetch } from './utils/fetch';

export const fetchScheduledService = async (
params: FetchScheduledServiceOptions
options: FetchScheduledServiceOptions
): Promise<ScheduledService | undefined> => {
if (!params[FetchScheduledServiceParams.routeId]) return undefined;
const url = new URL(`${APP_DATA_BASE_PATH}/api/scheduledservice`, window.location.origin);
Object.keys(params).forEach((paramKey) => {
url.searchParams.append(paramKey, params[paramKey]);
});
const response = await fetch(url.toString());
if (!response.ok) throw new Error('Failed to fetch trip counts');
if (!options[FetchScheduledServiceParams.routeId]) return undefined;

return await response.json();
return await apiFetch({
path: '/api/scheduledservice',
options,
errorMessage: 'Failed to fetch trip counts',
});
};
18 changes: 9 additions & 9 deletions common/api/slowzones.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type {
SpeedRestriction,
} from '../../common/types/dataPoints';
import type { FetchSpeedRestrictionsOptions, FetchSpeedRestrictionsResponse } from '../types/api';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { getGtfsRailLineId } from '../utils/lines';
import { apiFetch } from './utils/fetch';

export const fetchDelayTotals = (): Promise<DayDelayTotals[]> => {
const url = new URL(`/static/slowzones/delay_totals.json`, window.location.origin);
Expand All @@ -21,18 +21,18 @@ export const fetchSpeedRestrictions = async (
options: FetchSpeedRestrictionsOptions
): Promise<SpeedRestriction[]> => {
const { lineId, date: requestedDate } = options;
const params = new URLSearchParams({ line_id: getGtfsRailLineId(lineId), date: requestedDate });
const speedRestrictionsUrl = new URL(
`${APP_DATA_BASE_PATH}/api/speed_restrictions?${params.toString()}`,
window.location.origin
);
const today = new Date();
const response = await fetch(speedRestrictionsUrl.toString());

const {
available,
date: resolvedDate,
zones,
}: FetchSpeedRestrictionsResponse = await response.json();
}: FetchSpeedRestrictionsResponse = await apiFetch({
path: `/api/speed_restrictions`,
options: { line_id: getGtfsRailLineId(lineId), date: requestedDate },
errorMessage: 'Failed to fetch speed restrictions',
});

const today = new Date();
if (available) {
return zones.map((zone) => ({
...zone,
Expand Down
18 changes: 8 additions & 10 deletions common/api/speed.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import type { FetchSpeedsOptions as FetchSpeedsOptions } from '../types/api';
import { FetchSpeedsParams as FetchSpeedsParams } from '../types/api';
import type { SpeedDataPoint } from '../types/dataPoints';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { apiFetch } from './utils/fetch';

export const fetchSpeeds = async (params: FetchSpeedsOptions): Promise<SpeedDataPoint[]> => {
if (!params[FetchSpeedsParams.line]) return [];
const url = new URL(`${APP_DATA_BASE_PATH}/api/speed`, window.location.origin);
Object.keys(params).forEach((paramKey) => {
url.searchParams.append(paramKey, params[paramKey]);
});
const response = await fetch(url.toString());
if (!response.ok) throw new Error('Failed to fetch traversal times');
export const fetchSpeeds = async (options: FetchSpeedsOptions): Promise<SpeedDataPoint[]> => {
if (!options[FetchSpeedsParams.line]) return [];

return await response.json();
return await apiFetch({
path: '/api/speed',
options,
errorMessage: 'Failed to fetch traversal times',
});
};
17 changes: 8 additions & 9 deletions common/api/tripmetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ import type { FetchDeliveredTripMetricsOptions } from '../types/api';
import { FetchDeliveredTripMetricsParams } from '../types/api';
import type { DeliveredTripMetrics } from '../types/dataPoints';
import type { Line } from '../types/lines';
import { APP_DATA_BASE_PATH } from '../utils/constants';
import { apiFetch } from './utils/fetch';

export const fetchActualTripsByLine = async (
params: FetchDeliveredTripMetricsOptions
options: FetchDeliveredTripMetricsOptions
): Promise<DeliveredTripMetrics[]> => {
if (!params[FetchDeliveredTripMetricsParams.line]) return [];
const url = new URL(`${APP_DATA_BASE_PATH}/api/tripmetrics`, window.location.origin);
Object.keys(params).forEach((paramKey) => {
url.searchParams.append(paramKey, params[paramKey]);
if (!options[FetchDeliveredTripMetricsParams.line]) return [];

return await apiFetch({
path: '/api/tripmetrics',
options,
errorMessage: 'Failed to fetch trip metrics',
});
const response = await fetch(url.toString());
if (!response.ok) throw new Error('Failed to fetch trip metrics');
return await response.json();
};

export const fetchLandingTripMetrics = (): Promise<{ [key in Line]: DeliveredTripMetrics[] }> => {
Expand Down
11 changes: 11 additions & 0 deletions common/api/utils/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { APP_DATA_BASE_PATH } from '../../utils/constants';

export const apiFetch = async ({ path, options, errorMessage }) => {
const url = new URL(`${APP_DATA_BASE_PATH}${path}`, window.location.origin);
Object.entries(options).forEach(([key, value]: [string, any]) => {

Check warning on line 5 in common/api/utils/fetch.ts

View workflow job for this annotation

GitHub Actions / frontend (18, 3.10)

Unexpected any. Specify a different type
url.searchParams.append(key, value.toString());
});
const response = await fetch(url.toString());
if (!response.ok) throw new Error(errorMessage);
return await response.json();
};
1 change: 1 addition & 0 deletions common/types/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface FormattedAlert {
stops: string[];
routes?: string[];
header: string;
description?: string;
}

export interface AlertsResponse {
Expand Down
Loading
Loading