diff --git a/src/helpers.ts b/src/helpers.ts index 5bbb118..b51cfb2 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -14,22 +14,22 @@ export function distanceKm(lat1: number, lon1: number, lat2: number, lon2: numbe export type ArangoDoc = Record export interface RouteStage { - from_uuid: string | null - from_name: string | null - from_lat: number | null - from_lon: number | null - to_uuid: string | null - to_name: string | null - to_lat: number | null - to_lon: number | null - distance_km: number - travel_time_seconds: number - transport_type: string | null + fromUuid: string | null + fromName: string | null + fromLat: number | null + fromLon: number | null + toUuid: string | null + toName: string | null + toLat: number | null + toLon: number | null + distanceKm: number + travelTimeSeconds: number + transportType: string | null } export interface RoutePath { - total_distance_km: number - total_time_seconds: number + totalDistanceKm: number + totalTimeSeconds: number stages: RouteStage[] } @@ -37,17 +37,17 @@ function buildStage(fromDoc: ArangoDoc | undefined, toDoc: ArangoDoc | undefined const distance = edges.reduce((s, e) => s + (e.distance_km || 0), 0) const time = edges.reduce((s, e) => s + (e.travel_time_seconds || 0), 0) return { - from_uuid: fromDoc?._key ?? null, - from_name: fromDoc?.name ?? null, - from_lat: fromDoc?.latitude ?? null, - from_lon: fromDoc?.longitude ?? null, - to_uuid: toDoc?._key ?? null, - to_name: toDoc?.name ?? null, - to_lat: toDoc?.latitude ?? null, - to_lon: toDoc?.longitude ?? null, - distance_km: distance, - travel_time_seconds: time, - transport_type: transportType, + fromUuid: fromDoc?._key ?? null, + fromName: fromDoc?.name ?? null, + fromLat: fromDoc?.latitude ?? null, + fromLon: fromDoc?.longitude ?? null, + toUuid: toDoc?._key ?? null, + toName: toDoc?.name ?? null, + toLat: toDoc?.latitude ?? null, + toLon: toDoc?.longitude ?? null, + distanceKm: distance, + travelTimeSeconds: time, + transportType: transportType, } } @@ -83,8 +83,8 @@ export function buildRouteFromEdges(pathEdges: [string, string, ArangoDoc][], no stages.push(buildStage(nodeDocs.get(segmentStart!), nodeDocs.get(lastTo), currentType!, currentEdges)) return { - total_distance_km: stages.reduce((s, st) => s + (st.distance_km || 0), 0), - total_time_seconds: stages.reduce((s, st) => s + (st.travel_time_seconds || 0), 0), + totalDistanceKm: stages.reduce((s, st) => s + (st.distanceKm || 0), 0), + totalTimeSeconds: stages.reduce((s, st) => s + (st.travelTimeSeconds || 0), 0), stages, } } diff --git a/src/schema.ts b/src/schema.ts index 5f719b3..cea4cba 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -8,13 +8,13 @@ const MAX_EXPANSIONS = 20000 export const typeDefs = `#graphql type Edge { - to_uuid: String - to_name: String - to_latitude: Float - to_longitude: Float - distance_km: Float - travel_time_seconds: Int - transport_type: String + toUuid: String + toName: String + toLatitude: Float + toLongitude: Float + distanceKm: Float + travelTimeSeconds: Int + transportType: String } type Node { @@ -23,51 +23,51 @@ export const typeDefs = `#graphql latitude: Float longitude: Float country: String - country_code: String - synced_at: String - transport_types: [String] + countryCode: String + syncedAt: String + transportTypes: [String] edges: [Edge] - distance_km: Float + distanceKm: Float } type NodeConnections { hub: Node - rail_node: Node - auto_edges: [Edge] - rail_edges: [Edge] + railNode: Node + autoEdges: [Edge] + railEdges: [Edge] } type Route { - distance_km: Float + distanceKm: Float geometry: JSON } type RouteStage { - from_uuid: String - from_name: String - from_lat: Float - from_lon: Float - to_uuid: String - to_name: String - to_lat: Float - to_lon: Float - distance_km: Float - travel_time_seconds: Int - transport_type: String + fromUuid: String + fromName: String + fromLat: Float + fromLon: Float + toUuid: String + toName: String + toLat: Float + toLon: Float + distanceKm: Float + travelTimeSeconds: Int + transportType: String } type RoutePath { - total_distance_km: Float - total_time_seconds: Int + totalDistanceKm: Float + totalTimeSeconds: Int stages: [RouteStage] } type ProductRouteOption { - source_uuid: String - source_name: String - source_lat: Float - source_lon: Float - distance_km: Float + sourceUuid: String + sourceName: String + sourceLat: Float + sourceLon: Float + distanceKm: Float routes: [RoutePath] } @@ -76,14 +76,14 @@ export const typeDefs = `#graphql latitude: Float longitude: Float count: Int - expansion_zoom: Int + expansionZoom: Int name: String } type Product { uuid: String name: String - offers_count: Int + offersCount: Int } type Supplier { @@ -91,41 +91,41 @@ export const typeDefs = `#graphql name: String latitude: Float longitude: Float - distance_km: Float + distanceKm: Float } type OfferNode { uuid: String - product_uuid: String - product_name: String - supplier_uuid: String - supplier_name: String + productUuid: String + productName: String + supplierUuid: String + supplierName: String latitude: Float longitude: Float country: String - country_code: String - price_per_unit: String + countryCode: String + pricePerUnit: String currency: String quantity: String unit: String - distance_km: Float + distanceKm: Float } type OfferWithRoute { uuid: String - product_uuid: String - product_name: String - supplier_uuid: String - supplier_name: String + productUuid: String + productName: String + supplierUuid: String + supplierName: String latitude: Float longitude: Float country: String - country_code: String - price_per_unit: String + countryCode: String + pricePerUnit: String currency: String quantity: String unit: String - distance_km: Float + distanceKm: Float routes: [RoutePath] } @@ -133,32 +133,32 @@ export const typeDefs = `#graphql type Query { node(uuid: String!): Node - nodes(limit: Int, offset: Int, transport_type: String, country: String, search: String, west: Float, south: Float, east: Float, north: Float): [Node!]! - nodes_count(transport_type: String, country: String, west: Float, south: Float, east: Float, north: Float): Int! - hub_countries: [String!]! - nearest_nodes(lat: Float!, lon: Float!, limit: Int): [Node!]! - node_connections(uuid: String!, limit_auto: Int, limit_rail: Int): NodeConnections - auto_route(from_lat: Float!, from_lon: Float!, to_lat: Float!, to_lon: Float!): Route - rail_route(from_lat: Float!, from_lon: Float!, to_lat: Float!, to_lon: Float!): Route - clustered_nodes(west: Float!, south: Float!, east: Float!, north: Float!, zoom: Int!, transport_type: String, node_type: String): [ClusterPoint!]! + nodes(limit: Int, offset: Int, transportType: String, country: String, search: String, west: Float, south: Float, east: Float, north: Float): [Node!]! + nodesCount(transportType: String, country: String, west: Float, south: Float, east: Float, north: Float): Int! + hubCountries: [String!]! + nearestNodes(lat: Float!, lon: Float!, limit: Int): [Node!]! + nodeConnections(uuid: String!, limitAuto: Int, limitRail: Int): NodeConnections + autoRoute(fromLat: Float!, fromLon: Float!, toLat: Float!, toLon: Float!): Route + railRoute(fromLat: Float!, fromLon: Float!, toLat: Float!, toLon: Float!): Route + clusteredNodes(west: Float!, south: Float!, east: Float!, north: Float!, zoom: Int!, transportType: String, nodeType: String): [ClusterPoint!]! products: [Product!]! - offers_by_product(product_uuid: String!): [OfferNode!]! - hubs_near_offer(offer_uuid: String!, limit: Int): [Node!]! + offersByProduct(productUuid: String!): [OfferNode!]! + hubsNearOffer(offerUuid: String!, limit: Int): [Node!]! suppliers: [Supplier!]! - products_by_supplier(supplier_uuid: String!): [Product!]! - offers_by_supplier_product(supplier_uuid: String!, product_uuid: String!): [OfferNode!]! - products_near_hub(hub_uuid: String!, radius_km: Float): [Product!]! - suppliers_for_product(product_uuid: String!): [Supplier!]! - hubs_for_product(product_uuid: String!, radius_km: Float): [Node!]! - offers_by_hub(hub_uuid: String!, product_uuid: String!, limit: Int): [ProductRouteOption!]! - offer_to_hub(offer_uuid: String!, hub_uuid: String!): ProductRouteOption - nearest_hubs(lat: Float!, lon: Float!, radius: Float, product_uuid: String, limit: Int): [Node!]! - nearest_offers(lat: Float!, lon: Float!, radius: Float, product_uuid: String, hub_uuid: String, limit: Int): [OfferWithRoute!]! - nearest_suppliers(lat: Float!, lon: Float!, radius: Float, product_uuid: String, limit: Int): [Supplier!]! - route_to_coordinate(offer_uuid: String!, lat: Float!, lon: Float!): ProductRouteOption - hubs_list(limit: Int, offset: Int, country: String, transport_type: String, west: Float, south: Float, east: Float, north: Float): [Node!]! - suppliers_list(limit: Int, offset: Int, country: String, west: Float, south: Float, east: Float, north: Float): [Supplier!]! - products_list(limit: Int, offset: Int, west: Float, south: Float, east: Float, north: Float): [Product!]! + productsBySupplier(supplierUuid: String!): [Product!]! + offersBySupplierProduct(supplierUuid: String!, productUuid: String!): [OfferNode!]! + productsNearHub(hubUuid: String!, radiusKm: Float): [Product!]! + suppliersForProduct(productUuid: String!): [Supplier!]! + hubsForProduct(productUuid: String!, radiusKm: Float): [Node!]! + offersByHub(hubUuid: String!, productUuid: String!, limit: Int): [ProductRouteOption!]! + offerToHub(offerUuid: String!, hubUuid: String!): ProductRouteOption + nearestHubs(lat: Float!, lon: Float!, radius: Float, productUuid: String, limit: Int): [Node!]! + nearestOffers(lat: Float!, lon: Float!, radius: Float, productUuid: String, hubUuid: String, limit: Int): [OfferWithRoute!]! + nearestSuppliers(lat: Float!, lon: Float!, radius: Float, productUuid: String, limit: Int): [Supplier!]! + routeToCoordinate(offerUuid: String!, lat: Float!, lon: Float!): ProductRouteOption + hubsList(limit: Int, offset: Int, country: String, transportType: String, west: Float, south: Float, east: Float, north: Float): [Node!]! + suppliersList(limit: Int, offset: Int, country: String, west: Float, south: Float, east: Float, north: Float): [Supplier!]! + productsList(limit: Int, offset: Int, west: Float, south: Float, east: Float, north: Float): [Product!]! } ` @@ -169,30 +169,42 @@ function mapNode(doc: ArangoDoc, includeEdges = false) { latitude: doc.latitude ?? null, longitude: doc.longitude ?? null, country: doc.country ?? null, - country_code: doc.country_code ?? null, - synced_at: doc.synced_at ?? null, - transport_types: doc.transport_types || [], + countryCode: doc.country_code ?? null, + syncedAt: doc.synced_at ?? null, + transportTypes: doc.transport_types || [], edges: includeEdges ? (doc.edges || []) : [], - distance_km: doc.distance_km ?? null, + distanceKm: doc.distance_km ?? null, } } function mapOffer(doc: ArangoDoc) { return { uuid: doc._key, - product_uuid: doc.product_uuid ?? null, - product_name: doc.product_name ?? null, - supplier_uuid: doc.supplier_uuid ?? null, - supplier_name: doc.supplier_name ?? null, + productUuid: doc.product_uuid ?? null, + productName: doc.product_name ?? null, + supplierUuid: doc.supplier_uuid ?? null, + supplierName: doc.supplier_name ?? null, latitude: doc.latitude ?? null, longitude: doc.longitude ?? null, country: doc.country ?? null, - country_code: doc.country_code ?? null, - price_per_unit: doc.price_per_unit ?? null, + countryCode: doc.country_code ?? null, + pricePerUnit: doc.price_per_unit ?? null, currency: doc.currency ?? null, quantity: doc.quantity ?? null, unit: doc.unit ?? null, - distance_km: doc.distance_km ?? null, + distanceKm: doc.distance_km ?? null, + } +} + +function mapEdge(doc: ArangoDoc) { + return { + toUuid: doc.to_uuid ?? doc._key ?? null, + toName: doc.to_name ?? doc.name ?? null, + toLatitude: doc.to_latitude ?? doc.latitude ?? null, + toLongitude: doc.to_longitude ?? doc.longitude ?? null, + distanceKm: doc.distance_km ?? null, + travelTimeSeconds: doc.travel_time_seconds ?? null, + transportType: doc.transport_type ?? null, } } @@ -314,11 +326,11 @@ async function resolveOfferToHubInternal(offerUuid: string, hubUuid: string): Pr } return { - source_uuid: offerUuid, - source_name: offer.name || offer.product_name, - source_lat: offerLat, - source_lon: offerLon, - distance_km: dm, + sourceUuid: offerUuid, + sourceName: offer.name || offer.product_name, + sourceLat: offerLat, + sourceLon: offerLon, + distanceKm: dm, routes: route ? [route] : [], } } @@ -360,7 +372,7 @@ export const resolvers = { }, Product: { - offers_count: async (parent: { uuid: string }) => { + offersCount: async (parent: { uuid: string }) => { const db = getDb() try { const cursor = await db.query(` @@ -399,10 +411,10 @@ export const resolvers = { `, { from_id: `nodes/${args.uuid}` }) const edges = await cursor.all() - return { ...mapNode(node, true), edges } + return { ...mapNode(node, true), edges: edges.map(mapEdge) } }, - nodes: async (_: unknown, args: { limit?: number; offset?: number; transport_type?: string; country?: string; search?: string; west?: number; south?: number; east?: number; north?: number }) => { + nodes: async (_: unknown, args: { limit?: number; offset?: number; transportType?: string; country?: string; search?: string; west?: number; south?: number; east?: number; north?: number }) => { const db = getDb() const bounds = boundsFilter(args) @@ -418,7 +430,7 @@ export const resolvers = { LIMIT @offset, @limit RETURN node `, { - transport_type: args.transport_type ?? null, + transport_type: args.transportType ?? null, country: args.country ?? null, search: args.search ?? null, offset: args.offset ?? 0, @@ -429,7 +441,7 @@ export const resolvers = { return nodes.map((n: ArangoDoc) => mapNode(n)) }, - nodes_count: async (_: unknown, args: { transport_type?: string; country?: string; west?: number; south?: number; east?: number; north?: number }) => { + nodesCount: async (_: unknown, args: { transportType?: string; country?: string; west?: number; south?: number; east?: number; north?: number }) => { const db = getDb() const bounds = boundsFilter(args) @@ -442,11 +454,11 @@ export const resolvers = { ${bounds.filter} COLLECT WITH COUNT INTO length RETURN length - `, { transport_type: args.transport_type ?? null, country: args.country ?? null, ...bounds.vars }) + `, { transport_type: args.transportType ?? null, country: args.country ?? null, ...bounds.vars }) return (await cursor.next()) ?? 0 }, - hub_countries: async () => { + hubCountries: async () => { const db = getDb() const cursor = await db.query(` FOR node IN nodes @@ -459,7 +471,7 @@ export const resolvers = { return cursor.all() }, - nearest_nodes: async (_: unknown, args: { lat: number; lon: number; limit?: number }) => { + nearestNodes: async (_: unknown, args: { lat: number; lon: number; limit?: number }) => { const db = getDb() const cursor = await db.query(` FOR node IN nodes @@ -474,7 +486,7 @@ export const resolvers = { return nodes.map((n: ArangoDoc) => mapNode(n)) }, - node_connections: async (_: unknown, args: { uuid: string; limit_auto?: number; limit_rail?: number }) => { + nodeConnections: async (_: unknown, args: { uuid: string; limitAuto?: number; limitRail?: number }) => { const db = getDb() const nodesCol = db.collection('nodes') const hub = await nodesCol.document(args.uuid).catch(() => null) @@ -524,8 +536,8 @@ export const resolvers = { hub_lat: hub.latitude, hub_lon: hub.longitude, hub_has_rail: (hub.transport_types || []).includes('rail'), - limit_auto: args.limit_auto ?? 12, - limit_rail: args.limit_rail ?? 12, + limit_auto: args.limitAuto ?? 12, + limit_rail: args.limitRail ?? 12, }) const result = await cursor.next() @@ -533,16 +545,16 @@ export const resolvers = { return { hub: result.hub ? mapNode(result.hub) : null, - rail_node: result.rail_node ? mapNode(result.rail_node) : null, - auto_edges: result.auto_edges || [], - rail_edges: result.rail_edges || [], + railNode: result.rail_node ? mapNode(result.rail_node) : null, + autoEdges: (result.auto_edges || []).map(mapEdge), + railEdges: (result.rail_edges || []).map(mapEdge), } }, - auto_route: async (_: unknown, args: { from_lat: number; from_lon: number; to_lat: number; to_lon: number }) => { + autoRoute: async (_: unknown, args: { fromLat: number; fromLon: number; toLat: number; toLon: number }) => { const url = new URL('/route', GRAPHHOPPER_URL) - url.searchParams.append('point', `${args.from_lat},${args.from_lon}`) - url.searchParams.append('point', `${args.to_lat},${args.to_lon}`) + url.searchParams.append('point', `${args.fromLat},${args.fromLon}`) + url.searchParams.append('point', `${args.toLat},${args.toLon}`) url.searchParams.append('profile', 'car') url.searchParams.append('instructions', 'false') url.searchParams.append('calc_points', 'true') @@ -553,16 +565,16 @@ export const resolvers = { const data = await res.json() as ArangoDoc if (data.paths?.length > 0) { const path = data.paths[0] - return { distance_km: Math.round((path.distance || 0) / 10) / 100, geometry: path.points?.coordinates || [] } + return { distanceKm: Math.round((path.distance || 0) / 10) / 100, geometry: path.points?.coordinates || [] } } } catch (e) { console.error('GraphHopper request failed:', e) } return null }, - rail_route: async (_: unknown, args: { from_lat: number; from_lon: number; to_lat: number; to_lon: number }) => { + railRoute: async (_: unknown, args: { fromLat: number; fromLon: number; toLat: number; toLon: number }) => { const url = new URL('/route', OPENRAILROUTING_URL) - url.searchParams.append('point', `${args.from_lat},${args.from_lon}`) - url.searchParams.append('point', `${args.to_lat},${args.to_lon}`) + url.searchParams.append('point', `${args.fromLat},${args.fromLon}`) + url.searchParams.append('point', `${args.toLat},${args.toLon}`) url.searchParams.append('profile', 'all_tracks') url.searchParams.append('calc_points', 'true') url.searchParams.append('points_encoded', 'false') @@ -572,14 +584,22 @@ export const resolvers = { const data = await res.json() as ArangoDoc if (data.paths?.length > 0) { const path = data.paths[0] - return { distance_km: Math.round((path.distance || 0) / 10) / 100, geometry: path.points?.coordinates || [] } + return { distanceKm: Math.round((path.distance || 0) / 10) / 100, geometry: path.points?.coordinates || [] } } } catch (e) { console.error('OpenRailRouting request failed:', e) } return null }, - clustered_nodes: async (_: unknown, args: { west: number; south: number; east: number; north: number; zoom: number; transport_type?: string; node_type?: string }) => { - return getClusteredNodes(args.west, args.south, args.east, args.north, args.zoom, args.transport_type, args.node_type) + clusteredNodes: async (_: unknown, args: { west: number; south: number; east: number; north: number; zoom: number; transportType?: string; nodeType?: string }) => { + const points = await getClusteredNodes(args.west, args.south, args.east, args.north, args.zoom, args.transportType, args.nodeType) + return points.map((p: ArangoDoc) => ({ + id: p.id, + latitude: p.latitude, + longitude: p.longitude, + count: p.count, + expansionZoom: p.expansion_zoom ?? p.expansionZoom ?? null, + name: p.name ?? null, + })) }, products: async () => { @@ -595,22 +615,22 @@ export const resolvers = { return cursor.all() }, - offers_by_product: async (_: unknown, args: { product_uuid: string }) => { + offersByProduct: async (_: unknown, args: { productUuid: string }) => { const db = getDb() const cursor = await db.query(` FOR node IN nodes FILTER node.node_type == 'offer' FILTER node.product_uuid == @product_uuid RETURN node - `, { product_uuid: args.product_uuid }) + `, { product_uuid: args.productUuid }) const nodes = await cursor.all() return nodes.map(mapOffer) }, - hubs_near_offer: async (_: unknown, args: { offer_uuid: string; limit?: number }) => { + hubsNearOffer: async (_: unknown, args: { offerUuid: string; limit?: number }) => { const db = getDb() const nodesCol = db.collection('nodes') - const offer = await nodesCol.document(args.offer_uuid).catch(() => null) + const offer = await nodesCol.document(args.offerUuid).catch(() => null) if (!offer || offer.latitude == null || offer.longitude == null) return [] const cursor = await db.query(` @@ -639,7 +659,7 @@ export const resolvers = { return cursor.all() }, - products_by_supplier: async (_: unknown, args: { supplier_uuid: string }) => { + productsBySupplier: async (_: unknown, args: { supplierUuid: string }) => { const db = getDb() const cursor = await db.query(` FOR node IN nodes @@ -649,11 +669,11 @@ export const resolvers = { COLLECT product_uuid = node.product_uuid INTO offers LET first_offer = FIRST(offers).node RETURN { uuid: product_uuid, name: first_offer.product_name } - `, { supplier_uuid: args.supplier_uuid }) + `, { supplier_uuid: args.supplierUuid }) return cursor.all() }, - offers_by_supplier_product: async (_: unknown, args: { supplier_uuid: string; product_uuid: string }) => { + offersBySupplierProduct: async (_: unknown, args: { supplierUuid: string; productUuid: string }) => { const db = getDb() const cursor = await db.query(` FOR node IN nodes @@ -661,15 +681,15 @@ export const resolvers = { FILTER node.supplier_uuid == @supplier_uuid FILTER node.product_uuid == @product_uuid RETURN node - `, { supplier_uuid: args.supplier_uuid, product_uuid: args.product_uuid }) + `, { supplier_uuid: args.supplierUuid, product_uuid: args.productUuid }) const nodes = await cursor.all() return nodes.map(mapOffer) }, - products_near_hub: async (_: unknown, args: { hub_uuid: string; radius_km?: number }) => { + productsNearHub: async (_: unknown, args: { hubUuid: string; radiusKm?: number }) => { const db = getDb() const nodesCol = db.collection('nodes') - const hub = await nodesCol.document(args.hub_uuid).catch(() => null) + const hub = await nodesCol.document(args.hubUuid).catch(() => null) if (!hub || hub.latitude == null || hub.longitude == null) return [] const cursor = await db.query(` @@ -682,11 +702,11 @@ export const resolvers = { COLLECT product_uuid = node.product_uuid INTO offers LET first_offer = FIRST(offers).node RETURN { uuid: product_uuid, name: first_offer.product_name } - `, { lat: hub.latitude, lon: hub.longitude, radius_km: args.radius_km ?? 500 }) + `, { lat: hub.latitude, lon: hub.longitude, radius_km: args.radiusKm ?? 500 }) return cursor.all() }, - suppliers_for_product: async (_: unknown, args: { product_uuid: string }) => { + suppliersForProduct: async (_: unknown, args: { productUuid: string }) => { const db = getDb() const cursor = await db.query(` FOR node IN nodes @@ -695,11 +715,11 @@ export const resolvers = { FILTER node.supplier_uuid != null COLLECT supplier_uuid = node.supplier_uuid RETURN { uuid: supplier_uuid } - `, { product_uuid: args.product_uuid }) + `, { product_uuid: args.productUuid }) return cursor.all() }, - hubs_for_product: async (_: unknown, args: { product_uuid: string; radius_km?: number }) => { + hubsForProduct: async (_: unknown, args: { productUuid: string; radiusKm?: number }) => { const db = getDb() const cursor = await db.query(` FOR offer IN nodes @@ -719,17 +739,17 @@ export const resolvers = { uuid: hub_uuid, name: hub_name, latitude: hub_lat, longitude: hub_lon, country: hub_country, country_code: hub_country_code, transport_types: hub_transport } - `, { product_uuid: args.product_uuid, radius_km: args.radius_km ?? 500 }) + `, { product_uuid: args.productUuid, radius_km: args.radiusKm ?? 500 }) const hubs = await cursor.all() return hubs.map((h: ArangoDoc) => ({ ...mapNode(h), uuid: h.uuid })) }, - offers_by_hub: async (_: unknown, args: { hub_uuid: string; product_uuid: string; limit?: number }) => { + offersByHub: async (_: unknown, args: { hubUuid: string; productUuid: string; limit?: number }) => { const db = getDb() await ensureGraph() const nodesCol = db.collection('nodes') - const hub = await nodesCol.document(args.hub_uuid).catch(() => null) + const hub = await nodesCol.document(args.hubUuid).catch(() => null) if (!hub || hub.latitude == null || hub.longitude == null) return [] const hubLat = hub.latitude as number @@ -737,11 +757,11 @@ export const resolvers = { const limit = args.limit ?? 10 // Priority queue: [cost, seq, nodeKey, phase] - const queue: [number, number, string, Phase][] = [[0, 0, args.hub_uuid, 'end_auto']] + const queue: [number, number, string, Phase][] = [[0, 0, args.hubUuid, 'end_auto']] let counter = 0 const visited = new Map() const predecessors = new Map() - const nodeDocs = new Map([[args.hub_uuid, hub]]) + const nodeDocs = new Map([[args.hubUuid, hub]]) const foundRoutes: ArangoDoc[] = [] let expansions = 0 @@ -753,7 +773,7 @@ export const resolvers = { if (visited.has(stateKey) && cost > visited.get(stateKey)!) continue const nodeDoc = nodeDocs.get(nodeKey) - if (nodeDoc && nodeDoc.product_uuid === args.product_uuid) { + if (nodeDoc && nodeDoc.product_uuid === args.productUuid) { const pathEdges: [string, string, ArangoDoc][] = [] let curState = stateKey let curKey = nodeKey @@ -772,11 +792,11 @@ export const resolvers = { if (srcLat != null && srcLon != null) dm = distanceKm(srcLat, srcLon, hubLat, hubLon) foundRoutes.push({ - source_uuid: nodeKey, - source_name: nodeDoc.name || nodeDoc.product_name, - source_lat: srcLat, - source_lon: srcLon, - distance_km: dm, + sourceUuid: nodeKey, + sourceName: nodeDoc.name || nodeDoc.product_name, + sourceLat: srcLat, + sourceLon: srcLon, + distanceKm: dm, routes: route ? [route] : [], }) continue @@ -810,11 +830,11 @@ export const resolvers = { return foundRoutes }, - offer_to_hub: async (_: unknown, args: { offer_uuid: string; hub_uuid: string }) => { - return resolveOfferToHubInternal(args.offer_uuid, args.hub_uuid) + offerToHub: async (_: unknown, args: { offerUuid: string; hubUuid: string }) => { + return resolveOfferToHubInternal(args.offerUuid, args.hubUuid) }, - nearest_hubs: async (_: unknown, args: { lat: number; lon: number; radius?: number; product_uuid?: string; limit?: number }) => { + nearestHubs: async (_: unknown, args: { lat: number; lon: number; radius?: number; productUuid?: string; limit?: number }) => { const db = getDb() const radius = args.radius ?? 1000 const limit = args.limit ?? 12 @@ -822,7 +842,7 @@ export const resolvers = { let aql: string let bindVars: Record - if (args.product_uuid) { + if (args.productUuid) { aql = ` FOR offer IN nodes FILTER offer.node_type == 'offer' @@ -843,7 +863,7 @@ export const resolvers = { LIMIT @limit RETURN MERGE(first_hub, {_key: hub_uuid, distance_km: hub_dist}) ` - bindVars = { lat: args.lat, lon: args.lon, radius, product_uuid: args.product_uuid, limit } + bindVars = { lat: args.lat, lon: args.lon, radius, product_uuid: args.productUuid, limit } } else { aql = ` FOR hub IN nodes @@ -864,7 +884,7 @@ export const resolvers = { return hubs.map((n: ArangoDoc) => mapNode(n)) }, - nearest_offers: async (_: unknown, args: { lat: number; lon: number; radius?: number; product_uuid?: string; hub_uuid?: string; limit?: number }) => { + nearestOffers: async (_: unknown, args: { lat: number; lon: number; radius?: number; productUuid?: string; hubUuid?: string; limit?: number }) => { const db = getDb() await ensureGraph() const radius = args.radius ?? 500 @@ -876,7 +896,7 @@ export const resolvers = { FILTER offer.product_uuid != null FILTER offer.latitude != null AND offer.longitude != null ` - if (args.product_uuid) aql += ` FILTER offer.product_uuid == @product_uuid\n` + if (args.productUuid) aql += ` FILTER offer.product_uuid == @product_uuid\n` aql += ` LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000 FILTER dist <= @radius @@ -886,7 +906,7 @@ export const resolvers = { ` const bindVars: Record = { lat: args.lat, lon: args.lon, radius, limit } - if (args.product_uuid) bindVars.product_uuid = args.product_uuid + if (args.productUuid) bindVars.product_uuid = args.productUuid const cursor = await db.query(aql, bindVars) const offerNodes = await cursor.all() @@ -894,8 +914,8 @@ export const resolvers = { const offers = [] for (const node of offerNodes) { let routes: RoutePath[] = [] - if (args.hub_uuid) { - const routeResult = await resolveOfferToHubInternal(node._key, args.hub_uuid) + if (args.hubUuid) { + const routeResult = await resolveOfferToHubInternal(node._key, args.hubUuid) if (routeResult?.routes) routes = routeResult.routes as RoutePath[] } offers.push({ ...mapOffer(node), routes }) @@ -903,7 +923,7 @@ export const resolvers = { return offers }, - nearest_suppliers: async (_: unknown, args: { lat: number; lon: number; radius?: number; product_uuid?: string; limit?: number }) => { + nearestSuppliers: async (_: unknown, args: { lat: number; lon: number; radius?: number; productUuid?: string; limit?: number }) => { const db = getDb() const radius = args.radius ?? 1000 const limit = args.limit ?? 12 @@ -914,7 +934,7 @@ export const resolvers = { FILTER offer.supplier_uuid != null FILTER offer.latitude != null AND offer.longitude != null ` - if (args.product_uuid) aql += ` FILTER offer.product_uuid == @product_uuid\n` + if (args.productUuid) aql += ` FILTER offer.product_uuid == @product_uuid\n` aql += ` LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000 FILTER dist <= @radius @@ -934,18 +954,18 @@ export const resolvers = { name: supplier_node != null ? supplier_node.name : first_offer.supplier_name, latitude: supplier_node != null ? supplier_node.latitude : first_offer.latitude, longitude: supplier_node != null ? supplier_node.longitude : first_offer.longitude, - distance_km: supplier_dist + distanceKm: supplier_dist } ` const bindVars: Record = { lat: args.lat, lon: args.lon, radius, limit } - if (args.product_uuid) bindVars.product_uuid = args.product_uuid + if (args.productUuid) bindVars.product_uuid = args.productUuid const cursor = await db.query(aql, bindVars) return cursor.all() }, - route_to_coordinate: async (_: unknown, args: { offer_uuid: string; lat: number; lon: number }) => { + routeToCoordinate: async (_: unknown, args: { offerUuid: string; lat: number; lon: number }) => { const db = getDb() const cursor = await db.query(` FOR hub IN nodes @@ -960,10 +980,10 @@ export const resolvers = { const hubs = await cursor.all() if (!hubs.length) return null - return resolveOfferToHubInternal(args.offer_uuid, hubs[0]._key) + return resolveOfferToHubInternal(args.offerUuid, hubs[0]._key) }, - hubs_list: async (_: unknown, args: { limit?: number; offset?: number; country?: string; transport_type?: string; west?: number; south?: number; east?: number; north?: number }) => { + hubsList: async (_: unknown, args: { limit?: number; offset?: number; country?: string; transportType?: string; west?: number; south?: number; east?: number; north?: number }) => { const db = getDb() const bounds = boundsFilter(args) @@ -978,12 +998,12 @@ export const resolvers = { SORT node.name ASC LIMIT @offset, @limit RETURN node - `, { transport_type: args.transport_type ?? null, country: args.country ?? null, offset: args.offset ?? 0, limit: args.limit ?? 50, ...bounds.vars }) + `, { transport_type: args.transportType ?? null, country: args.country ?? null, offset: args.offset ?? 0, limit: args.limit ?? 50, ...bounds.vars }) const nodes = await cursor.all() return nodes.map((n: ArangoDoc) => mapNode(n)) }, - suppliers_list: async (_: unknown, args: { limit?: number; offset?: number; country?: string; west?: number; south?: number; east?: number; north?: number }) => { + suppliersList: async (_: unknown, args: { limit?: number; offset?: number; country?: string; west?: number; south?: number; east?: number; north?: number }) => { const db = getDb() const bounds = boundsFilter(args) @@ -1002,11 +1022,11 @@ export const resolvers = { name: n.name ?? null, latitude: n.latitude ?? null, longitude: n.longitude ?? null, - distance_km: null, + distanceKm: null, })) }, - products_list: async (_: unknown, args: { limit?: number; offset?: number; west?: number; south?: number; east?: number; north?: number }) => { + productsList: async (_: unknown, args: { limit?: number; offset?: number; west?: number; south?: number; east?: number; north?: number }) => { const db = getDb() const bounds = boundsFilter(args)