mirror of
				https://github.com/public-transport/db-vendo-client.git
				synced 2025-11-04 10:06:32 +02:00 
			
		
		
		
	refreshJourney with tickets, loyaltyCard/firstClass rest support, accept-lang
This commit is contained in:
		
							parent
							
								
									f379fba930
								
							
						
					
					
						commit
						760a1bdb54
					
				
					 12 changed files with 10884 additions and 50 deletions
				
			
		
							
								
								
									
										26
									
								
								api.js
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								api.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,30 @@
 | 
			
		|||
import {createClient} from './index.js';
 | 
			
		||||
import {profile as dbProfile} from './p/db/index.js';
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
const mapRouteParsers = (route, parsers) => {
 | 
			
		||||
	if (!route.includes('journey')) {
 | 
			
		||||
		return parsers;
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		...parsers,
 | 
			
		||||
		loyaltyCard: loyaltyCardParser,
 | 
			
		||||
		firstClass: {
 | 
			
		||||
			description: 'Search for first-class options?',
 | 
			
		||||
			type: 'boolean',
 | 
			
		||||
			default: 'false',
 | 
			
		||||
			parse: parseBoolean,
 | 
			
		||||
		},
 | 
			
		||||
		age: {
 | 
			
		||||
			description: 'Age of traveller',
 | 
			
		||||
			type: 'integer',
 | 
			
		||||
			defaultStr: '*adult*',
 | 
			
		||||
			parse: parseInteger,
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const config = {
 | 
			
		||||
	hostname: process.env.HOSTNAME || 'localhost',
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +39,10 @@ const config = {
 | 
			
		|||
	aboutPage: true,
 | 
			
		||||
	etags: 'strong',
 | 
			
		||||
	csp: 'default-src \'none\' style-src \'self\' \'unsafe-inline\' img-src https:',
 | 
			
		||||
	mapRouteParsers,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const start = async () => {
 | 
			
		||||
	const vendo = createClient(dbProfile, 'my-hafas-rest-api');
 | 
			
		||||
	const api = await createApi(vendo, config);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								index.js
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								index.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -43,7 +43,7 @@ const createClient = (profile, userAgent, opt = {}) => {
 | 
			
		|||
		throw new TypeError('userAgent must be a string');
 | 
			
		||||
	}
 | 
			
		||||
	if (FORBIDDEN_USER_AGENTS.includes(userAgent.toLowerCase())) {
 | 
			
		||||
		throw new TypeError(`userAgent should tell the HAFAS API operators how to contact you. If you have copied "${userAgent}" value from the documentation, please adapt it.`);
 | 
			
		||||
		throw new TypeError(`userAgent should tell the API operators how to contact you. If you have copied "${userAgent}" value from the documentation, please adapt it.`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const _stationBoard = async (station, type, resultsField, parse, opt = {}) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -241,17 +241,11 @@ const createClient = (profile, userAgent, opt = {}) => {
 | 
			
		|||
		const req = profile.formatRefreshJourneyReq({profile, opt}, refreshToken);
 | 
			
		||||
 | 
			
		||||
		const {res, common} = await profile.request({profile, opt}, userAgent, req);
 | 
			
		||||
		if (!Array.isArray(res.outConL) || !res.outConL[0]) {
 | 
			
		||||
			throw new HafasError('invalid response, expected outConL[0]', null, {});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const ctx = {profile, opt, common, res};
 | 
			
		||||
 | 
			
		||||
		return {
 | 
			
		||||
			journey: profile.parseJourney(ctx, res.outConL[0]),
 | 
			
		||||
			realtimeDataUpdatedAt: res.planrtTS && res.planrtTS !== '0'
 | 
			
		||||
				? parseInt(res.planrtTS)
 | 
			
		||||
				: null,
 | 
			
		||||
			journey: profile.parseJourney(ctx, res.verbindungen[0]),
 | 
			
		||||
			realtimeDataUpdatedAt: null, // TODO
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,7 +95,7 @@ const checkIfResponseIsOk = (_) => {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
const request = async (ctx, userAgent, reqData) => {
 | 
			
		||||
	const {profile} = ctx;
 | 
			
		||||
	const {profile, opt} = ctx;
 | 
			
		||||
 | 
			
		||||
	const endpoint = reqData.endpoint;
 | 
			
		||||
	delete reqData.endpoint;
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +110,7 @@ const request = async (ctx, userAgent, reqData) => {
 | 
			
		|||
			'Content-Type': 'application/json',
 | 
			
		||||
			'Accept-Encoding': 'gzip, br, deflate',
 | 
			
		||||
			'Accept': 'application/json',
 | 
			
		||||
			'Accept-Language': opt.language || profile.defaultLanguage || 'en',
 | 
			
		||||
			'user-agent': profile.randomizeUserAgent
 | 
			
		||||
				? randomizeUserAgent(userAgent)
 | 
			
		||||
				: userAgent,
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +122,6 @@ const request = async (ctx, userAgent, reqData) => {
 | 
			
		|||
	});
 | 
			
		||||
 | 
			
		||||
	const url = endpoint + (reqData.path || '') + '?' + stringify(req.query, {arrayFormat: 'brackets', encodeValuesOnly: true});
 | 
			
		||||
	console.log(url);
 | 
			
		||||
	const reqId = randomBytes(3)
 | 
			
		||||
		.toString('hex');
 | 
			
		||||
	const fetchReq = new Request(url, req);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
	"journeysEndpoint": "https://int.bahn.de/web/api/angebote/fahrplan",
 | 
			
		||||
	"refreshJourneysEndpoint": "https://int.bahn.de/web/api/angebote/recon",
 | 
			
		||||
	"locationsEndpoint": "https://int.bahn.de/web/api/reiseloesung/orte",
 | 
			
		||||
	"nearbyEndpoint": "https://int.bahn.de/web/api/reiseloesung/orte/nearby",
 | 
			
		||||
	"boardEndpoint": "https://regio-guide.de/@prd/zupo-travel-information/api/public/ri/board/",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -204,36 +204,20 @@ const transformJourneysQuery = ({profile, opt}, query) => {
 | 
			
		|||
 | 
			
		||||
const formatRefreshJourneyReq = (ctx, refreshToken) => {
 | 
			
		||||
	const {profile, opt} = ctx;
 | 
			
		||||
	const req = {
 | 
			
		||||
		getIST: true,
 | 
			
		||||
		getPasslist: Boolean(opt.stopovers),
 | 
			
		||||
		getPolyline: Boolean(opt.polylines),
 | 
			
		||||
		getTariff: Boolean(opt.tickets),
 | 
			
		||||
	let query = {
 | 
			
		||||
		ctxRecon: refreshToken,
 | 
			
		||||
		deutschlandTicketVorhanden: false,
 | 
			
		||||
		nurDeutschlandTicketVerbindungen: false,
 | 
			
		||||
		reservierungsKontingenteVorhanden: false,
 | 
			
		||||
	};
 | 
			
		||||
	if (profile.refreshJourneyUseOutReconL) {
 | 
			
		||||
		req.outReconL = [{ctx: refreshToken}];
 | 
			
		||||
	} else {
 | 
			
		||||
		req.ctxRecon = refreshToken;
 | 
			
		||||
	}
 | 
			
		||||
	req.trfReq = trfReq(opt, true);
 | 
			
		||||
 | 
			
		||||
	query = Object.assign(query, trfReq(opt, true));
 | 
			
		||||
	return {
 | 
			
		||||
		meth: 'Reconstruction',
 | 
			
		||||
		req,
 | 
			
		||||
		endpoint: profile.refreshJourneysEndpoint,
 | 
			
		||||
		body: query,
 | 
			
		||||
		method: 'post',
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// todo: fix this
 | 
			
		||||
// line: {
 | 
			
		||||
// 	type: 'line',
 | 
			
		||||
// 	id: '5-vbbbvb-x9',
 | 
			
		||||
// 	fahrtNr: '52496',
 | 
			
		||||
// 	name: 'X9',
 | 
			
		||||
// 	public: true,
 | 
			
		||||
// 	mode: 'bus',
 | 
			
		||||
// 	product: 'bus',
 | 
			
		||||
// 	operator: {type: 'operator', id: 'nahreisezug', name: 'Nahreisezug'}
 | 
			
		||||
// }
 | 
			
		||||
const parseLineWithAdditionalName = ({parsed}, l) => {
 | 
			
		||||
	if (l.nameS && ['bus', 'tram', 'ferry'].includes(l.product)) {
 | 
			
		||||
		parsed.name = l.nameS;
 | 
			
		||||
| 
						 | 
				
			
			@ -245,11 +229,8 @@ const parseLineWithAdditionalName = ({parsed}, l) => {
 | 
			
		|||
	return parsed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// todo: sotRating, conSubscr, isSotCon, showARSLink, sotCtxt
 | 
			
		||||
// todo: conSubscr, showARSLink, useableTime
 | 
			
		||||
const mutateToAddPrice = (parsed, raw) => {
 | 
			
		||||
	parsed.price = null;
 | 
			
		||||
	// TODO find all prices?
 | 
			
		||||
	if (raw.angebotsPreis?.betrag) {
 | 
			
		||||
		parsed.price = {
 | 
			
		||||
			amount: raw.angebotsPreis.betrag,
 | 
			
		||||
| 
						 | 
				
			
			@ -260,9 +241,49 @@ const mutateToAddPrice = (parsed, raw) => {
 | 
			
		|||
	return parsed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mutateToAddTickets = (parsed, opt, j) => {
 | 
			
		||||
	if (!opt.tickets) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (j.reiseAngebote && j.reiseAngebote.length > 0) { // if refreshJourney()
 | 
			
		||||
		parsed.tickets = j.reiseAngebote
 | 
			
		||||
			.filter(s => s.typ == 'REISEANGEBOT' && !s.angebotsbeziehungList.flatMap(b => b.referenzen)
 | 
			
		||||
				.find(r => r.referenzAngebotsoption == 'PFLICHT'))
 | 
			
		||||
			.map((s) => {
 | 
			
		||||
				return {
 | 
			
		||||
					name: s.name,
 | 
			
		||||
					priceObj: {
 | 
			
		||||
						amount: Math.round(s.preis?.betrag * 100),
 | 
			
		||||
						currency: s.preis?.waehrung,
 | 
			
		||||
					},
 | 
			
		||||
					addData: s.teilpreis ? 'Teilpreis / partial fare' : undefined,
 | 
			
		||||
					addDataTicketInfo: s.konditionsAnzeigen?.map(a => a.anzeigeUeberschrift)
 | 
			
		||||
						.join('. '),
 | 
			
		||||
					addDataTicketDetails: s.konditionsAnzeigen?.map(a => a.textLang)
 | 
			
		||||
						.join(' '),
 | 
			
		||||
					addDataTravelInfo: s.leuchtturmInfo?.text,
 | 
			
		||||
					firstClass: s.klasse == 'KLASSE_1',
 | 
			
		||||
					partialFare: s.teilpreis,
 | 
			
		||||
				};
 | 
			
		||||
			});
 | 
			
		||||
		if (opt.generateUnreliableTicketUrls) {
 | 
			
		||||
			// TODO
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else if (j.angebotsPreis?.betrag) { // if journeys()
 | 
			
		||||
		parsed.tickets = [{
 | 
			
		||||
			name: 'from',
 | 
			
		||||
			priceObj: {
 | 
			
		||||
				amount: Math.round(j.angebotsPreis.betrag * 100),
 | 
			
		||||
				currency: j.angebotsPreis.waehrung,
 | 
			
		||||
			},
 | 
			
		||||
		}];
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const parseJourneyWithPriceAndTickets = ({parsed, opt}, raw) => {
 | 
			
		||||
	mutateToAddPrice(parsed, raw);
 | 
			
		||||
	// mutateToAddTickets(parsed, opt, raw); TODO
 | 
			
		||||
	mutateToAddTickets(parsed, opt, raw);
 | 
			
		||||
	return parsed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,4 @@
 | 
			
		|||
const c = {
 | 
			
		||||
	NONE: Symbol('no loyalty card'),
 | 
			
		||||
	BAHNCARD: Symbol('Bahncard'),
 | 
			
		||||
	VORTEILSCARD: Symbol('VorteilsCard'),
 | 
			
		||||
	HALBTAXABO: Symbol('HalbtaxAbo'),
 | 
			
		||||
	VOORDEELURENABO: Symbol('Voordeelurenabo'),
 | 
			
		||||
	SHCARD: Symbol('SH-Card'),
 | 
			
		||||
	GENERALABONNEMENT: Symbol('General-Abonnement'),
 | 
			
		||||
};
 | 
			
		||||
import {data as c} from 'hafas-client/p/db/loyalty-cards.js'; // TODO remove hafas-client dep?
 | 
			
		||||
 | 
			
		||||
// see https://gist.github.com/juliuste/202bb04f450a79f8fa12a2ec3abcd72d
 | 
			
		||||
const formatLoyaltyCard = (data) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,7 @@
 | 
			
		|||
		"cross-fetch": "^4.0.0",
 | 
			
		||||
		"google-polyline": "^1.0.3",
 | 
			
		||||
		"gps-distance": "0.0.4",
 | 
			
		||||
		"hafas-client": "^6.3.2",
 | 
			
		||||
		"https-proxy-agent": "^7.0.0",
 | 
			
		||||
		"lodash": "^4.17.5",
 | 
			
		||||
		"luxon": "^3.1.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +74,7 @@
 | 
			
		|||
		"@pollyjs/core": "^6.0.5",
 | 
			
		||||
		"@pollyjs/persister-fs": "^6.0.5",
 | 
			
		||||
		"@stylistic/eslint-plugin": "^1.5.1",
 | 
			
		||||
		"db-rest": "github:derhuerst/db-rest",
 | 
			
		||||
		"eslint": "^8.56.0",
 | 
			
		||||
		"hafas-rest-api": "^5.1.3",
 | 
			
		||||
		"is-coordinates": "^2.0.2",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,11 @@ import slugg from 'slugg';
 | 
			
		|||
 | 
			
		||||
const parseLine = (ctx, p) => {
 | 
			
		||||
	const profile = ctx.profile;
 | 
			
		||||
	const fahrtNr = p.verkehrsmittel?.nummer || p.transport?.number || p.train?.no;
 | 
			
		||||
	const res = {
 | 
			
		||||
		type: 'line',
 | 
			
		||||
		id: slugg(p.verkehrsmittel?.langText || p.transport?.journeyDescription || p.train?.no), // TODO terrible
 | 
			
		||||
		fahrtNr: (p.verkehrsmittel?.nummer || p.transport?.number || p.train?.no)+'',
 | 
			
		||||
		fahrtNr: fahrtNr ? String(fahrtNr) : undefined,
 | 
			
		||||
		name: p.verkehrsmittel?.name || p.zugName || p.transport?.journeyDescription || p.train && p.train.category + ' ' + p.train.lineName,
 | 
			
		||||
		public: true,
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										40
									
								
								test/db-refresh-journey.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								test/db-refresh-journey.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
// todo: use import assertions once they're supported by Node.js & ESLint
 | 
			
		||||
// https://github.com/tc39/proposal-import-assertions
 | 
			
		||||
import {createRequire} from 'module';
 | 
			
		||||
const require = createRequire(import.meta.url);
 | 
			
		||||
 | 
			
		||||
import tap from 'tap';
 | 
			
		||||
 | 
			
		||||
import {createClient} from '../index.js';
 | 
			
		||||
import {profile as rawProfile} from '../p/db/index.js';
 | 
			
		||||
const res = require('./fixtures/db-refresh-journey.json');
 | 
			
		||||
import {dbJourney as expected} from './fixtures/db-refresh-journey.js';
 | 
			
		||||
 | 
			
		||||
const client = createClient(rawProfile, 'public-transport/hafas-client:test');
 | 
			
		||||
const {profile} = client;
 | 
			
		||||
 | 
			
		||||
const opt = {
 | 
			
		||||
	results: null,
 | 
			
		||||
	via: null,
 | 
			
		||||
	stopovers: false,
 | 
			
		||||
	transfers: -1,
 | 
			
		||||
	transferTime: 0,
 | 
			
		||||
	accessibility: 'none',
 | 
			
		||||
	bike: false,
 | 
			
		||||
	tickets: true,
 | 
			
		||||
	polylines: true,
 | 
			
		||||
	remarks: true,
 | 
			
		||||
	walkingSpeed: 'normal',
 | 
			
		||||
	startWithWalking: true,
 | 
			
		||||
	scheduledDays: false,
 | 
			
		||||
	departure: '2020-04-10T20:33+02:00',
 | 
			
		||||
	products: {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
tap.test('parses a refresh journey correctly (DB)', (t) => {
 | 
			
		||||
	const ctx = {profile, opt, common: null, res};
 | 
			
		||||
	const journey = profile.parseJourney(ctx, res.verbindungen[0]);
 | 
			
		||||
 | 
			
		||||
	t.same(journey, expected.journey);
 | 
			
		||||
	t.end();
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										7
									
								
								test/fixtures/db-journey.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								test/fixtures/db-journey.js
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -283,6 +283,13 @@ const dbJourney = {
 | 
			
		|||
	],
 | 
			
		||||
	refreshToken: '¶HKI¶T$A=1@O=Köln Hbf@X=6958730@Y=50943029@L=8000207@a=128@$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$202504110511$202504110512$S     12$$1$$$$$$§W$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$202504110512$202504110519$$$1$$$$$$§T$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$A=1@O=Nürnberg Hbf@X=11082989@Y=49445615@L=8000284@a=128@$202504110520$202504110858$ICE  523$$1$$$$$$¶KC¶#VE#2#CF#100#CA#0#CM#0#SICT#0#AM#81#AM2#0#RT#7#¶KCC¶I1ZFIzEjRVJHIzMjSElOIzAjRUNLIzcwNTkxMXw3MDU5MTF8NzA2MTM4fDcwNjEzOHwwfDB8NDg1fDcwNTg5N3wxfDB8MTh8MHwwfC0yMTQ3NDgzNjQ4I0dBTSMxMTA0MjUwNTExIwpaI1ZOIzEjU1QjMTczMzE3MzczMSNQSSMxI1pJIzE2MTQ3MyNUQSMxI0RBIzExMDQyNSMxUyM4MDAwMjA4IzFUIzUwNCNMUyM4MDAyNzUzI0xUIzU0NSNQVSM4MSNSVCMxI0NBI3MjWkUjMTIjWkIjUyAgICAgMTIjUEMjNCNGUiM4MDAwMjA3I0ZUIzUxMSNUTyM4MDAzMzY4I1RUIzUxMiMKRiNWTiMwI1NUIzE3MzMxNzM3MzEjUEkjMSNQVSM4MSNaSSMyMjgzODI4ODkzI0RBIzExMDQyNSNGUiM4MDAzMzY4I1RPIzgwNzMzNjgjRlQjNTEyI1RUIzUxOSNUUyMwI0ZGIyNGViMwIwpaI1ZOIzEjU1QjMTczMzE3MzczMSNQSSMxI1pJIzE1NTA2MyNUQSMwI0RBIzExMDQyNSMxUyM4MDAwMDgwIzFUIzM1OCNMUyM4MDAwMjYxI0xUIzEwMDYjUFUjODEjUlQjMSNDQSNJQ0UjWkUjNTIzI1pCI0lDRSAgNTIzI1BDIzAjRlIjODA3MzM2OCNGVCM1MjAjVE8jODAwMDI4NCNUVCM4NTgj¶KRCC¶#VE#1#¶SC¶1_H4sIAAAAAAACA32P306DMBjFX8X0GpevhUIhIUFGFv8sGzHOaIwXbHQTU2CWskgIz+GbeOXdXswCemE09qLpOT09v68tOnCJPIQnDkMG4q9Kiyic3EYTV2vJX5DXoqLOZ8ijRn8IkQcGKmsVJYrrMAFCwcIYDeZNlvcmUNBLW9uh4RQb6LloZkLJOfIeWqSafR+Lr5eRDuVl2quLxVSLQyLqXmEgJuoeh5mmT7uxWJNTvp+Xm7FGZKlOnvk4WPpXx3dRnJyvt8Gdb7uUOSYE9z4F1zKBuMHKZxDM9QZAwAlC/WbvY8c0scNsmwSZvzq+ATDA1KIs0INUavzgbJgikfJP7OL4IYs1l7svNMbAiMtczbZcy6I2pj/YzPqHTQh2zd/sHVdxKRqRFdpTsuaDdVnWsuBNWNZFWiFvm4hqvIiTqhJZpb6zfFPGiUxyHWq7rvsE0LytQvMBAAA=',
 | 
			
		||||
	price: {amount: 31.49, currency: 'EUR', hint: null},
 | 
			
		||||
	tickets: [{
 | 
			
		||||
		name: 'from',
 | 
			
		||||
		priceObj: {
 | 
			
		||||
			amount: 3149,
 | 
			
		||||
			currency: 'EUR',
 | 
			
		||||
		},
 | 
			
		||||
	}],
 | 
			
		||||
	remarks: [],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										265
									
								
								test/fixtures/db-refresh-journey.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								test/fixtures/db-refresh-journey.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,265 @@
 | 
			
		|||
const dbJourney = {
 | 
			
		||||
	journey: {
 | 
			
		||||
		type: 'journey',
 | 
			
		||||
		legs: [
 | 
			
		||||
			{
 | 
			
		||||
				origin: {
 | 
			
		||||
					type: 'stop',
 | 
			
		||||
					id: '8011160',
 | 
			
		||||
					name: 'Berlin Hbf',
 | 
			
		||||
					location: {
 | 
			
		||||
						type: 'location',
 | 
			
		||||
						id: '8011160',
 | 
			
		||||
						latitude: 52.525589,
 | 
			
		||||
						longitude: 13.369549,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				destination: {
 | 
			
		||||
					type: 'stop',
 | 
			
		||||
					id: '8000080',
 | 
			
		||||
					name: 'Dortmund Hbf',
 | 
			
		||||
					location: {
 | 
			
		||||
						type: 'location',
 | 
			
		||||
						id: '8000080',
 | 
			
		||||
						latitude: 51.517899,
 | 
			
		||||
						longitude: 7.459294,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				departure: '2024-12-18T00:22:00+01:00',
 | 
			
		||||
				plannedDeparture: '2024-12-18T00:22:00+01:00',
 | 
			
		||||
				departureDelay: null,
 | 
			
		||||
				arrival: '2024-12-18T05:21:00+01:00',
 | 
			
		||||
				plannedArrival: '2024-12-18T05:21:00+01:00',
 | 
			
		||||
				arrivalDelay: null,
 | 
			
		||||
				tripId: '2|#VN#1#ST#1733173731#PI#1#ZI#153019#TA#0#DA#181224#1S#8010255#1T#11#LS#8500010#LT#1048#PU#81#RT#1#CA#ICE#ZE#101#ZB#ICE  101#PC#0#FR#8010255#FT#11#TO#8500010#TT#1048#',
 | 
			
		||||
				line: {
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					id: 'ice-101',
 | 
			
		||||
					fahrtNr: '101',
 | 
			
		||||
					name: 'ICE 101',
 | 
			
		||||
					public: true,
 | 
			
		||||
					productName: 'ICE',
 | 
			
		||||
					mode: 'train',
 | 
			
		||||
					product: 'nationalExpress',
 | 
			
		||||
					operator: {
 | 
			
		||||
						type: 'operator',
 | 
			
		||||
						id: 'db-fernverkehr-ag',
 | 
			
		||||
						name: 'DB Fernverkehr AG',
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				direction: 'KÖLN',
 | 
			
		||||
				arrivalPlatform: '16',
 | 
			
		||||
				plannedArrivalPlatform: '16',
 | 
			
		||||
				departurePlatform: '13',
 | 
			
		||||
				plannedDeparturePlatform: '13',
 | 
			
		||||
				remarks: [
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Bicycles conveyed - subject to reservation',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'bicycle-conveyance-reservation',
 | 
			
		||||
						summary: 'bicycles conveyed, subject to reservation',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Number of bicycles conveyed limited',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'bicycle-conveyance',
 | 
			
		||||
						summary: 'bicycles conveyed',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Komfort Check-in possible (visit bahn.de/kci for more information)',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'komfort-checkin',
 | 
			
		||||
						summary: 'Komfort-Checkin available',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'vehicle-mounted access aid',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'boarding-ramp',
 | 
			
		||||
						summary: 'vehicle-mounted boarding ramp available',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						code: 'text.journeystop.product.or.direction.changes.journey.message',
 | 
			
		||||
						summary: 'From Minden(Westf) as ICE 101 heading towards Basel SBB',
 | 
			
		||||
						text: 'From Minden(Westf) as ICE 101 heading towards Basel SBB',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				origin: {
 | 
			
		||||
					type: 'stop',
 | 
			
		||||
					id: '8000080',
 | 
			
		||||
					name: 'Dortmund Hbf',
 | 
			
		||||
					location: {
 | 
			
		||||
						type: 'location',
 | 
			
		||||
						id: '8000080',
 | 
			
		||||
						latitude: 51.517899,
 | 
			
		||||
						longitude: 7.459294,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				destination: {
 | 
			
		||||
					type: 'stop',
 | 
			
		||||
					id: '8000207',
 | 
			
		||||
					name: 'Köln Hbf',
 | 
			
		||||
					location: {
 | 
			
		||||
						type: 'location',
 | 
			
		||||
						id: '8000207',
 | 
			
		||||
						latitude: 50.943029,
 | 
			
		||||
						longitude: 6.95873,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				departure: '2024-12-18T05:36:00+01:00',
 | 
			
		||||
				plannedDeparture: '2024-12-18T05:36:00+01:00',
 | 
			
		||||
				departureDelay: null,
 | 
			
		||||
				arrival: '2024-12-18T06:47:00+01:00',
 | 
			
		||||
				plannedArrival: '2024-12-18T06:47:00+01:00',
 | 
			
		||||
				arrivalDelay: null,
 | 
			
		||||
				tripId: '2|#VN#1#ST#1733173731#PI#1#ZI#154039#TA#0#DA#181224#1S#8000080#1T#536#LS#8000096#LT#1024#PU#81#RT#1#CA#IC#ZE#2040#ZB#IC  2040#PC#1#FR#8000080#FT#536#TO#8000096#TT#1024#',
 | 
			
		||||
				line: {
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					id: 'ic-2040',
 | 
			
		||||
					fahrtNr: '2040',
 | 
			
		||||
					name: 'IC 2040',
 | 
			
		||||
					public: true,
 | 
			
		||||
					productName: 'IC',
 | 
			
		||||
					mode: 'train',
 | 
			
		||||
					product: 'national',
 | 
			
		||||
					operator: {
 | 
			
		||||
						type: 'operator',
 | 
			
		||||
						id: 'db-fernverkehr-ag',
 | 
			
		||||
						name: 'DB Fernverkehr AG',
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				direction: 'Stuttgart Hbf',
 | 
			
		||||
				arrivalPlatform: '7 D-G',
 | 
			
		||||
				plannedArrivalPlatform: '7 D-G',
 | 
			
		||||
				departurePlatform: '11',
 | 
			
		||||
				plannedDeparturePlatform: '11',
 | 
			
		||||
				remarks: [
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Bicycles conveyed - subject to reservation',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'bicycle-conveyance-reservation',
 | 
			
		||||
						summary: 'bicycles conveyed, subject to reservation',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Number of bicycles conveyed limited',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'bicycle-conveyance',
 | 
			
		||||
						summary: 'bicycles conveyed',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Komfort Check-in possible (visit bahn.de/kci for more information)',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'komfort-checkin',
 | 
			
		||||
						summary: 'Komfort-Checkin available',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						code: 'RZ',
 | 
			
		||||
						summary: 'Einstieg mit Rollstuhl stufenfrei',
 | 
			
		||||
						text: 'Einstieg mit Rollstuhl stufenfrei',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						text: 'Intercity 2: visit www.bahn.de/ic2 for more information',
 | 
			
		||||
						type: 'hint',
 | 
			
		||||
						code: 'intercity-2',
 | 
			
		||||
						summary: 'Intercity 2',
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
		refreshToken: 'T$A=1@O=Berlin Hbf@X=13369549@Y=52525589@L=8011160@a=128@$A=1@O=Dortmund Hbf@X=7459294@Y=51517899@L=8000080@a=128@$202412180022$202412180521$ICE  101$$1$$$$$$§T$A=1@O=Dortmund Hbf@X=7459294@Y=51517899@L=8000080@a=128@$A=1@O=Köln Hbf@X=6958730@Y=50943029@L=8000207@a=128@$202412180536$202412180647$IC  2040$$1$$$$$$',
 | 
			
		||||
		remarks: [],
 | 
			
		||||
		price: {
 | 
			
		||||
			amount: 27.99,
 | 
			
		||||
			currency: 'EUR',
 | 
			
		||||
			hint: null,
 | 
			
		||||
		},
 | 
			
		||||
		tickets: [
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Super Sparpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 2799,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Train-specific travel. No cancellations',
 | 
			
		||||
				addDataTicketDetails: 'You can use all trains indicated on your ticket. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket, see there. Your ticket cannot be cancelled.',
 | 
			
		||||
				addDataTravelInfo: undefined,
 | 
			
		||||
				firstClass: false,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Super Sparpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 3599,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Train-specific travel. No cancellations. No access to the DB Lounge',
 | 
			
		||||
				addDataTicketDetails: 'You can use all trains indicated on your ticket. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket, see there. Your ticket cannot be cancelled. Your ticket does not entitle you to use the DB Lounge.',
 | 
			
		||||
				addDataTravelInfo: 'Travel 1st class',
 | 
			
		||||
				firstClass: true,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Sparpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 3499,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Train-specific travel. Cancellation subject to a fee before first day of validity',
 | 
			
		||||
				addDataTicketDetails: 'You can use all trains indicated on your ticket. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket, see there. You can cancel your ticket up to and including 17.12.2024 for a fee of EUR 10,00. You will receive a voucher for the remaining amount. No cancellation thereafter.',
 | 
			
		||||
				addDataTravelInfo: undefined,
 | 
			
		||||
				firstClass: false,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Sparpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 4499,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Train-specific travel. Cancellation subject to a fee before first day of validity. No access to the DB Lounge',
 | 
			
		||||
				addDataTicketDetails: 'You can use all trains indicated on your ticket. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket, see there. You can cancel your ticket up to and including 17.12.2024 for a fee of EUR 10,00. You will receive a voucher for the remaining amount. No cancellation thereafter. Your ticket does not entitle you to use the DB Lounge.',
 | 
			
		||||
				addDataTravelInfo: 'Travel 1st class',
 | 
			
		||||
				firstClass: true,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Flexpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 13280,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Unrestricted choice of trains. Cancellation free of charge before first day of validity. City-Ticket',
 | 
			
		||||
				addDataTicketDetails: 'Your ICE ticket lets you take any type of train. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket; see there. You can cancel your ticket free of charge up to and including 17.12.2024. After that, cancellation is available for a fee of EUR 19,00. Your ticket includes a City-Ticket for Berlin, Tarifbereiche A und B and Köln, Stadtgebiet Köln (Tarifgebiet 2100). The City-Ticket is valid in conjunction with your long-distance ticket only when you use it for connecting services in local or regional rail passenger transport (e.g. S-Bahn, RE and RB trains) as part of the through ticket. The City-Ticket is issued together with your Super Sparpreis or Sparpreis ticket, depending on the journey you have booked.',
 | 
			
		||||
				addDataTravelInfo: undefined,
 | 
			
		||||
				firstClass: false,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				name: 'Flexpreis',
 | 
			
		||||
				priceObj: {
 | 
			
		||||
					amount: 23910,
 | 
			
		||||
					currency: 'EUR',
 | 
			
		||||
				},
 | 
			
		||||
				addData: undefined,
 | 
			
		||||
				addDataTicketInfo: 'Unrestricted choice of trains. Cancellation free of charge before first day of validity. City-Ticket. Access to the DB Lounge. Seat included',
 | 
			
		||||
				addDataTicketDetails: 'Your ICE ticket lets you take any type of train. Your ticket constitutes a continuous contract of carriage in each direction (through ticket). Should you make a passenger rights claim, the ticket will be considered in its entirety. Special rules apply to tickets including City-Ticket; see there. You can cancel your ticket free of charge up to and including 17.12.2024. After that, cancellation is available for a fee of EUR 19,00. Your ticket includes a City-Ticket for Berlin, Tarifbereiche A und B and Köln, Stadtgebiet Köln (Tarifgebiet 2100). The City-Ticket is valid in conjunction with your long-distance ticket only when you use it for connecting services in local or regional rail passenger transport (e.g. S-Bahn, RE and RB trains) as part of the through ticket. The City-Ticket is issued together with your Super Sparpreis or Sparpreis ticket, depending on the journey you have booked. Your ticket entitles you to use the DB Lounge. Your ticket includes a free seat reservation.',
 | 
			
		||||
				addDataTravelInfo: 'Travel 1st class',
 | 
			
		||||
				firstClass: true,
 | 
			
		||||
				partialFare: false,
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
	},
 | 
			
		||||
	realtimeDataUpdatedAt: null,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
	dbJourney,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										10485
									
								
								test/fixtures/db-refresh-journey.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10485
									
								
								test/fixtures/db-refresh-journey.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		
		Reference in a new issue