177 lines
4.9 KiB
TypeScript

/* eslint-disable @typescript-eslint/restrict-plus-operands */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/no-loop-func */
import { UserInputError } from '@graphql/errors'
import type { Context } from '@src/context'
const locales = ['en', 'de', 'fr', 'nl', 'it', 'es', 'pt', 'pl', 'ru']
const REQUEST_TIMEOUT = 3000
const createLocation = async (session, mapboxData) => {
const data = {
id: mapboxData.id + (mapboxData.address ? `-${mapboxData.address}` : ''),
nameEN: mapboxData.text_en,
nameDE: mapboxData.text_de,
nameFR: mapboxData.text_fr,
nameNL: mapboxData.text_nl,
nameIT: mapboxData.text_it,
nameES: mapboxData.text_es,
namePT: mapboxData.text_pt,
namePL: mapboxData.text_pl,
nameRU: mapboxData.text_ru,
type: mapboxData.id.split('.')[0].toLowerCase(),
address: mapboxData.address,
lng: mapboxData.center?.length ? mapboxData.center[0] : null,
lat: mapboxData.center?.length ? mapboxData.center[1] : null,
}
let mutation =
'MERGE (l:Location {id: $id}) ' +
'SET l.name = $nameEN, ' +
'l.nameEN = $nameEN, ' +
'l.nameDE = $nameDE, ' +
'l.nameFR = $nameFR, ' +
'l.nameNL = $nameNL, ' +
'l.nameIT = $nameIT, ' +
'l.nameES = $nameES, ' +
'l.namePT = $namePT, ' +
'l.namePL = $namePL, ' +
'l.nameRU = $nameRU, ' +
'l.type = $type'
if (data.lat && data.lng) {
mutation += ', l.lat = $lat, l.lng = $lng'
}
if (data.address) {
mutation += ', l.address = $address'
}
mutation += ' RETURN l.id'
await session.writeTransaction((transaction) => {
return transaction.run(mutation, data)
})
}
export const createOrUpdateLocations = async (
nodeLabel,
nodeId,
locationName,
session,
context: Context,
) => {
if (locationName === undefined) return
let locationId
if (locationName !== null) {
const response: any = await fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(
locationName,
)}.json?access_token=${
context.config.MAPBOX_TOKEN
}&types=region,place,country,address&language=${locales.join(',')}`,
{
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
},
)
const res = await response.json()
if (!res?.features?.[0]) {
throw new UserInputError('locationName is invalid')
}
let data
res.features.forEach((item) => {
if (item.matching_place_name === locationName) {
data = item
}
})
if (!data) {
data = res.features[0]
}
if (!data?.place_type?.length) {
throw new UserInputError('locationName is invalid')
}
if (data.place_type.length > 1) {
data.id = 'region.' + data.id.split('.')[1]
}
await createLocation(session, data)
let parent = data
if (parent.address) {
parent.id += `-${parent.address}`
}
if (data.context) {
for await (const ctx of data.context) {
await createLocation(session, ctx)
await session.writeTransaction((transaction) => {
return transaction.run(
`
MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId})
MERGE (child)<-[:IS_IN]-(parent)
RETURN child.id, parent.id
`,
{
parentId: parent.id,
childId: ctx.id,
},
)
})
parent = ctx
}
}
locationId = data.id
} else {
locationId = 'non-existent-id'
}
// delete all current locations from node and add new location
await session.writeTransaction((transaction) => {
return transaction.run(
`
MATCH (node:${nodeLabel} {id: $nodeId})
OPTIONAL MATCH (node)-[relationship:IS_IN]->(:Location)
DELETE relationship
WITH node
MATCH (location:Location {id: $locationId})
MERGE (node)-[:IS_IN]->(location)
RETURN location.id, node.id
`,
{ nodeId, locationId },
)
})
}
export const queryLocations = async ({ place, lang }, context: Context) => {
const res: any = await fetch(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${place}.json?access_token=${context.config.MAPBOX_TOKEN}&types=region,place,country&language=${lang}`,
{
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
},
)
const response = await res.json()
// Return empty array if no location found or error occurred
if (!response?.features) {
return []
}
return response.features
}