Преглед изворни кода

Let Openapi Automotive lookups skip the car search (#466)

* Pin Node to 22.23.4 for the RCE fix

* Keep the user's attach-PDF choice when the default loads late

* Let Openapi Automotive lookups skip the car search
Bernt Christian Egeland пре 6 дана
родитељ
комит
5052cd5eb0

+ 4 - 2
messages/de/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Land",
         "countryHelp": "Das zu durchsuchende Register. Französische und italienische Kennzeichen sehen gleich aus, das Land lässt sich daher nicht am Kennzeichen ablesen.",
-        "bikes": "Auch Motorräder abfragen",
-        "bikesHelp": "Ist ein Kennzeichen kein Pkw, wird zusätzlich das Motorradregister gefragt. Ein Fehltreffer beim Pkw ist kostenlos, es entstehen also nur Kosten, wenn ein Motorrad gefunden wird. Für Portugal nicht verfügbar."
+        "cars": "Pkw abfragen",
+        "carsHelp": "Das Pkw-Register durchsuchen. Schalten Sie dies aus, wenn Sie nur Motorräder betreuen, dann entfällt die Pkw-Suche und die Abfrage antwortet schneller.",
+        "bikes": "Motorräder abfragen",
+        "bikesHelp": "Das Motorradregister durchsuchen. Ist auch Pkw eingeschaltet, wird es nur gefragt, wenn kein Pkw passt, und ein Fehltreffer beim Pkw ist kostenlos. Für Portugal nicht verfügbar."
       }
     },
     "rdw": {

+ 4 - 2
messages/en/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Country",
         "countryHelp": "The registry to search. French and Italian plates look the same, so the country cannot be read off the plate.",
-        "bikes": "Also look up motorcycles",
-        "bikesHelp": "When a plate is not a car, ask the motorcycle registry too. A miss on the car side is free, so this costs nothing extra unless a motorcycle is found. Not available for Portugal."
+        "cars": "Look up cars",
+        "carsHelp": "Search the car registry. Turn this off if you only work on motorcycles, so a lookup skips the car search and answers faster.",
+        "bikes": "Look up motorcycles",
+        "bikesHelp": "Search the motorcycle registry. With cars on too, it is asked only when no car matches, and a miss on the car side is free. Not available for Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/es/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "País",
         "countryHelp": "El registro donde buscar. Las matrículas francesas e italianas son iguales, así que el país no puede deducirse de la matrícula.",
-        "bikes": "Buscar también motocicletas",
-        "bikesHelp": "Cuando una matrícula no es de un coche, se consulta también el registro de motocicletas. Un fallo en la búsqueda de coches es gratis, así que no cuesta nada más salvo que se encuentre una motocicleta. No disponible para Portugal."
+        "cars": "Buscar coches",
+        "carsHelp": "Consulta el registro de coches. Desactívalo si solo trabajas con motocicletas, así la búsqueda se salta los coches y responde más rápido.",
+        "bikes": "Buscar motocicletas",
+        "bikesHelp": "Consulta el registro de motocicletas. Si los coches también están activados, solo se consulta cuando no coincide ningún coche, y un fallo en la búsqueda de coches es gratis. No disponible para Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/fr/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Pays",
         "countryHelp": "Le registre à interroger. Les plaques françaises et italiennes ont le même format, le pays ne peut donc pas être déduit de la plaque.",
-        "bikes": "Rechercher aussi les motos",
-        "bikesHelp": "Quand une plaque n'est pas celle d'une voiture, le registre des motos est interrogé à son tour. Une recherche voiture sans résultat est gratuite, cela ne coûte donc rien de plus sauf si une moto est trouvée. Indisponible pour le Portugal."
+        "cars": "Rechercher les voitures",
+        "carsHelp": "Interroge le registre des voitures. Désactivez-le si vous ne travaillez que sur des motos : la recherche saute alors les voitures et répond plus vite.",
+        "bikes": "Rechercher les motos",
+        "bikesHelp": "Interroge le registre des motos. Si les voitures sont aussi activées, il n'est interrogé que lorsqu'aucune voiture ne correspond, et une recherche voiture sans résultat est gratuite. Indisponible pour le Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/it/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Paese",
         "countryHelp": "Il registro da consultare. Le targhe francesi e italiane hanno lo stesso formato, quindi il paese non si può ricavare dalla targa.",
-        "bikes": "Cerca anche i motocicli",
-        "bikesHelp": "Quando una targa non è di un'auto, viene consultato anche il registro dei motocicli. Una ricerca auto senza esito è gratuita, quindi non costa nulla in più a meno che non venga trovato un motociclo. Non disponibile per il Portogallo."
+        "cars": "Cerca le auto",
+        "carsHelp": "Consulta il registro delle auto. Disattivalo se lavori solo su motocicli, così la ricerca salta le auto e risponde più in fretta.",
+        "bikes": "Cerca i motocicli",
+        "bikesHelp": "Consulta il registro dei motocicli. Se sono attive anche le auto, viene consultato solo quando nessuna auto corrisponde, e una ricerca auto senza esito è gratuita. Non disponibile per il Portogallo."
       }
     },
     "rdw": {

+ 4 - 2
messages/lt/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Šalis",
         "countryHelp": "Registras, kuriame ieškoti. Prancūziški ir itališki numeriai atrodo vienodai, todėl šalies negalima nustatyti iš numerio.",
-        "bikes": "Ieškoti ir motociklų",
-        "bikesHelp": "Kai numeris nėra automobilio, papildomai užklausiamas motociklų registras. Nerastas automobilis nekainuoja, todėl papildomų išlaidų nėra, nebent randamas motociklas. Portugalijai neprieinama."
+        "cars": "Ieškoti automobilių",
+        "carsHelp": "Užklausiamas automobilių registras. Išjunkite, jei dirbate tik su motociklais: paieška praleis automobilius ir atsakys greičiau.",
+        "bikes": "Ieškoti motociklų",
+        "bikesHelp": "Užklausiamas motociklų registras. Jei įjungti ir automobiliai, jis užklausiamas tik neradus automobilio, o nerastas automobilis nekainuoja. Portugalijai neprieinama."
       }
     },
     "rdw": {

+ 4 - 2
messages/nb/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Land",
         "countryHelp": "Registeret som skal søkes i. Franske og italienske skilt ser like ut, så landet kan ikke leses av skiltet.",
-        "bikes": "Slå også opp motorsykler",
-        "bikesHelp": "Når et skilt ikke er en bil, spørres motorsykkelregisteret også. Et bomskudd på bilsiden er gratis, så dette koster ikke noe ekstra med mindre en motorsykkel blir funnet. Ikke tilgjengelig for Portugal."
+        "cars": "Slå opp biler",
+        "carsHelp": "Søk i bilregisteret. Slå av dette hvis dere bare jobber med motorsykler, så hopper oppslaget over bilsøket og svarer raskere.",
+        "bikes": "Slå opp motorsykler",
+        "bikesHelp": "Søk i motorsykkelregisteret. Når biler også er på, spørres det bare når ingen bil passer, og et bomskudd på bilsiden er gratis. Ikke tilgjengelig for Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/nl/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Land",
         "countryHelp": "Het register om in te zoeken. Franse en Italiaanse kentekens zien er hetzelfde uit, dus het land is niet van het kenteken af te lezen.",
-        "bikes": "Ook motorfietsen opzoeken",
-        "bikesHelp": "Als een kenteken geen auto is, wordt ook het motorfietsregister bevraagd. Geen resultaat bij auto's is gratis, dus dit kost niets extra tenzij een motorfiets wordt gevonden. Niet beschikbaar voor Portugal."
+        "cars": "Auto's opzoeken",
+        "carsHelp": "Doorzoek het autoregister. Zet dit uit als u alleen aan motorfietsen werkt, dan slaat de zoekopdracht auto's over en antwoordt sneller.",
+        "bikes": "Motorfietsen opzoeken",
+        "bikesHelp": "Doorzoek het motorfietsregister. Staan auto's ook aan, dan wordt het alleen bevraagd als geen auto overeenkomt, en geen resultaat bij auto's is gratis. Niet beschikbaar voor Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/pl/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Kraj",
         "countryHelp": "Rejestr, w którym szukać. Francuskie i włoskie tablice wyglądają tak samo, więc kraju nie da się odczytać z tablicy.",
-        "bikes": "Wyszukuj także motocykle",
-        "bikesHelp": "Gdy tablica nie należy do samochodu, odpytywany jest również rejestr motocykli. Brak wyniku dla samochodu jest bezpłatny, więc nie kosztuje to nic więcej, chyba że zostanie znaleziony motocykl. Niedostępne dla Portugalii."
+        "cars": "Wyszukuj samochody",
+        "carsHelp": "Przeszukuje rejestr samochodów. Wyłącz, jeśli obsługujesz tylko motocykle, wtedy wyszukiwanie pomija samochody i odpowiada szybciej.",
+        "bikes": "Wyszukuj motocykle",
+        "bikesHelp": "Przeszukuje rejestr motocykli. Gdy samochody też są włączone, jest odpytywany tylko wtedy, gdy żaden samochód nie pasuje, a brak wyniku dla samochodu jest bezpłatny. Niedostępne dla Portugalii."
       }
     },
     "rdw": {

+ 4 - 2
messages/pt-BR/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "País",
         "countryHelp": "O registro a consultar. Placas francesas e italianas têm o mesmo formato, então o país não pode ser deduzido da placa.",
-        "bikes": "Consultar também motocicletas",
-        "bikesHelp": "Quando uma placa não é de carro, o registro de motocicletas também é consultado. Uma consulta de carro sem resultado é gratuita, então não custa nada a mais a menos que uma motocicleta seja encontrada. Indisponível para Portugal."
+        "cars": "Consultar carros",
+        "carsHelp": "Consulta o registro de carros. Desative se você só trabalha com motocicletas, assim a consulta pula os carros e responde mais rápido.",
+        "bikes": "Consultar motocicletas",
+        "bikesHelp": "Consulta o registro de motocicletas. Com carros também ativado, ele só é consultado quando nenhum carro corresponde, e uma consulta de carro sem resultado é gratuita. Indisponível para Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/pt-PT/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "País",
         "countryHelp": "O registo a consultar. As matrículas francesas e italianas têm o mesmo formato, por isso o país não pode ser deduzido da matrícula.",
-        "bikes": "Consultar também motociclos",
-        "bikesHelp": "Quando uma matrícula não é de um automóvel, o registo de motociclos também é consultado. Uma consulta de automóvel sem resultado é gratuita, por isso não custa nada a mais, a menos que seja encontrado um motociclo. Indisponível para Portugal."
+        "cars": "Consultar automóveis",
+        "carsHelp": "Consulta o registo de automóveis. Desative se só trabalha com motociclos, assim a consulta salta os automóveis e responde mais depressa.",
+        "bikes": "Consultar motociclos",
+        "bikesHelp": "Consulta o registo de motociclos. Com os automóveis também ativos, só é consultado quando nenhum automóvel corresponde, e uma consulta de automóvel sem resultado é gratuita. Indisponível para Portugal."
       }
     },
     "rdw": {

+ 4 - 2
messages/ru/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Страна",
         "countryHelp": "Реестр, в котором искать. Французские и итальянские номера выглядят одинаково, поэтому страну нельзя определить по номеру.",
-        "bikes": "Искать также мотоциклы",
-        "bikesHelp": "Если номер не принадлежит автомобилю, запрашивается и реестр мотоциклов. Запрос по автомобилю без результата бесплатен, поэтому дополнительных расходов нет, если мотоцикл не найден. Недоступно для Португалии."
+        "cars": "Искать автомобили",
+        "carsHelp": "Запрашивает реестр автомобилей. Отключите, если работаете только с мотоциклами: поиск пропустит автомобили и ответит быстрее.",
+        "bikes": "Искать мотоциклы",
+        "bikesHelp": "Запрашивает реестр мотоциклов. Если автомобили тоже включены, он запрашивается только когда автомобиль не найден, а запрос по автомобилю без результата бесплатен. Недоступно для Португалии."
       }
     },
     "rdw": {

+ 4 - 2
messages/tr/integrations.json

@@ -429,8 +429,10 @@
       "settings": {
         "country": "Ülke",
         "countryHelp": "Aranacak sicil. Fransız ve İtalyan plakaları aynı görünür, bu yüzden ülke plakadan okunamaz.",
-        "bikes": "Motosikletleri de sorgula",
-        "bikesHelp": "Bir plaka otomobil değilse motosiklet sicili de sorgulanır. Otomobil tarafında sonuçsuz sorgu ücretsizdir, bu nedenle bir motosiklet bulunmadıkça ek maliyet oluşmaz. Portekiz için kullanılamaz."
+        "cars": "Otomobilleri sorgula",
+        "carsHelp": "Otomobil sicilini sorgular. Yalnızca motosikletle çalışıyorsanız kapatın, böylece sorgu otomobil aramasını atlar ve daha hızlı yanıt verir.",
+        "bikes": "Motosikletleri sorgula",
+        "bikesHelp": "Motosiklet sicilini sorgular. Otomobiller de açıksa yalnızca hiçbir otomobil eşleşmediğinde sorgulanır ve otomobil tarafında sonuçsuz sorgu ücretsizdir. Portekiz için kullanılamaz."
       }
     },
     "rdw": {

+ 44 - 0
src/__tests__/features/integrations/openapi-automotive.test.ts

@@ -422,6 +422,50 @@ describe('openapi automotive connector', () => {
     expect(pt.fetch).toHaveBeenCalledTimes(1)
   })
 
+  it('skips the car search when cars are off', async () => {
+    const bike: AutomotiveRecord = {
+      CarMake: 'TRIUMPH',
+      CarModel: 'BONNEVILLE',
+      FuelType: 'PETROL',
+    }
+    const answer = async (url: string) =>
+      url.includes('UK-bike')
+        ? envelope(bike)
+        : Response.json({ data: [], success: true, message: 'car not found' }, { status: 404 })
+    const bikesOnly = context(answer, { country: 'GB', cars: false, bikes: true })
+    const found = await connector.lookupVehicle?.(bikesOnly.ctx, { plate: 'AB12 CDE' })
+    expect(found?.model).toBe('BONNEVILLE')
+    expect(bikesOnly.fetch.mock.calls.map((c) => c[0])).toEqual([
+      'https://automotive.openapi.com/UK-bike/AB12CDE',
+    ])
+    // A connection saved before the car switch existed still searches cars.
+    const saved = context(answer, { country: 'GB', bikes: true })
+    await connector.lookupVehicle?.(saved.ctx, { plate: 'AB12CDE' })
+    expect(saved.fetch.mock.calls.map((c) => c[0])).toEqual([
+      'https://automotive.openapi.com/UK-car/AB12CDE',
+      'https://automotive.openapi.com/UK-bike/AB12CDE',
+    ])
+  })
+
+  it('refuses to look up with both cars and motorcycles off', async () => {
+    const { ctx, fetch } = context(async () => envelope(FRANCE), {
+      country: 'GB',
+      cars: false,
+      bikes: false,
+    })
+    await expect(connector.lookupVehicle?.(ctx, { plate: 'AB12CDE' })).rejects.toThrow(
+      /turn on cars or motorcycles/
+    )
+    expect(fetch).not.toHaveBeenCalled()
+    const test = await connector.test(ctx)
+    expect(test.ok).toBe(false)
+    // Portugal has no motorcycle registry, so cars off leaves nothing to search.
+    const pt = context(async () => envelope(FRANCE), { country: 'PT', cars: false, bikes: true })
+    await expect(connector.lookupVehicle?.(pt.ctx, { plate: '9027QL' })).rejects.toThrow(
+      /turn on cars/
+    )
+  })
+
   it('polls the status endpoint while the vendor is still fetching', async () => {
     const pending = { state: 'PENDING', id: '66a8ed9b82cf2ae627012068' }
     let polls = 0

+ 7 - 0
src/integrations/openapi-automotive/manifest.ts

@@ -37,6 +37,13 @@ export const manifest: ConnectorManifest = {
         { value: 'GB', label: 'United Kingdom' },
       ],
     },
+    {
+      key: 'cars',
+      type: 'boolean',
+      label: 'cars',
+      help: 'carsHelp',
+      default: true,
+    },
     {
       key: 'bikes',
       type: 'boolean',

+ 33 - 10
src/integrations/openapi-automotive/server.ts

@@ -307,6 +307,21 @@ async function fetchRecord(
   }
 }
 
+/**
+ * The registries a lookup asks, in order. Cars come first when both are on,
+ * as before the car switch existed; a workshop that only sees motorcycles
+ * turns cars off and saves the wait on a car search that finds nothing.
+ * Connections saved before the car switch have no value for it and keep
+ * searching cars. Portugal has no motorcycle registry.
+ */
+export function endpointsOf(ctx: ConnectorContext, country: Country): string[] {
+  const { car, bike } = ENDPOINTS[country]
+  const out: string[] = []
+  if (ctx.connection.settings.cars !== false) out.push(car)
+  if (ctx.connection.settings.bikes === true && bike) out.push(bike)
+  return out
+}
+
 /** One vehicle by plate. This registry has no VIN search, so a VIN alone finds nothing. */
 async function lookupVehicle(
   ctx: ConnectorContext,
@@ -319,17 +334,15 @@ async function lookupVehicle(
   }
   const country = countryOf(ctx)
   if (!country) throw new Error('Openapi Automotive: choose a country on the integration page')
-  const endpoints = ENDPOINTS[country]
-  const car = await fetchRecord(ctx, endpoints.car, plate)
-  if (car) {
-    await ctx.log('info', 'Vehicle lookup: match', { endpoint: endpoints.car })
-    return mapRecord(car, plate)
+  const endpoints = endpointsOf(ctx, country)
+  if (endpoints.length === 0) {
+    throw new Error('Openapi Automotive: turn on cars or motorcycles on the integration page')
   }
-  if (ctx.connection.settings.bikes === true && endpoints.bike) {
-    const bike = await fetchRecord(ctx, endpoints.bike, plate)
-    if (bike) {
-      await ctx.log('info', 'Vehicle lookup: match', { endpoint: endpoints.bike })
-      return mapRecord(bike, plate)
+  for (const endpoint of endpoints) {
+    const record = await fetchRecord(ctx, endpoint, plate)
+    if (record) {
+      await ctx.log('info', 'Vehicle lookup: match', { endpoint })
+      return mapRecord(record, plate)
     }
   }
   await ctx.log('info', 'Vehicle lookup: no match', { country })
@@ -344,6 +357,16 @@ export const connector: ConnectorServer = {
     if (!countryOf(ctx)) {
       return { ok: false, message: 'Openapi Automotive: choose the country to search' }
     }
+    const country = countryOf(ctx) as Country
+    if (endpointsOf(ctx, country).length === 0) {
+      return {
+        ok: false,
+        message:
+          country === 'PT'
+            ? 'Openapi Automotive: Portugal has no motorcycle registry, turn on cars'
+            : 'Openapi Automotive: turn on cars or motorcycles',
+      }
+    }
     // There is no ping endpoint and every lookup is billed, so the request
     // status endpoint is asked about an id that was never issued: a good
     // token gets "not found" or "not valid", a bad one is turned away.