From edcfc2e41bc821f717299143af66c0c618177b4c Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 28 May 2024 14:10:05 -0700 Subject: [PATCH 1/5] use a different local storage key for jwt token in admin-frontend vs frontend --- admin-frontend/src/common/apiService.ts | 8 +++++--- admin-frontend/src/store/modules/auth.js | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/admin-frontend/src/common/apiService.ts b/admin-frontend/src/common/apiService.ts index 92a43c58..2f43c6f3 100644 --- a/admin-frontend/src/common/apiService.ts +++ b/admin-frontend/src/common/apiService.ts @@ -4,6 +4,8 @@ import { ApiRoutes } from '../utils/constant'; import AuthService from './authService'; import { IConfigValue, IReport } from './types'; +export const LOCAL_STORAGE_KEY_JWT = 'pay-transparency-admin-jwt'; + export enum REPORT_FORMATS { HTML = 'html', PDF = 'pdf', @@ -51,12 +53,12 @@ const intercept = apiAxios.interceptors.response.use( axios.interceptors.response.eject(intercept); return new Promise((resolve, reject) => { AuthService.refreshAuthToken( - localStorage.getItem('jwtToken'), + localStorage.getItem(LOCAL_STORAGE_KEY_JWT), localStorage.getItem('correlationID'), ) .then((response) => { if (response.jwtFrontend) { - localStorage.setItem('jwtToken', response.jwtFrontend); + localStorage.setItem(LOCAL_STORAGE_KEY_JWT, response.jwtFrontend); localStorage.setItem('correlationID', response.correlationID); apiAxios.defaults.headers.common['Authorization'] = `Bearer ${response.jwtFrontend}`; @@ -72,7 +74,7 @@ const intercept = apiAxios.interceptors.response.use( }) .catch((e) => { processQueue(e, null); - localStorage.removeItem('jwtToken'); + localStorage.removeItem(LOCAL_STORAGE_KEY_JWT); window.location.href = '/token-expired'; reject(new Error('token expired', { cause: e })); }); diff --git a/admin-frontend/src/store/modules/auth.js b/admin-frontend/src/store/modules/auth.js index a5b9697a..922dd43d 100644 --- a/admin-frontend/src/store/modules/auth.js +++ b/admin-frontend/src/store/modules/auth.js @@ -1,4 +1,4 @@ -import ApiService from '../../common/apiService'; +import ApiService, { LOCAL_STORAGE_KEY_JWT } from '../../common/apiService'; import AuthService from '../../common/authService'; import { defineStore } from 'pinia'; @@ -22,7 +22,7 @@ export const authStore = defineStore('auth', { error: false, isLoading: true, loginError: false, - jwtToken: localStorage.getItem('jwtToken'), + jwtToken: localStorage.getItem(LOCAL_STORAGE_KEY_JWT), }), getters: { acronymsGet: (state) => state.acronyms, @@ -39,11 +39,11 @@ export const authStore = defineStore('auth', { if (token) { this.isAuthenticated = true; this.jwtToken = token; - localStorage.setItem('jwtToken', token); + localStorage.setItem(LOCAL_STORAGE_KEY_JWT, token); } else { this.isAuthenticated = false; this.jwtToken = null; - localStorage.removeItem('jwtToken'); + localStorage.removeItem(LOCAL_STORAGE_KEY_JWT); } }, setCorrelationID(correlationID) { From 22562116a2396edd683774d0edf140dc85857533 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 28 May 2024 14:28:22 -0700 Subject: [PATCH 2/5] fix admin-frontend unit tests --- .../src/store/modules/__tests__/auth.spec.ts | 26 ++++---- .../store/modules/__tests__/codeStore.spec.ts | 61 +++++++++++-------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/admin-frontend/src/store/modules/__tests__/auth.spec.ts b/admin-frontend/src/store/modules/__tests__/auth.spec.ts index b1318234..0f92bab3 100644 --- a/admin-frontend/src/store/modules/__tests__/auth.spec.ts +++ b/admin-frontend/src/store/modules/__tests__/auth.spec.ts @@ -1,16 +1,17 @@ -import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; import { createTestingPinia } from '@pinia/testing'; import { setActivePinia } from 'pinia'; -import { authStore } from '../auth'; +import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; +import { LOCAL_STORAGE_KEY_JWT } from '../../../common/apiService'; import AuthService from '../../../common/authService'; +import { authStore } from '../auth'; vi.mock('../../../common/authService', async (importOriginal) => { const mod: any = await importOriginal(); const resp: any = { default: { ...mod.default, getAuthToken: vi.fn(), - refreshAuthToken: vi.fn() - } + refreshAuthToken: vi.fn(), + }, }; return resp; }); @@ -20,19 +21,22 @@ describe('AuthStore', () => { let pinia; beforeEach(() => { - - pinia = createTestingPinia({ stubActions: false, fakeApp: true, createSpy: vi.fn }); + pinia = createTestingPinia({ + stubActions: false, + fakeApp: true, + createSpy: vi.fn, + }); setActivePinia(pinia); auth = authStore(pinia); }); it('setJwtToken, if provided will set value to local storage', async () => { await auth.setJwtToken('testToken'); - expect(localStorage.getItem('jwtToken')).toBe('testToken'); + expect(localStorage.getItem(LOCAL_STORAGE_KEY_JWT)).toBe('testToken'); }); it('setJwtToken, if not provided will remove value to local storage', async () => { await auth.setJwtToken(); - expect(localStorage.getItem('jwtToken')).toBeNull(); + expect(localStorage.getItem(LOCAL_STORAGE_KEY_JWT)).toBeNull(); }); it('setCorrelationID, if provided will set value to local storage', async () => { await auth.setCorrelationID('correlationID'); @@ -45,9 +49,9 @@ describe('AuthStore', () => { it('getJwtToken, if token provided by API, should set in localStorage', async () => { (AuthService.getAuthToken as Mock).mockResolvedValueOnce({ jwtFrontend: 'testToken', - correlationID: 'testCorrelationID' - } ); + correlationID: 'testCorrelationID', + }); await auth.getJwtToken(); - expect(localStorage.getItem('jwtToken')).toBeTruthy(); + expect(localStorage.getItem(LOCAL_STORAGE_KEY_JWT)).toBeTruthy(); }); }); diff --git a/admin-frontend/src/store/modules/__tests__/codeStore.spec.ts b/admin-frontend/src/store/modules/__tests__/codeStore.spec.ts index 99ddc06d..a6f8f77a 100644 --- a/admin-frontend/src/store/modules/__tests__/codeStore.spec.ts +++ b/admin-frontend/src/store/modules/__tests__/codeStore.spec.ts @@ -11,16 +11,17 @@ import { useCodeStore } from '../codeStore'; // ---------------------------------------------------------------------------- vi.mock('../../../common/apiService', async (importOriginal) => { - const mod: any = await importOriginal() + const mod: any = await importOriginal(); const resp = { + ...mod, default: { ...mod.default, getEmployeeCountRanges: vi.fn(), getNaicsCodes: vi.fn(), - } - } + }, + }; return resp; -}) +}); // ---------------------------------------------------------------------------- // Test Data @@ -29,15 +30,15 @@ vi.mock('../../../common/apiService', async (importOriginal) => { const testEmployeeCountRanges = [ { employee_count_range_id: 'ea8b2547-4e93-4bfa-aec1-3e90f91027dd', - employee_count_range: '1-99' + employee_count_range: '1-99', }, { employee_count_range_id: 'c7e1c454-7db9-46c6-b250-1567a543d22f', - employee_count_range: '100-499' + employee_count_range: '100-499', }, { employee_count_range_id: '5f26cc90-7960-4e14-9700-87ecd75f0a0f', - employee_count_range: '500+' + employee_count_range: '500+', }, ]; @@ -53,53 +54,59 @@ const testNaicsCodes = [ { naics_code: '3', naics_label: 'test3', - } -] + }, +]; // ---------------------------------------------------------------------------- // Test Suite // ---------------------------------------------------------------------------- -describe("CodeStore", () => { +describe('CodeStore', () => { let codeStore; let auth; let pinia; beforeEach(() => { - - pinia = createTestingPinia({ stubActions: false, fakeApp: true, createSpy: vi.fn, }); - setActivePinia(pinia) + pinia = createTestingPinia({ + stubActions: false, + fakeApp: true, + createSpy: vi.fn, + }); + setActivePinia(pinia); auth = authStore(pinia); codeStore = useCodeStore(pinia); - }) + }); it('Fetches employee range count and naics code when auth status becomes true', async () => { - // Mock functions in the ApiService which normally make HTTP requests to the backend. // Instead, have these functions return some test data without any HTTP requests. - (ApiService.getEmployeeCountRanges as Mock).mockResolvedValueOnce(testEmployeeCountRanges); + (ApiService.getEmployeeCountRanges as Mock).mockResolvedValueOnce( + testEmployeeCountRanges, + ); (ApiService.getNaicsCodes as Mock).mockResolvedValueOnce(testNaicsCodes); - // Setup "watches" on CodeStore state variables. When the values of these + // Setup "watches" on CodeStore state variables. When the values of these // variables change, check that they are what we expect - const { naicsCodes, employeeCountRanges } = storeToRefs(codeStore) + const { naicsCodes, employeeCountRanges } = storeToRefs(codeStore); watch(naicsCodes, (val) => { expect(naicsCodes.value?.length).toBe(testNaicsCodes.length); expect(naicsCodes.value[0].naics_code).toBe(testNaicsCodes[0].naics_code); - }) + }); watch(employeeCountRanges, (val) => { - expect(employeeCountRanges.value?.length).toBe(testEmployeeCountRanges.length); - expect(employeeCountRanges.value[0].employee_count_range_id).toBe(testEmployeeCountRanges[0].employee_count_range_id); - }) + expect(employeeCountRanges.value?.length).toBe( + testEmployeeCountRanges.length, + ); + expect(employeeCountRanges.value[0].employee_count_range_id).toBe( + testEmployeeCountRanges[0].employee_count_range_id, + ); + }); // Setting the JWT Token in the AuthService triggers a function inside CodeStore // called fetchAllCodes(). This is the method that calls the ApiService to fetch // data, and then sets the data in state variables: naicsCodes and employeeCountRanges. // (The token isn't validated by auth.setJwtToken(), so even an invalid token should // cause the events described above) - await auth.setJwtToken("fake token"); - - }) - -}) \ No newline at end of file + await auth.setJwtToken('fake token'); + }); +}); From 2c9ca1c44675455a1ba9b94badac551a3cd48058 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 28 May 2024 14:58:28 -0700 Subject: [PATCH 3/5] fixed port of admin api --- .../fin-pay-transparency/charts/backend/templates/service.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/fin-pay-transparency/charts/backend/templates/service.yaml b/charts/fin-pay-transparency/charts/backend/templates/service.yaml index 7be64fb1..71dcef83 100644 --- a/charts/fin-pay-transparency/charts/backend/templates/service.yaml +++ b/charts/fin-pay-transparency/charts/backend/templates/service.yaml @@ -17,7 +17,7 @@ spec: targetPort: external-http protocol: TCP name: external-http - - port: 3002 + - port: 3004 targetPort: admin-http protocol: TCP name: admin-http From e6c4ea91f3ac65807d3ac51e559e604daf07b239 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 28 May 2024 15:32:40 -0700 Subject: [PATCH 4/5] provide an admin-frontend url for the dev environment --- .github/workflows/merge.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 5fa57414..7a369925 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -116,6 +116,7 @@ jobs: target: dev tag: ${{ needs.semantic-version.outputs.semanticVersion }} frontend-url: https://dev.paytransparency.fin.gov.bc.ca + admin-frontend-url: https://dev-admin.paytransparency.fin.gov.bc.ca semver: ${{ needs.semantic-version.outputs.semanticVersion }} values: "values-dev.yaml" From e83b504ea2bfffcf80d0b218945d6ee1f2fcdce5 Mon Sep 17 00:00:00 2001 From: Brock Anderson Date: Tue, 28 May 2024 16:07:52 -0700 Subject: [PATCH 5/5] update url for admin-frontend dev deployment --- .github/workflows/merge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 7a369925..2855aeb7 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -116,7 +116,7 @@ jobs: target: dev tag: ${{ needs.semantic-version.outputs.semanticVersion }} frontend-url: https://dev.paytransparency.fin.gov.bc.ca - admin-frontend-url: https://dev-admin.paytransparency.fin.gov.bc.ca + admin-frontend-url: https://pay-transparency-dev-admin-frontend.apps.silver.devops.gov.bc.ca semver: ${{ needs.semantic-version.outputs.semanticVersion }} values: "values-dev.yaml"