diff --git a/.gitignore b/.gitignore index 11e3992e..9a467729 100644 --- a/.gitignore +++ b/.gitignore @@ -147,6 +147,7 @@ dist # IDEs .vscode +.idea # Cypress related files **/cypress.env.json @@ -160,4 +161,4 @@ dist **/token.json **/local-dev/** **/*copy.json -testing/cypress.config copy.ts \ No newline at end of file +testing/cypress.config copy.ts diff --git a/app/.env.example b/app/.env.example index 62cffa36..c4d12ffa 100644 --- a/app/.env.example +++ b/app/.env.example @@ -4,7 +4,8 @@ NUXT_SITEMINDER_LOGOUT_URL="https://logontest7.gov.bc.ca/clp-cgi/logoff.cgi" #vaults API -NUXT_NAMEX_API_URL="https://namex-dev.apps.silver.devops.gov.bc.ca" +NUXT_NAMEX_API_GW_URL="https://test.api.connect.gov.bc.ca/namex-dev" +NUXT_NAMEX_API_KEY= NUXT_NAMEX_API_VERSION="/api/v1" NUXT_NAMEX_ADMIN_URL = "https://namex-solr-dev.apps.silver.devops.gov.bc.ca/" diff --git a/app/devops/vaults.env b/app/devops/vaults.env index 91fe7801..28b24e00 100644 --- a/app/devops/vaults.env +++ b/app/devops/vaults.env @@ -5,7 +5,8 @@ NUXT_SITEMINDER_LOGOUT_URL="op://web-url/$APP_ENV/siteminder/SITEMINDER_LOGOUT_U NUXT_NAMEX_ADMIN_URL="op://web-url/$APP_ENV/namex-examination/NAMEX_ADMIN_URL" #vaults API -NUXT_NAMEX_API_URL="op://API/$APP_ENV/namex-api/NAMEX_API_URL" +NUXT_NAMEX_API_GW_URL="op://API/$APP_ENV/namex-api/NAMEX_API_GW_URL" +NUXT_NAMEX_API_KEY="op://API/$APP_ENV/namex-api/NAMEX_API_KEY" NUXT_NAMEX_API_VERSION="op://API/$APP_ENV/namex-api/NAMEX_API_VERSION" #vaults keycloak diff --git a/app/nuxt.config.ts b/app/nuxt.config.ts index cc911c44..c21429b9 100644 --- a/app/nuxt.config.ts +++ b/app/nuxt.config.ts @@ -18,7 +18,6 @@ export default defineNuxtConfig({ '@nuxt/content', '@nuxtjs/tailwindcss', '@pinia/nuxt', - 'nuxt-vitest', ], css: ['@/assets/css/main.scss'], typescript: { @@ -50,7 +49,8 @@ export default defineNuxtConfig({ firebaseAuthDomain: process.env.NUXT_AUTH_DOMAIN, firebaseProjectId: process.env.NUXT_PROJECT_ID, firebaseAppId: process.env.NUXT_APP_ID, - namexAPIURL: process.env.NUXT_NAMEX_API_URL, + namexAPIURL: process.env.NUXT_NAMEX_API_GW_URL, + namexAPIKey: process.env.NUXT_NAMEX_API_KEY, namexAPIVersion: process.env.NUXT_NAMEX_API_VERSION, namexAdminURL: process.env.NUXT_NAMEX_ADMIN_URL, keycloakAuthUrl: process.env.NUXT_KEYCLOAK_AUTH_URL, diff --git a/app/package-lock.json b/app/package-lock.json index 0178aa1d..c5ea8989 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "name-examination", - "version": "1.2.39", + "version": "1.2.48", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "name-examination", - "version": "1.2.39", + "version": "1.2.48", "hasInstallScript": true, "dependencies": { "@headlessui/vue": "0.0.0-insiders.01a34cb", diff --git a/app/package.json b/app/package.json index 4e991a38..534d1030 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "name-examination", - "version": "1.2.47", + "version": "1.2.49", "private": true, "scripts": { "build": "nuxt generate", diff --git a/app/store/examine/conflicts.ts b/app/store/examine/conflicts.ts index 4bfc5b69..d683baca 100644 --- a/app/store/examine/conflicts.ts +++ b/app/store/examine/conflicts.ts @@ -1,5 +1,6 @@ import type { ConflictListItem } from '~/types' import { getConflicts } from '~/util/namex-api' +import { highlightWord } from '~/util/html/highlight' export const useConflicts = defineStore('conflicts', () => { @@ -83,31 +84,44 @@ export const useConflicts = defineStore('conflicts', () => { } function highlightNameChoices(entry: any): string { - let result = entry.name - if (entry.highlighting) { - if (entry.highlighting.stems) { - entry.highlighting.stems.forEach((stem: string) => { - const re = new RegExp(stem, 'gi') - result = result.replace(re, (match: any) => `${match}`) - }) + const name: string = entry?.name ?? '' + const highlighting = entry?.highlighting + + // If we have nothing to highlight, keep original text intact + if (!name || !highlighting) { + return name + } + + // Split into word and whitespace tokens so we preserve spacing exactly + const tokens = name.split(/(\s+)/) + + const exactList: string[] = Array.isArray(highlighting.exact) ? highlighting.exact : [] + const synonymList: string[] = Array.isArray(highlighting.synonyms) ? highlighting.synonyms : [] + const stemList: string[] = Array.isArray(highlighting.stems) ? highlighting.stems : [] + + const applyFirstMatchingCategory = (word: string): string => { + // exact > synonym > stem + for (const exact of exactList) { + const highlighted = highlightWord(exact, word, 'exact-highlight') + if (highlighted !== word) return highlighted } - if (entry.highlighting.synonyms) { - entry.highlighting.synonyms.forEach((synonym: string) => { - const re = new RegExp(synonym, 'gi') - result = result.replace(re, (match: any) => `${match}`) - }) + for (const synonym of synonymList) { + const highlighted = highlightWord(synonym, word, 'synonym-highlight') + if (highlighted !== word) return highlighted } - if (entry.highlighting.exact) { - entry.highlighting.exact.forEach((exact: string) => { - const re = new RegExp(exact, 'gi') - result = result.replace(re, (match: any) => `${match}`) - }) + for (const stem of stemList) { + const highlighted = highlightWord(stem, word, 'stem-highlight') + if (highlighted !== word) return highlighted } + + return word } - return result + return tokens + .map((token) => (token.trim().length === 0 ? token : applyFirstMatchingCategory(token))) + .join('') } async function initialize(searchQuery: string, exactPhrase: string) { @@ -163,7 +177,7 @@ export const useConflicts = defineStore('conflicts', () => { } /** Reset selectedConflicts and comparedConflicts and save existing data */ - function disableAutoAdd () { + function disableAutoAdd() { if (!autoAdd.value) { const initialRun = (prevSelectedConflicts.value.length === 0 && prevComparedConflicts.value.length === 0) for (const conflict of selectedConflicts.value) { @@ -180,7 +194,7 @@ export const useConflicts = defineStore('conflicts', () => { } /** Reassign selectedConflicts and comparedConflicts */ - function enableAutoAdd () { + function enableAutoAdd() { if (autoAdd.value) { selectedConflicts.value = prevSelectedConflicts.value comparedConflicts.value = prevComparedConflicts.value @@ -205,6 +219,6 @@ export const useConflicts = defineStore('conflicts', () => { enableAutoAdd, autoAdd, firstConflictItem, - syncSelectedAndComparedConflicts, + syncSelectedAndComparedConflicts } }) diff --git a/app/util/html/highlight.ts b/app/util/html/highlight.ts new file mode 100644 index 00000000..cfea1ea5 --- /dev/null +++ b/app/util/html/highlight.ts @@ -0,0 +1,7 @@ +const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + +export const highlightWord = (word: string, text: string, highlightCss: string) => { + if (!word) return text + const re = new RegExp(escapeRegExp(word), 'gi') + return text.replace(re, (match: string) => `${match}`) +} diff --git a/app/util/namex-api.ts b/app/util/namex-api.ts index ca109cd6..2e7c5048 100644 --- a/app/util/namex-api.ts +++ b/app/util/namex-api.ts @@ -29,6 +29,7 @@ async function callNamexApi(url: URL, options?: object, headers?: object) { headers: { Authorization: `Bearer ${token}`, 'App-Name': packageInfo.name, + 'X-Apikey': useRuntimeConfig().public.namexAPIKey || '', ...headers, }, ...(options ? options : {}), @@ -41,9 +42,10 @@ async function callNamexApi(url: URL, options?: object, headers?: object) { */ export function getNamexApiUrl(endpoint: string): URL { const config = useRuntimeConfig().public + const base = config.namexAPIURL return new URL( - config.namexAPIVersion + endpoint, - config.namexAPIURL as string + config.namexAPIVersion.replace(/^\//, '') + endpoint, + base.endsWith('/') ? base : base + '/' as string ) }