mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-04-20 23:23:56 +03:00
dbnav profile: locations, nearby
This commit is contained in:
parent
ad6c356552
commit
debc1ee150
16 changed files with 167 additions and 34 deletions
1
api.js
1
api.js
|
@ -4,6 +4,7 @@ import {createHafasRestApi as createApi} from 'hafas-rest-api';
|
|||
import {loyaltyCardParser} from 'db-rest/lib/loyalty-cards.js';
|
||||
import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js';
|
||||
|
||||
// TODO product support for nearby etc?
|
||||
const mapRouteParsers = (route, parsers) => {
|
||||
if (!route.includes('journey')) {
|
||||
return parsers;
|
||||
|
|
|
@ -34,6 +34,9 @@ const formatProductsFilter = (ctx, filter, key = 'vendo') => {
|
|||
if (!foundDeselected && key == 'ris') {
|
||||
return undefined;
|
||||
}
|
||||
if (!foundDeselected && key == 'dbnav') {
|
||||
return ['ALL'];
|
||||
}
|
||||
|
||||
return products;
|
||||
};
|
||||
|
|
|
@ -15,25 +15,6 @@ const formatStationBoardReq = (ctx, station, type) => {
|
|||
};
|
||||
};
|
||||
|
||||
/*
|
||||
TODO separate DB Nav profile?
|
||||
const formatStationBoardReq = (ctx, station, type) => {
|
||||
const { profile, opt } = ctx;
|
||||
|
||||
return {
|
||||
endpoint: profile.boardEndpoint,
|
||||
path: type == 'departures' ? 'abfahrt' : 'ankunft',
|
||||
body: { "anfragezeit": profile.formatTime(profile, opt.when), "datum": profile.formatDate(profile, opt.when), "ursprungsBahnhofId": profile.formatStation(station).lid, "verkehrsmittel": profile.formatProductsFilter(ctx, opt.products || {}, 'dbnav') },
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Accept': 'application/x.db.vendo.mob.bahnhofstafeln.v2+json',
|
||||
'X-Correlation-ID': 'null',
|
||||
'Content-Type': 'application/x.db.vendo.mob.bahnhofstafeln.v2+json'
|
||||
}
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO separate RIS::Boards profile?
|
||||
const formatRisStationBoardReq = (ctx, station, type) => {
|
||||
|
|
11
index.js
11
index.js
|
@ -104,7 +104,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
const results = (res[resultsField] || res.items).map(res => parse(ctx, res)); // todo sort?
|
||||
const results = (res[resultsField] || res.items || res.bahnhofstafelAbfahrtPositionen || res.bahnhofstafelAnkunftPositionen)
|
||||
.map(res => parse(ctx, res)); // TODO sort?, slice
|
||||
|
||||
return {
|
||||
[resultsField]: results,
|
||||
|
@ -189,7 +190,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
const req = profile.formatJourneysReq({profile, opt}, from, to, when, outFrwd, journeysRef);
|
||||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||
const ctx = {profile, opt, common, res};
|
||||
const verbindungen = opt.results ? res.verbindungen.slice(0, opt.results) : res.verbindungen;
|
||||
const verbindungen = Number.isInteger(opt.results) ? res.verbindungen.slice(0, opt.results) : res.verbindungen;
|
||||
const journeys = verbindungen
|
||||
.map(j => profile.parseJourney(ctx, j));
|
||||
|
||||
|
@ -246,7 +247,11 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
return res.map(loc => profile.parseLocation(ctx, loc));
|
||||
const results = res.map(loc => profile.parseLocation(ctx, loc));
|
||||
|
||||
return Number.isInteger(opt.results)
|
||||
? results.slice(0, opt.results)
|
||||
: results;
|
||||
};
|
||||
|
||||
const stop = async (stop, opt = {}) => { // TODO
|
||||
|
|
|
@ -100,7 +100,7 @@ const request = async (ctx, userAgent, reqData) => {
|
|||
const endpoint = reqData.endpoint;
|
||||
delete reqData.endpoint;
|
||||
const rawReqBody = profile.transformReqBody(ctx, reqData.body);
|
||||
// console.log(rawReqBody, JSON.stringify(rawReqBody.req.reisende));
|
||||
|
||||
const req = profile.transformReq(ctx, {
|
||||
agent: getAgent(),
|
||||
method: reqData.method,
|
||||
|
@ -121,7 +121,10 @@ const request = async (ctx, userAgent, reqData) => {
|
|||
query: reqData.query,
|
||||
});
|
||||
|
||||
const url = endpoint + (reqData.path || '') + '?' + stringify(req.query, {arrayFormat: 'brackets', encodeValuesOnly: true});
|
||||
const url = endpoint + (reqData.path || '');
|
||||
if (query) {
|
||||
url += '?' + stringify(req.query, {arrayFormat: 'brackets', encodeValuesOnly: true});
|
||||
}
|
||||
const reqId = randomBytes(3)
|
||||
.toString('hex');
|
||||
const fetchReq = new Request(url, req);
|
||||
|
@ -147,7 +150,7 @@ const request = async (ctx, userAgent, reqData) => {
|
|||
let cType = res.headers.get('content-type');
|
||||
if (cType) {
|
||||
const {type} = parseContentType(cType);
|
||||
if (type !== 'application/json' && type !== 'application/vnd.de.db.ris+json') {
|
||||
if (type !== req.headers['Accept']) {
|
||||
throw new HafasError('invalid/unsupported response content-type: ' + cType, null, errProps);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
|||
to = profile.formatLocation(profile, to, 'to');
|
||||
const filters = profile.formatProductsFilter({profile}, opt.products || {});
|
||||
// TODO opt.accessibility
|
||||
|
||||
// TODO routingMode
|
||||
let query = {
|
||||
maxUmstiege: opt.transfers,
|
||||
minUmstiegszeit: opt.transferTime,
|
||||
|
|
10
p/dbnav/base.json
Normal file
10
p/dbnav/base.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"journeysEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/fahrplan",
|
||||
"refreshJourneysEndpointPrice": "https://app.vendo.noncd.db.de/mob/angebote/recon/autonomereservierung",
|
||||
"refreshJourneysEndpointPolyline": "https://app.vendo.noncd.db.de/mob/trip/recon",
|
||||
"locationsEndpoint": "https://app.vendo.noncd.db.de/mob/location/search",
|
||||
"nearbyEndpoint": "https://app.vendo.noncd.db.de/mob/location/nearby",
|
||||
"tripEndpoint": "https://app.vendo.noncd.db.de/mob/zuglauf",
|
||||
"boardEndpoint": "https://app.vendo.noncd.db.de/mob/bahnhofstafel/abfahrt",
|
||||
"defaultLanguage": "en"
|
||||
}
|
11
p/dbnav/header.js
Normal file
11
p/dbnav/header.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const getHeaders = (contentType) => {
|
||||
return {
|
||||
'X-Correlation-ID': 'null',
|
||||
'Accept': contentType,
|
||||
'Content-Type': contentType,
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
getHeaders,
|
||||
};
|
33
p/dbnav/index.js
Normal file
33
p/dbnav/index.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {createRequire} from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const baseProfile = require('./base.json');
|
||||
import {products} from '../../lib/products.js';
|
||||
// import {formatJourneysReq, formatRefreshJourneyReq} from './journeys-req.js';
|
||||
import {formatLocationFilter} from './location-filter.js';
|
||||
import {formatLocationsReq} from './locations-req.js';
|
||||
import {formatNearbyReq} from './nearby-req.js';
|
||||
import {formatStationBoardReq} from './station-board-req.js';
|
||||
// import {formatTravellers} from './travellers.js';
|
||||
// import {parseTickets, parsePrice} from './tickets.js';
|
||||
|
||||
const profile = {
|
||||
...baseProfile,
|
||||
locale: 'de-DE',
|
||||
timezone: 'Europe/Berlin',
|
||||
|
||||
products,
|
||||
// formatJourneysReq,
|
||||
// formatRefreshJourneyReq,
|
||||
formatNearbyReq,
|
||||
formatLocationsReq,
|
||||
formatStationBoardReq,
|
||||
formatLocationFilter,
|
||||
// parsePrice,
|
||||
// parseTickets,
|
||||
// formatTravellers,
|
||||
};
|
||||
|
||||
export {
|
||||
profile,
|
||||
};
|
20
p/dbnav/location-filter.js
Normal file
20
p/dbnav/location-filter.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const formatLocationFilter = (stops, addresses, poi) => {
|
||||
if (stops && addresses && poi) {
|
||||
return ['ALL'];
|
||||
}
|
||||
const types = [];
|
||||
if (stops) {
|
||||
types.push('ST');
|
||||
}
|
||||
if (addresses) {
|
||||
types.push('ADR');
|
||||
}
|
||||
if (poi) {
|
||||
types.push('POI');
|
||||
}
|
||||
return types;
|
||||
};
|
||||
|
||||
export {
|
||||
formatLocationFilter,
|
||||
};
|
20
p/dbnav/locations-req.js
Normal file
20
p/dbnav/locations-req.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import {getHeaders} from './header.js';
|
||||
|
||||
const formatLocationsReq = (ctx, query) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
endpoint: profile.locationsEndpoint,
|
||||
body: {
|
||||
locationTypes: profile.formatLocationFilter(opt.stops, opt.addresses, opt.poi),
|
||||
searchTerm: query,
|
||||
maxResults: opt.results,
|
||||
},
|
||||
headers: getHeaders('application/x.db.vendo.mob.location.v3+json'),
|
||||
method: 'post',
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatLocationsReq,
|
||||
};
|
29
p/dbnav/nearby-req.js
Normal file
29
p/dbnav/nearby-req.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import {getHeaders} from './header.js';
|
||||
|
||||
const formatNearbyReq = (ctx, location) => {
|
||||
const {profile, opt} = ctx;
|
||||
if (opt.distance > 10000) {
|
||||
throw new Error('maximum supported distance by this endpoint is 10000');
|
||||
}
|
||||
// TODO location types
|
||||
return {
|
||||
endpoint: profile.nearbyEndpoint,
|
||||
body: {
|
||||
area: {
|
||||
coordinates: {
|
||||
longitude: location.longitude,
|
||||
latitude: location.latitude,
|
||||
},
|
||||
radius: opt.distance || 10000,
|
||||
},
|
||||
maxResults: opt.results,
|
||||
products: profile.formatProductsFilter(ctx, opt.products || {}, 'dbnav'),
|
||||
},
|
||||
headers: getHeaders('application/x.db.vendo.mob.location.v3+json'),
|
||||
method: 'post',
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatNearbyReq,
|
||||
};
|
17
p/dbnav/station-board-req.js
Normal file
17
p/dbnav/station-board-req.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import {getHeaders} from './header.js';
|
||||
|
||||
const formatStationBoardReq = (ctx, station, type) => {
|
||||
const {profile, opt} = ctx;
|
||||
|
||||
return {
|
||||
endpoint: profile.boardEndpoint,
|
||||
path: type == 'departures' ? 'abfahrt' : 'ankunft',
|
||||
body: {anfragezeit: profile.formatTimeOfDay(profile, opt.when), datum: profile.formatDate(profile, opt.when), ursprungsBahnhofId: profile.formatStation(station).lid, verkehrsmittel: profile.formatProductsFilter(ctx, opt.products || {}, 'dbnav')},
|
||||
method: 'POST',
|
||||
header: getHeaders('application/x.db.vendo.mob.bahnhofstafeln.v2+json'),
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
formatStationBoardReq,
|
||||
};
|
|
@ -13,16 +13,16 @@ const parseLocation = (ctx, l) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const lid = parse(l.id, {delimiter: '@'});
|
||||
const lid = parse(l.id || l.locationId, {delimiter: '@'});
|
||||
const res = {
|
||||
type: 'location',
|
||||
id: (l.extId || lid.L || l.evaNumber || l.evaNo || '').replace(leadingZeros, '') || null,
|
||||
id: (l.extId || l.evaNr || lid.L || l.evaNumber || l.evaNo || '').replace(leadingZeros, '') || null,
|
||||
};
|
||||
const name = l.name || lid.O;
|
||||
|
||||
if (l.lat && l.lon) {
|
||||
res.latitude = l.lat;
|
||||
res.longitude = l.lon;
|
||||
if (l.lat && l.lon || l.coordinates || l.position) {
|
||||
res.latitude = l.lat || l.coordinates?.latitude || l.position?.latitude;
|
||||
res.longitude = l.lon || l.coordinates?.longitude || l.position?.longitude;
|
||||
} else if ('X' in lid && 'Y' in lid) {
|
||||
res.latitude = lid.Y / 1000000;
|
||||
res.longitude = lid.X / 1000000;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const parseProducts = ({profile}, bitmask) => {
|
||||
const parseProducts = ({profile}, products) => {
|
||||
const res = {};
|
||||
for (let product of profile.products) {
|
||||
res[product.id] = Boolean(bitmask.find(p => p == product.vendo));
|
||||
res[product.id] = Boolean(products.find(p => p == product.vendo || p == product.dbnav));
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ const opt = {
|
|||
via: null,
|
||||
stopovers: false,
|
||||
transfers: null,
|
||||
transferTime: 0,
|
||||
transferTime: 0,
|
||||
accessibility: 'none',
|
||||
bike: false,
|
||||
walkingSpeed: 'normal',
|
||||
|
|
Loading…
Add table
Reference in a new issue