Skip to content

Commit

Permalink
Merge pull request #1178 from opentripplanner/middleware-graphql
Browse files Browse the repository at this point in the history
Monitored Trips - Middleware GraphQL support
  • Loading branch information
binh-dam-ibigroup authored Oct 14, 2024
2 parents 17598ad + 18ee4b5 commit 4c6b236
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 22 deletions.
6 changes: 5 additions & 1 deletion lib/actions/apiV2.js
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,11 @@ export function routingQuery(searchId = null, updateSearchInReducer) {
}
}
})
?.map(convertGraphQLResponseToLegacy)
?.map((leg) => ({
...convertGraphQLResponseToLegacy(leg),
route: leg.transitLeg ? leg.route : undefined
})),
otp2QueryParams: query.variables
})
)

Expand Down
27 changes: 26 additions & 1 deletion lib/actions/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isEqual from 'lodash.isequal'
import qs from 'qs'
import toast from 'react-hot-toast'

import { applyRouteModeOverrides } from '../util/itinerary'
import {
convertToPlace,
getPersistenceMode,
Expand Down Expand Up @@ -129,6 +130,11 @@ export function fetchMonitoredTrips() {
'GET'
)
if (status === 'success') {
const { routeModeOverrides } = getState().otp.config
trips.data.forEach((trip) => {
applyRouteModeOverrides(trip.itinerary, routeModeOverrides)
})

dispatch(setCurrentUserMonitoredTrips(trips.data))
}
}
Expand Down Expand Up @@ -169,14 +175,33 @@ function convertRequestToSearch(config) {
}
}

/**
* Determines whether two GraphQL sets of variables are for the same trip request/search.
*
* Modes are excluded from the comparison because the UI triggers multiple queries
* with the same GraphQL variables but with different combinations of modes.
* Modes exclusion also means that if someone makes the same search with different mode settings
* (e.g. excludes/adds transit modes), that search will also be combined with the previous ones.
*/
function areRequestsSameExceptModes(qp1, qp2) {
const { modes: modes1, ...otherParams1 } = qp1
const { modes: modes2, ...otherParams2 } = qp2
return isEqual(otherParams1, otherParams2)
}

