From 0cfb04bfed93d72f28c1048cfdccae9f8998f6ac Mon Sep 17 00:00:00 2001
From: Falk <falk.neumann@adfinis.com>
Date: Thu, 11 Apr 2024 18:19:40 +0200
Subject: [PATCH] feat(street-service): add low level request optimization for
 multi language search

---
 addon/services/languages.js           |  51 ++++++++++++
 addon/services/street.js              | 107 +++++++++++++++++---------
 tests/unit/services/languages-test.js |  37 +++++++++
 3 files changed, 157 insertions(+), 38 deletions(-)
 create mode 100644 addon/services/languages.js
 create mode 100644 tests/unit/services/languages-test.js

diff --git a/addon/services/languages.js b/addon/services/languages.js
new file mode 100644
index 00000000..781b8764
--- /dev/null
+++ b/addon/services/languages.js
@@ -0,0 +1,51 @@
+import Service, { inject as service } from "@ember/service";
+import { languageOptions } from "ember-ebau-gwr/models/options";
+
+export default class LanguagesService extends Service {
+  @service config;
+
+  languages = {
+    AG: ["de"],
+    AR: ["de"],
+    AI: ["de"],
+    BL: ["de"],
+    BS: ["de"],
+    BE: ["de", "fr"],
+    FR: ["de", "fr"],
+    GE: ["de", "fr"],
+    GL: ["de"],
+    GR: ["de", "rm", "it"],
+    JU: ["fr"],
+    LU: ["de"],
+    NE: ["fr"],
+    NW: ["de"],
+    OW: ["de"],
+    SH: ["de"],
+    SZ: ["de"],
+    SO: ["de"],
+    SG: ["de"],
+    TI: ["it"],
+    TG: ["de"],
+    UR: ["de"],
+    VD: ["fr"],
+    VS: ["de", "fr"],
+    ZG: ["de"],
+    ZH: ["de"],
+  };
+
+  get availableLanguages() {
+    return this.config.cantonAbbreviation
+      ? this.languages[this.config.cantonAbbreviation]
+      : ["de", "rm", "fr", "it"];
+  }
+
+  codeToLanguage(code) {
+    return Object.entries(languageOptions).find(
+      ([, value]) => value === code,
+    )[0];
+  }
+
+  languageToCode(languageAbbreviation) {
+    return languageOptions[languageAbbreviation];
+  }
+}
diff --git a/addon/services/street.js b/addon/services/street.js
index a3583c62..4e6b2f8e 100644
--- a/addon/services/street.js
+++ b/addon/services/street.js
@@ -1,12 +1,21 @@
-import { languageOptions } from "ember-ebau-gwr/models/options";
+import { inject as service } from "@ember/service";
+import { task } from "ember-concurrency";
 import Street, { StreetList } from "ember-ebau-gwr/models/street";
 
 import GWRService from "./gwr";
 
 export default class StreetService extends GWRService {
+  @service languages;
+
   cacheKey = "ESID";
   cacheClass = Street;
-  cachedLanguageOverride;
+  cachedLanguageOverrides;
+
+  constructor(...args) {
+    super(...args);
+
+    this.resetCachedLanguages();
+  }
 
   async get(ESID) {
     if (!ESID) {
@@ -18,54 +27,76 @@ export default class StreetService extends GWRService {
     return this.cache(street);
   }
 
-  get language() {
-    return languageOptions[this.intl.primaryLocale?.split("-")?.[0]];
+  get primaryLanguage() {
+    return this.intl.primaryLocale?.split("-")?.[0];
   }
 
-  async search(query = {}) {
-    if (this.cachedLanguageOverride) {
-      query.language = this.cachedLanguageOverride;
-    }
+  get language() {
+    return this.languages.languageToCode(this.primaryLanguage);
+  }
 
-    const searchResult = await super.search(query, query.EGID, {
+  @task
+  *_search(query) {
+    return yield super.search(query, query.EGID, {
       xmlMethod: "getStreet",
       urlPath: "streets",
       listModel: StreetList,
       listKey: "street",
       searchKey: "streetWithoutStreetGeometryType",
     });
+  }
 
-    if (!searchResult) {
-      let results = [];
-
-      for (const [lang, code] of Object.entries(languageOptions)) {
-        query.language = code;
-
-        results.push({
-          lang: [lang, code],
-          result: super.search(query, query.EGID, {
-            xmlMethod: "getStreet",
-            urlPath: "streets",
-            listModel: StreetList,
-            listKey: "street",
-            searchKey: "streetWithoutStreetGeometryType",
-          }),
-        });
-      }
-
-      results = await Promise.allSettled(
-        results.map(async ({ lang, result }) => {
-          return { lang, result: await result };
+  resetCachedLanguages() {
+    this.cachedLanguageOverrides = new Set([]);
+  }
+
+  async searchMultiple(query, useCachedLanguagesOnly) {
+    let results = [];
+
+    for (const lang of useCachedLanguagesOnly
+      ? this.cachedLanguageOverrides
+      : this.languages.availableLanguages) {
+      const code = this.languages.languageToCode(lang);
+      query.language = code;
+
+      results.push({
+        lang: [lang, code],
+        result: this._search.perform(query, query.EGID, {
+          xmlMethod: "getStreet",
+          urlPath: "streets",
+          listModel: StreetList,
+          listKey: "street",
+          searchKey: "streetWithoutStreetGeometryType",
         }),
-      );
-      return results
-        .filter(({ value }) => value.result?.length)
-        .reduce((previous, { value: { lang, result } }) => {
-          this.cachedLanguageOverride = lang[1];
-          return previous.concat(result);
-        }, []);
+      });
     }
 
-    return searchResult;
+    results = await Promise.allSettled(
+      results.map(async ({ lang, result }) => {
+        return { lang, result: await result };
+      }),
+    );
+    return results
+      .filter(({ value }) => value.result?.length)
+      .reduce((previous, { value: { lang, result } }) => {
+        this.cachedLanguageOverrides.add(lang[0]);
+        return previous.concat(result);
+      }, []);
+  }
+
+  async search(query = {}) {
+    const lastQueryString =
+      this._search.lastPerformed?.args[0]?.description?.descriptionLong.replaceAll(
+        "*",
+        "",
+      ) || "";
+    const currentQueryString =
+      query.description?.descriptionLong?.replaceAll("*", "") || "";
+
+    if (lastQueryString && currentQueryString.includes(lastQueryString)) {
+      return this.searchMultiple(query, true);
+    }
+    this.resetCachedLanguages();
+    return this.searchMultiple(query);
   }
 }
diff --git a/tests/unit/services/languages-test.js b/tests/unit/services/languages-test.js
new file mode 100644
index 00000000..cc7871b2
--- /dev/null
+++ b/tests/unit/services/languages-test.js
@@ -0,0 +1,37 @@
+import { setupTest } from "dummy/tests/helpers";
+import { module, test } from "qunit";
+
+module("Unit | Service | languages", function (hooks) {
+  setupTest(hooks);
+
+  test("available languages for a given canton", function (assert) {
+    const configService = this.owner.lookup("service:config");
+    const languagesService = this.owner.lookup("service:languages");
+
+    configService.cantonAbbreviation = "GR";
+
+    assert.deepEqual(languagesService.availableLanguages, ["de", "rm", "it"]);
+
+    configService.cantonAbbreviation = "BE";
+
+    assert.deepEqual(languagesService.availableLanguages, ["de", "fr"]);
+  });
+
+  test("transforms language abbrevation to gwr API language code", function (assert) {
+    const service = this.owner.lookup("service:languages");
+
+    assert.strictEqual(service.languageToCode("de"), 9901);
+    assert.strictEqual(service.languageToCode("rm"), 9902);
+    assert.strictEqual(service.languageToCode("fr"), 9903);
+    assert.strictEqual(service.languageToCode("it"), 9904);
+  });
+
+  test("transforms gwr API language code to language abbrevation", function (assert) {
+    const service = this.owner.lookup("service:languages");
+
+    assert.strictEqual(service.codeToLanguage(9901), "de");
+    assert.strictEqual(service.codeToLanguage(9902), "rm");
+    assert.strictEqual(service.codeToLanguage(9903), "fr");
+    assert.strictEqual(service.codeToLanguage(9904), "it");
+  });
+});