/
DataAccess.ts
123 lines (108 loc) · 3.72 KB
/
DataAccess.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { decamelizeKeys } from 'humps';
import { DirectionSendResponseCode, Mapper, Route, RouteRequest } from '.';
import { ApiResponse, StApi } from '../Api';
import { routesCache as cache } from '../Cache';
import { NamedLocation } from '../Geo';
import { TransportAvoid, TransportMode } from '../Trip';
import { flatten, splitArrayToChunks } from '../Util';
import { estimateModeDirections } from './Estimator';
import { Waypoint } from './Route';
export async function getRoutes(requests: RouteRequest[]): Promise<Route[]> {
const keys: string[] = requests.map(createCacheKey);
const cached = await cache.getBatchMap(keys);
const routesData: any[] = keys.map((key: string) => (cached.get(key)));
const apiData = await getFromApi(requests.filter((request: RouteRequest, index: number) => (!routesData[index])));
return routesData.map((routeData: object | null) => {
if (routeData === null) {
return apiData.shift();
}
return routeData;
}).map((routeData, index) => {
const route: Route = Mapper.mapRouteFromApiResponse(
routeData,
requests[index].avoid
);
route.modeDirections = route.modeDirections.concat(
estimateModeDirections(route.modeDirections, route.origin, route.destination)
);
route.chosenDirection = route.modeDirections[0].directions[0];
return route;
});
}
async function getFromApi(requests: RouteRequest[]): Promise<object[]> {
const apiRequestData = requests.map((request: RouteRequest) => ({
origin: request.origin,
destination: request.destination,
waypoints: request.waypoints,
avoid: request.avoid,
depart_at: request.departAt,
arrive_at: request.arriveAt,
modes: request.chosenMode ? [request.chosenMode] : null
}));
const CHUNK_SIZE: number = 50;
const apiRequestDataChunks = splitArrayToChunks(apiRequestData, CHUNK_SIZE);
const apiRequests: Promise<ApiResponse>[] = apiRequestDataChunks.map((apiRequestDataChunk) => {
return StApi.post('/directions/path', apiRequestDataChunk);
});
const apiResponses: ApiResponse[] = await Promise.all(apiRequests);
const chunkedPaths: any[][] = apiResponses.map((apiResponse: ApiResponse) => {
apiResponse.data.path.forEach((routeData, index) => {
const request: RouteRequest = requests[index];
if (!(request.chosenMode === TransportMode.PUBLIC_TRANSIT && !request.departAt && !request.arriveAt)) {
cache.set(createCacheKey(request), routeData);
}
});
return apiResponse.data.path;
});
return flatten(chunkedPaths);
}
export async function sendDirections(
email: string,
destination: NamedLocation,
origin?: NamedLocation,
waypoints?: Waypoint[],
avoid?: TransportAvoid[],
) {
const apiRequestData: any = {
user_email: email,
destination
};
if (origin) {
apiRequestData.origin = origin;
}
if (waypoints) {
apiRequestData.waypoints = waypoints;
}
if (avoid) {
apiRequestData.avoid = avoid;
}
try {
await StApi.post('directions/send-by-email', decamelizeKeys(apiRequestData));
return DirectionSendResponseCode.OK;
} catch (e) {
if (e.response.data.status_code === 422) {
return DirectionSendResponseCode.ERROR_INVALID_INPUT;
}
}
return DirectionSendResponseCode.ERROR;
}
const createCacheKey = (request: RouteRequest): string => {
const parts: string[] = [];
parts.push(request.origin.lat.toString());
parts.push(request.origin.lng.toString());
parts.push(request.destination.lat.toString());
parts.push(request.destination.lng.toString());
parts.push(request.avoid.join('-'));
if (request.chosenMode) {
parts.push(request.chosenMode);
}
if (request.departAt) {
parts.push(request.departAt);
}
if (request.waypoints) {
parts.push(request.waypoints.map((waypoint) => (
waypoint.location.lat.toString() + '-' + waypoint.location.lng.toString()
)).join('-'));
}
return parts.join('-');
};