/**
* Removes duplicate requests so that only one request is displayed per "batch".
*/
function removeDuplicateRequests(filtered, tripRequest) {
// Compare one trip request to the next one.
if (filtered.length === 0) {
filtered.push(tripRequest)
} else if (!isEqual(filtered[filtered.length - 1].query, tripRequest.query)) {
} else if (
!areRequestsSameExceptModes(
filtered[filtered.length - 1].query,
tripRequest.query
)
) {
filtered.push(tripRequest)
} else {
filtered[filtered.length - 1].query.modes.push(...tripRequest.query.modes)
Expand Down
10 changes: 8 additions & 2 deletions lib/components/narrative/metro/default-route-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ const DefaultRouteRenderer = ({
leg,
style
}: RouteRendererProps): JSX.Element => {
const routeTitle = leg.routeShortName || leg.routeLongName
const routeTitle =
typeof leg.route === 'object'
? leg.route.shortName || leg.route.longName
: leg.routeShortName || leg.routeLongName
return (
<Block
color={leg.routeColor || '333333'}
color={
(typeof leg.route === 'object' ? leg.route.color : leg.routeColor) ||
'333333'
}
isOnColoredBackground={leg.onColoredBackground}
style={style}
title={routeTitle}
Expand Down
17 changes: 13 additions & 4 deletions lib/components/user/monitored-trip/saved-trip-screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import * as formActions from '../../../actions/form'
import * as uiActions from '../../../actions/ui'
import * as userActions from '../../../actions/user'
import { arrayToDayFields } from '../../../util/monitored-trip'
import {
copyAndRemoveRouteModeOverrides,
getItineraryDefaultMonitoredDays
} from '../../../util/itinerary'
import { getActiveItineraries, getActiveSearch } from '../../../util/state'
import { getItineraryDefaultMonitoredDays } from '../../../util/itinerary'
import { RETURN_TO_CURRENT_ROUTE } from '../../../util/ui'
import { TRIPS_PATH } from '../../../util/constants'
import AccountPage from '../account-page'
Expand Down Expand Up @@ -60,14 +63,16 @@ class SavedTripScreen extends Component {
itinerary,
homeTimezone
)
const { otp2QueryParams, ...otherItineraryProps } = itinerary
return {
...arrayToDayFields(monitoredDays),
arrivalVarianceMinutesThreshold: 5,
departureVarianceMinutesThreshold: 5,
excludeFederalHolidays: true,
isActive: true,
itinerary,
itinerary: copyAndRemoveRouteModeOverrides(otherItineraryProps),
leadTimeInMinutes: 30,
otp2QueryParams,
// when creating a monitored trip, the query params will be changed on the
// backend so that the modes parameter will reflect the modes seen in the
// itinerary
Expand All @@ -87,8 +92,13 @@ class SavedTripScreen extends Component {
*/
_updateMonitoredTrip = (monitoredTrip) => {
const { createOrUpdateUserMonitoredTrip, intl, isCreating } = this.props
const tripToSave = {
...monitoredTrip,
itinerary: copyAndRemoveRouteModeOverrides(monitoredTrip.itinerary)
}

createOrUpdateUserMonitoredTrip(
monitoredTrip,
tripToSave,
isCreating,
undefined,
undefined,
Expand Down Expand Up @@ -269,7 +279,6 @@ const mapStateToProps = (state, ownProps) => {
const tripId = ownProps.match.params.id
const { disableSingleItineraryDays } = state.otp.config
return {
activeSearchId: state.otp.activeSearchId,
disableSingleItineraryDays,
homeTimezone: state.otp.config.homeTimezone,
isCreating: tripId === 'new',
Expand Down
3 changes: 2 additions & 1 deletion lib/components/user/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export type MonitoredTrip = Record<DaysOfWeek, boolean> & {
itinerary: Itinerary
itineraryExistence?: ItineraryExistence
leadTimeInMinutes: number
queryParams: string
otp2QueryParams: Record<string, unknown>
queryParams: Record<string, unknown>
tripName: string
userId: string
}
Expand Down
15 changes: 2 additions & 13 deletions lib/reducers/create-otp-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import deepmerge from 'deepmerge'
import objectPath from 'object-path'
import update from 'immutability-helper'

import { checkForRouteModeOverride } from '../util/config'
import { applyRouteModeOverrides } from '../util/itinerary'
import {
FETCH_STATUS,
PERSIST_TO_LOCAL_STORAGE,
Expand Down Expand Up @@ -311,18 +311,7 @@ function createOtpReducer(config) {
response.requestId = requestId
response.plan.itineraries = response.plan?.itineraries?.map(
(itinerary) => {
itinerary.legs = itinerary.legs.map((leg) => {
if (leg.routeId) {
leg.mode = checkForRouteModeOverride(
{
id: leg.routeId,
mode: leg.mode
},
state.config?.routeModeOverrides
)
}
return leg
})
applyRouteModeOverrides(itinerary, state.config.routeModeOverrides)
return itinerary
}
)
Expand Down
39 changes: 39 additions & 0 deletions lib/util/itinerary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import hash from 'object-hash'
import memoize from 'lodash.memoize'

import { AppConfig, CO2Config } from './config-types'
import { checkForRouteModeOverride } from './config'
import { WEEKDAYS, WEEKEND_DAYS } from './monitored-trip'

export interface ItineraryStartTime {
Expand Down Expand Up @@ -450,3 +451,41 @@ export function addSortingCosts<T extends Itinerary>(
totalFare
}
}

interface LegWithOriginalMode extends Leg {
originalMode?: string
}

/** Applies route mode overrides to an itinerary. */
export function applyRouteModeOverrides(
itinerary: Itinerary,
routeModeOverrides: Record<string, string>
): void {
itinerary.legs.forEach((leg: LegWithOriginalMode) => {
// Use OTP2 leg route first, fallback on legacy leg routeId.
const routeId = typeof leg.route === 'object' ? leg.route.id : leg.routeId
if (routeId) {
leg.originalMode = leg.mode
leg.mode = checkForRouteModeOverride(
{
id: routeId,
mode: leg.mode
},
routeModeOverrides
)
}
})
}

/** Remove mode overrides from an itinerary */
export function copyAndRemoveRouteModeOverrides(
itinerary: Itinerary
): Itinerary {
return {
...itinerary,
legs: itinerary.legs.map((leg: LegWithOriginalMode) => ({
...leg,
mode: leg.originalMode || leg.mode
}))
}
}

0 comments on commit 4c6b236

Please sign in to comment.