feat(oceanpark): add Ocean Park Hong Kong#130
feat(oceanpark): add Ocean Park Hong Kong#130technodisney wants to merge 1 commit intoThemeParks:mainfrom
Conversation
- Auth token (optoken) injected via @Inject; device UUID persisted 90 days via @cache; dynamic TTL from API tokenExpire field - Coordinate affine transform: fetches reference_points.json from map subdomain and projects pixel positions to lat/lng for all entities - Entities: rides, transport, shows, dining — with height restriction tags, wet rides, pregnant warning, and FastPass (paidReturnTime) - Live data: wait times + today's operating hours (rides/transport), showtimes from activityList (shows) - Schedules: 30-day park operating hours, parking, Summit zone entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cubehouse
left a comment
There was a problem hiding this comment.
Nice work — well-structured implementation with clean auth flow and good use of the decorator patterns. A few items to address:
| m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + | ||
| m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); | ||
|
|
||
| const D = det(M); |
There was a problem hiding this comment.
If the map API ever returns collinear or duplicate reference points, D will be zero and all coordinates become NaN/Infinity — which silently propagates through entity locations (the if (coords) check passes because {latitude: NaN, longitude: NaN} is truthy).
Add a guard here:
const D = det(M);
if (Math.abs(D) < 1e-10) return null;Then handle the null in getCoordinateMapEntries by falling back to no coordinates for that category.
| // ── Implementation ────────────────────────────────────────────────────────── | ||
|
|
||
| @destinationController({category: 'Ocean Park'}) | ||
| @config |
There was a problem hiding this comment.
The @config class decorator is not needed here — @destinationController already applies @config internally (see destinationRegistry.ts line 208). Having both double-wraps the class in config proxies. Remove this line.
| const coeffs = computeAffineTransform(refPoints); | ||
| const entries: [string, {latitude: number; longitude: number}][] = []; | ||
|
|
||
| for (const category of MAP_CATEGORIES) { |
There was a problem hiding this comment.
These 6 map category fetches are independent but awaited sequentially. With the HTTP queue rate limit (250ms), this adds ~1.5s of wall time on a cold cache.
Use Promise.all to parallelize:
const responses = await Promise.all(
MAP_CATEGORIES.map(cat => this.fetchMapCategoryData(cat))
);
for (const resp of responses) {
const entities: OceanParkMapEntity[] = await resp.json();
// ...
}|
Hey @technodisney — just checking in on this. There are 3 inline review comments from April 8 that still need addressing:
Let us know if you need any help with these or if you have questions! |
Summary
Adds Ocean Park Hong Kong as a new destination.
Single destination, one park. No API credentials required — the park's mobile API uses a short-lived bearer token obtained from a public endpoint.
Implementation
Auth
optokenheader injected via@injecton all requests tosop.oceanpark.com.hk@cachetokenExpirefield in the API responseCoordinates
reference_points.jsonfrommap.oceanpark.com.hkprovides pixel→lat/lng anchor pointsEntities
sortId: 8), transport (sortId: 7), shows (sortId: 15), dining (sortId: 17)paidReturnTime)Live data
pflowInfo)activityListon entity detail endpointSchedules
Test plan
npm run dev -- oceanparkhongkong— entities, live data, and schedules return datanpm run dev -- oceanparkhongkong --ignore-cache— fresh fetch works correctlynpm test— existing tests unaffected🤖 Generated with Claude Code