From 01e79987212b57d37766cef37634eaf72276d0f3 Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Mon, 27 Nov 2023 15:12:54 -0800 Subject: [PATCH 1/2] Allow multiple CHEFS forms to be configured FE can pull from multiple data sources. BE has dynamic basic auth based on the requested form ID --- app/app.ts | 2 +- app/config/custom-environment-variables.json | 14 +++ app/src/components/utils.ts | 28 ++++++ app/src/controllers/chefs.ts | 97 +++++++------------ .../middleware/requireChefsFormConfigData.ts | 36 +++++++ app/src/routes/v1/chefs.ts | 37 ++----- app/src/services/chefs.ts | 74 ++------------ app/src/types/ChefsFormConfig.ts | 10 ++ app/src/types/ChefsSubmissionDataSource.ts | 11 +++ frontend/src/components/layout/Navbar.vue | 5 +- frontend/src/router/index.ts | 6 ++ frontend/src/services/chefsService.ts | 58 +---------- frontend/src/utils/constants.ts | 1 + frontend/src/views/InitiativesView.vue | 10 ++ frontend/src/views/SubmissionView.vue | 25 +---- frontend/src/views/SubmissionsView.vue | 33 ++----- 16 files changed, 183 insertions(+), 264 deletions(-) create mode 100644 app/src/middleware/requireChefsFormConfigData.ts create mode 100644 app/src/types/ChefsFormConfig.ts create mode 100644 app/src/types/ChefsSubmissionDataSource.ts create mode 100644 frontend/src/views/InitiativesView.vue diff --git a/app/app.ts b/app/app.ts index 0da40e88..01bdf824 100644 --- a/app/app.ts +++ b/app/app.ts @@ -73,7 +73,7 @@ appRouter.get('/config', (_req: Request, res: Response, next: (err: unknown) => ...config.get('frontend'), gitRev: state.gitRev, idpList: readIdpList(), - version: process.env.npm_package_version + version: appVersion }); } catch (err) { next(err); diff --git a/app/config/custom-environment-variables.json b/app/config/custom-environment-variables.json index 97a68473..89d14efe 100644 --- a/app/config/custom-environment-variables.json +++ b/app/config/custom-environment-variables.json @@ -10,6 +10,20 @@ "server": { "apiPath": "SERVER_APIPATH", "bodyLimit": "SERVER_BODYLIMIT", + "chefs": { + "forms": { + "form1": { + "name": "FORM_1_NAME", + "formId": "FORM_1_ID", + "formApiKey": "FORM_1_APIKEY" + }, + "form2": { + "name": "FORM_2_NAME", + "formId": "FORM_2_ID", + "formApiKey": "FORM_2_APIKEY" + } + } + }, "oidc": { "enabled": "SERVER_OIDC_ENABLED", "clientId": "SERVER_OIDC_CLIENTID", diff --git a/app/src/components/utils.ts b/app/src/components/utils.ts index 8c9819c5..1f53e384 100644 --- a/app/src/components/utils.ts +++ b/app/src/components/utils.ts @@ -1,9 +1,21 @@ +import config from 'config'; import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; import { getLogger } from './log'; +import { ChefsFormConfig, ChefsFormConfigData } from '../types/ChefsFormConfig'; const log = getLogger(module.filename); +/** + * @function getChefsApiKey + * Search for a CHEFS form Api Key + * @returns {string | undefined} The CHEFS form Api Key if it exists + */ +export function getChefsApiKey(formId: string): string | undefined { + const cfg = config.get('server.chefs.forms') as ChefsFormConfig; + return Object.values(cfg).find((o: ChefsFormConfigData) => o.formId === formId)?.formApiKey; +} + /** * @function getGitRevision * Gets the current git revision hash @@ -74,3 +86,19 @@ export function readIdpList(): object[] { return idpList; } + +/** + * @function redactSecrets + * Sanitizes objects by replacing sensitive data with a REDACTED string value + * @param {object} data An arbitrary object + * @param {string[]} fields An array of field strings to sanitize on + * @returns {object} An arbitrary object with specified secret fields marked as redacted + */ +export function redactSecrets(data: { [key: string]: unknown }, fields: Array): unknown { + if (fields && Array.isArray(fields) && fields.length) { + fields.forEach((field) => { + if (data[field]) data[field] = 'REDACTED'; + }); + } + return data; +} diff --git a/app/src/controllers/chefs.ts b/app/src/controllers/chefs.ts index e8f2b90e..9c34d4c4 100644 --- a/app/src/controllers/chefs.ts +++ b/app/src/controllers/chefs.ts @@ -1,49 +1,47 @@ +import config from 'config'; + import { chefsService } from '../services'; import { isTruthy } from '../components/utils'; import { IdentityProvider } from '../components/constants'; import type { NextFunction, Request, Response } from 'express'; import type { JwtPayload } from 'jsonwebtoken'; +import type { ChefsFormConfig, ChefsFormConfigData } from '../types/ChefsFormConfig'; +import type { ChefsSubmissionDataSource } from '../types/ChefsSubmissionDataSource'; const controller = { - exportSubmissions: async (req: Request, res: Response, next: NextFunction) => { - try { - const response = await chefsService.exportSubmissions(req.params.formId); - res.status(200).send(response); - } catch (e: unknown) { - next(e); - } - }, - - getFormSubmissions: async (req: Request, res: Response, next: NextFunction) => { + getSubmissions: async (req: Request, res: Response, next: NextFunction) => { try { - const response = await chefsService.getFormSubmissions(req.params.formId); - - // IDIR users should be able to see all submissions - const filterToUser = (req.currentUser?.tokenPayload as JwtPayload).identity_provider !== IdentityProvider.IDIR; - - if (isTruthy(filterToUser)) { - res - .status(200) - .send( - response.filter( - (x: { createdBy: string }) => - x.createdBy.toUpperCase().substring(0, x.createdBy.indexOf('@idir')) === - (req.currentUser?.tokenPayload as JwtPayload).idir_username.toUpperCase() - ) + const cfg = config.get('server.chefs.forms') as ChefsFormConfig; + let formData = new Array(); + + await Promise.all( + Object.values(cfg).map(async (x: ChefsFormConfigData) => { + const data = await chefsService.getFormSubmissions(x.formId); + formData = formData.concat(data); + }) + ); + + /* + * Filter Data source + * IDIR users should be able to see all submissions + * BCeID/Business should only see their own submissions + */ + const filterData = (data: Array) => { + const filterToUser = (req.currentUser?.tokenPayload as JwtPayload).identity_provider !== IdentityProvider.IDIR; + + if (isTruthy(filterToUser)) { + return data.filter( + (x: { createdBy: string }) => + x.createdBy.toUpperCase().substring(0, x.createdBy.indexOf('@')) === + (req.currentUser?.tokenPayload as JwtPayload).bceid_username.toUpperCase() ); - } else { - res.status(200).send(response); - } - } catch (e: unknown) { - next(e); - } - }, + } else { + return data; + } + }; - getPublishedVersion: async (req: Request, res: Response, next: NextFunction) => { - try { - const response = await chefsService.getPublishedVersion(req.params.formId); - res.status(200).send(response); + res.status(200).send(filterData(formData)); } catch (e: unknown) { next(e); } @@ -51,34 +49,7 @@ const controller = { getSubmission: async (req: Request, res: Response, next: NextFunction) => { try { - const response = await chefsService.getSubmission(req.params.formSubmissionId); - res.status(200).send(response); - } catch (e: unknown) { - next(e); - } - }, - - getVersion: async (req: Request, res: Response, next: NextFunction) => { - try { - const response = await chefsService.getVersion(req.params.formId, req.params.versionId); - res.status(200).send(response); - } catch (e: unknown) { - next(e); - } - }, - - getVersionFields: async (req: Request, res: Response, next: NextFunction) => { - try { - const response = await chefsService.getVersionFields(req.params.formId, req.params.versionId); - res.status(200).send(response); - } catch (e: unknown) { - next(e); - } - }, - - getVersionSubmissions: async (req: Request, res: Response, next: NextFunction) => { - try { - const response = await chefsService.getVersionSubmissions(req.params.formId, req.params.versionId); + const response = await chefsService.getSubmission(req.query.formId as string, req.params.formSubmissionId); res.status(200).send(response); } catch (e: unknown) { next(e); diff --git a/app/src/middleware/requireChefsFormConfigData.ts b/app/src/middleware/requireChefsFormConfigData.ts new file mode 100644 index 00000000..80a21d80 --- /dev/null +++ b/app/src/middleware/requireChefsFormConfigData.ts @@ -0,0 +1,36 @@ +// @ts-expect-error api-problem lacks a defined interface; code still works fine +import Problem from 'api-problem'; + +import { getChefsApiKey } from '../components/utils'; + +import type { NextFunction, Request, Response } from 'express'; + +/** + * @function requireChefsFormConfigData + * Rejects the request if there is no form ID present in the request + * or if the given Form ID/Api Key is not configured + * @param {object} req Express request object + * @param {object} _res Express response object + * @param {function} next The next callback function + * @returns {function} Express middleware function + * @throws The error encountered upon failure + */ +export const requireChefsFormConfigData = (req: Request, _res: Response, next: NextFunction) => { + const params = { ...req.query, ...req.params }; + + if (!params.formId) { + throw new Problem(400, { + detail: 'Form ID not present in request.', + instance: req.originalUrl + }); + } + + if (!getChefsApiKey(params.formId as string)) { + throw new Problem(412, { + detail: 'Form not present or misconfigured.', + instance: req.originalUrl + }); + } + + next(); +}; diff --git a/app/src/routes/v1/chefs.ts b/app/src/routes/v1/chefs.ts index 4f5d73bb..f24321b7 100644 --- a/app/src/routes/v1/chefs.ts +++ b/app/src/routes/v1/chefs.ts @@ -1,5 +1,6 @@ import express from 'express'; import { chefsController } from '../../controllers'; +import { requireChefsFormConfigData } from '../../middleware/requireChefsFormConfigData'; import { requireSomeAuth } from '../../middleware/requireSomeAuth'; import type { NextFunction, Request, Response } from 'express'; @@ -7,41 +8,17 @@ import type { NextFunction, Request, Response } from 'express'; const router = express.Router(); router.use(requireSomeAuth); -// Export submissions endpoint -router.get('/forms/:formId/export', (req: Request, res: Response, next: NextFunction): void => { - chefsController.exportSubmissions(req, res, next); -}); - -// Form submissions endpoint -router.get('/forms/:formId/submissions', (req: Request, res: Response, next: NextFunction): void => { - chefsController.getFormSubmissions(req, res, next); -}); - -// Published version endpoint -router.get('/forms/:formId/version', (req: Request, res: Response, next: NextFunction): void => { - chefsController.getPublishedVersion(req, res, next); -}); - // Submission endpoint -router.get('/submission/:formSubmissionId', (req: Request, res: Response, next: NextFunction): void => { - chefsController.getSubmission(req, res, next); +router.get('/submissions', (req: Request, res: Response, next: NextFunction): void => { + chefsController.getSubmissions(req, res, next); }); -// Version endpoint -router.get('/forms/:formId/versions/:versionId', (req: Request, res: Response, next: NextFunction): void => { - chefsController.getVersion(req, res, next); -}); - -// Version fields endpoint -router.get('/forms/:formId/versions/:versionId/fields', (req: Request, res: Response, next: NextFunction): void => { - chefsController.getVersionFields(req, res, next); -}); - -// Version submissions endpoint +// Submission endpoint router.get( - '/forms/:formId/versions/:versionId/submissions', + '/submission/:formSubmissionId', + requireChefsFormConfigData, (req: Request, res: Response, next: NextFunction): void => { - chefsController.getVersionSubmissions(req, res, next); + chefsController.getSubmission(req, res, next); } ); diff --git a/app/src/services/chefs.ts b/app/src/services/chefs.ts index 9b22b602..3f70044f 100644 --- a/app/src/services/chefs.ts +++ b/app/src/services/chefs.ts @@ -2,7 +2,9 @@ import axios from 'axios'; import config from 'config'; -import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'; +import { getChefsApiKey } from '../components/utils'; + +import type { AxiosInstance, AxiosRequestConfig } from 'axios'; /** * @function chefsAxios @@ -10,84 +12,28 @@ import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig } fr * @param {AxiosRequestConfig} options Axios request config options * @returns {AxiosInstance} An axios instance */ -function chefsAxios(options: AxiosRequestConfig = {}): AxiosInstance { - const instance = axios.create({ +function chefsAxios(formId: string, options: AxiosRequestConfig = {}): AxiosInstance { + return axios.create({ baseURL: config.get('frontend.chefs.apiPath'), timeout: 10000, - ...options + ...options, + auth: { username: formId, password: getChefsApiKey(formId) ?? '' } }); - - instance.interceptors.request.use( - async (cfg: InternalAxiosRequestConfig) => { - cfg.auth = { username: config.get('frontend.chefs.formId'), password: config.get('frontend.chefs.formApiKey') }; - return Promise.resolve(cfg); - }, - (error: Error) => { - return Promise.reject(error); - } - ); - - return instance; } const service = { - exportSubmissions: async (formId: string) => { - try { - const response = await chefsAxios().get(`forms/${formId}/export`); - return response.data; - } catch (e: unknown) { - throw e; - } - }, - getFormSubmissions: async (formId: string) => { try { - const response = await chefsAxios().get(`forms/${formId}/submissions`); - return response.data; - } catch (e: unknown) { - throw e; - } - }, - - getPublishedVersion: async (formId: string) => { - try { - const response = await chefsAxios().get(`forms/${formId}/version`); - return response.data; - } catch (e: unknown) { - throw e; - } - }, - - getSubmission: async (formSubmissionId: string) => { - try { - const response = await chefsAxios().get(`submissions/${formSubmissionId}`); - return response.data; - } catch (e: unknown) { - throw e; - } - }, - - getVersion: async (formId: string, versionId: string) => { - try { - const response = await chefsAxios().get(`forms/${formId}/versions/${versionId}`); - return response.data; - } catch (e: unknown) { - throw e; - } - }, - - getVersionFields: async (formId: string, versionId: string) => { - try { - const response = await chefsAxios().get(`forms/${formId}/versions/${versionId}/fields`); + const response = await chefsAxios(formId).get(`forms/${formId}/submissions`); return response.data; } catch (e: unknown) { throw e; } }, - getVersionSubmissions: async (formId: string, versionId: string) => { + getSubmission: async (formId: string, formSubmissionId: string) => { try { - const response = await chefsAxios().get(`forms/${formId}/versions/${versionId}/submissions`); + const response = await chefsAxios(formId).get(`submissions/${formSubmissionId}`); return response.data; } catch (e: unknown) { throw e; diff --git a/app/src/types/ChefsFormConfig.ts b/app/src/types/ChefsFormConfig.ts new file mode 100644 index 00000000..b503641d --- /dev/null +++ b/app/src/types/ChefsFormConfig.ts @@ -0,0 +1,10 @@ +export type ChefsFormConfig = { + form1: ChefsFormConfigData; + form2: ChefsFormConfigData; +}; + +export type ChefsFormConfigData = { + name: string; + formId: string; + formApiKey: string; +}; diff --git a/app/src/types/ChefsSubmissionDataSource.ts b/app/src/types/ChefsSubmissionDataSource.ts new file mode 100644 index 00000000..82329924 --- /dev/null +++ b/app/src/types/ChefsSubmissionDataSource.ts @@ -0,0 +1,11 @@ +export type ChefsSubmissionDataSource = { + confirmationId: string; + createdAt: string; + formId: string; + formSubmissionStatusCode: string; + submissionId: string; + deleted: boolean; + createdBy: string; + formVersionId: string; + lateEntry: boolean; +}; diff --git a/frontend/src/components/layout/Navbar.vue b/frontend/src/components/layout/Navbar.vue index abfb6696..6b1cd92a 100644 --- a/frontend/src/components/layout/Navbar.vue +++ b/frontend/src/components/layout/Navbar.vue @@ -16,14 +16,11 @@ const { getIsAuthenticated } = storeToRefs(useAuthStore());
  • Home
  • -
  • - Stylings -
  • - Submissions + Initiatives
  • diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 1fbad98a..bb805625 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -23,6 +23,12 @@ const routes: Array = [ component: () => import('../views/HomeView.vue'), meta: { title: 'Home' } }, + { + path: '/initiatives', + name: RouteNames.INITIATIVES, + component: () => import('../views/InitiativesView.vue'), + meta: { title: 'Initiatives' } + }, { path: '/stylings', name: RouteNames.STYLINGS, diff --git a/frontend/src/services/chefsService.ts b/frontend/src/services/chefsService.ts index b9824573..5583fe8f 100644 --- a/frontend/src/services/chefsService.ts +++ b/frontend/src/services/chefsService.ts @@ -2,66 +2,18 @@ import { appAxios } from './interceptors'; export default { /** - * @function exportSubmissions + * @function getSubmissions * @returns {Promise} An axios response */ - exportSubmissions(formId: string, versionId: string) { - return appAxios().get(`chefs/forms/${formId}/export`, { - responseType: 'blob', - headers: { 'Content-Type': 'text/csv' }, - params: { - format: 'csv', - type: 'submissions', - version: versionId - } - }); - }, - - /** - * @function getFormSubmissions - * @returns {Promise} An axios response - */ - getFormSubmissions(formId: string) { - return appAxios().get(`chefs/forms/${formId}/submissions`); - }, - - /** - * @function getPublishedVersion - * @returns {Promise} An axios response - */ - getPublishedVersion(formId: string) { - return appAxios().get(`chefs/forms/${formId}/version`); + getSubmissions() { + return appAxios().get('chefs/submissions'); }, /** * @function getSubmission * @returns {Promise} An axios response */ - getSubmission(formSubmissionId: string) { - return appAxios().get(`chefs/submission/${formSubmissionId}`); - }, - - /** - * @function getVersion - * @returns {Promise} An axios response - */ - getVersion(formId: string, versionId: string) { - return appAxios().get(`chefs/forms/${formId}/versions/${versionId}`); - }, - - /** - * @function getVersionFields - * @returns {Promise} An axios response - */ - getVersionFields(formId: string, versionId: string) { - return appAxios().get(`chefs/forms/${formId}/versions/${versionId}/fields`); - }, - - /** - * @function getVersionSubmissions - * @returns {Promise} An axios response - */ - getVersionSubmissions(formId: string, versionId: string) { - return appAxios().get(`chefs/forms/${formId}/versions/${versionId}/submissions`); + getSubmission(formId: string, formSubmissionId: string) { + return appAxios().get(`chefs/submission/${formSubmissionId}`, { params: { formId } }); } }; diff --git a/frontend/src/utils/constants.ts b/frontend/src/utils/constants.ts index c89cf123..8622e7aa 100644 --- a/frontend/src/utils/constants.ts +++ b/frontend/src/utils/constants.ts @@ -11,6 +11,7 @@ export const RouteNames = Object.freeze({ DEVELOPER: 'developer', FORBIDDEN: 'forbidden', HOME: 'home', + INITIATIVES: 'initiatives', LOGIN: 'login', LOGOUT: 'logout', SUBMISSION: 'submission', diff --git a/frontend/src/views/InitiativesView.vue b/frontend/src/views/InitiativesView.vue new file mode 100644 index 00000000..1f2cfce7 --- /dev/null +++ b/frontend/src/views/InitiativesView.vue @@ -0,0 +1,10 @@ + + + diff --git a/frontend/src/views/SubmissionView.vue b/frontend/src/views/SubmissionView.vue index 5d32e711..db28811f 100644 --- a/frontend/src/views/SubmissionView.vue +++ b/frontend/src/views/SubmissionView.vue @@ -1,30 +1,24 @@ @@ -33,16 +27,3 @@ onMounted(async () => {
    {{ submission }}
    - - diff --git a/frontend/src/views/SubmissionsView.vue b/frontend/src/views/SubmissionsView.vue index fdad8229..8389f202 100644 --- a/frontend/src/views/SubmissionsView.vue +++ b/frontend/src/views/SubmissionsView.vue @@ -1,19 +1,14 @@ @@ -66,17 +60,15 @@ onMounted(async () => { - - - From bb94a848382b3e2cb929b8d423e4e2065406afe4 Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Wed, 29 Nov 2023 11:04:59 -0800 Subject: [PATCH 2/2] Resolve unit tests. Allow proper Axios options override. --- app/src/components/utils.ts | 2 +- app/src/controllers/chefs.ts | 5 +- .../middleware/requireChefsFormConfigData.ts | 2 +- app/src/services/chefs.ts | 4 +- app/src/types/ChefsFormConfig.ts | 4 +- frontend/src/router/index.ts | 2 +- frontend/src/store/chefsStore.ts | 49 ------------------- frontend/src/store/index.ts | 1 - frontend/src/utils/formatters.ts | 1 - .../unit/components/layout/Navbar.spec.ts | 8 ++- frontend/tests/unit/store/helloStore.spec.ts | 42 ---------------- 11 files changed, 13 insertions(+), 107 deletions(-) delete mode 100644 frontend/src/store/chefsStore.ts delete mode 100644 frontend/tests/unit/store/helloStore.spec.ts diff --git a/app/src/components/utils.ts b/app/src/components/utils.ts index 1f53e384..7e5023a0 100644 --- a/app/src/components/utils.ts +++ b/app/src/components/utils.ts @@ -13,7 +13,7 @@ const log = getLogger(module.filename); */ export function getChefsApiKey(formId: string): string | undefined { const cfg = config.get('server.chefs.forms') as ChefsFormConfig; - return Object.values(cfg).find((o: ChefsFormConfigData) => o.formId === formId)?.formApiKey; + return Object.values(cfg).find((o: ChefsFormConfigData) => o.id === formId)?.apiKey; } /** diff --git a/app/src/controllers/chefs.ts b/app/src/controllers/chefs.ts index 9c34d4c4..dd712af0 100644 --- a/app/src/controllers/chefs.ts +++ b/app/src/controllers/chefs.ts @@ -17,7 +17,7 @@ const controller = { await Promise.all( Object.values(cfg).map(async (x: ChefsFormConfigData) => { - const data = await chefsService.getFormSubmissions(x.formId); + const data = await chefsService.getFormSubmissions(x.id); formData = formData.concat(data); }) ); @@ -28,7 +28,8 @@ const controller = { * BCeID/Business should only see their own submissions */ const filterData = (data: Array) => { - const filterToUser = (req.currentUser?.tokenPayload as JwtPayload).identity_provider !== IdentityProvider.IDIR; + const tokenPayload = req.currentUser?.tokenPayload as JwtPayload; + const filterToUser = tokenPayload && tokenPayload.identity_provider !== IdentityProvider.IDIR; if (isTruthy(filterToUser)) { return data.filter( diff --git a/app/src/middleware/requireChefsFormConfigData.ts b/app/src/middleware/requireChefsFormConfigData.ts index 80a21d80..6cdeb491 100644 --- a/app/src/middleware/requireChefsFormConfigData.ts +++ b/app/src/middleware/requireChefsFormConfigData.ts @@ -26,7 +26,7 @@ export const requireChefsFormConfigData = (req: Request, _res: Response, next: N } if (!getChefsApiKey(params.formId as string)) { - throw new Problem(412, { + throw new Problem(501, { detail: 'Form not present or misconfigured.', instance: req.originalUrl }); diff --git a/app/src/services/chefs.ts b/app/src/services/chefs.ts index 3f70044f..c4617723 100644 --- a/app/src/services/chefs.ts +++ b/app/src/services/chefs.ts @@ -16,8 +16,8 @@ function chefsAxios(formId: string, options: AxiosRequestConfig = {}): AxiosInst return axios.create({ baseURL: config.get('frontend.chefs.apiPath'), timeout: 10000, - ...options, - auth: { username: formId, password: getChefsApiKey(formId) ?? '' } + auth: { username: formId, password: getChefsApiKey(formId) ?? '' }, + ...options }); } diff --git a/app/src/types/ChefsFormConfig.ts b/app/src/types/ChefsFormConfig.ts index b503641d..b9f0f096 100644 --- a/app/src/types/ChefsFormConfig.ts +++ b/app/src/types/ChefsFormConfig.ts @@ -5,6 +5,6 @@ export type ChefsFormConfig = { export type ChefsFormConfigData = { name: string; - formId: string; - formApiKey: string; + id: string; + apiKey: string; }; diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index bb805625..bff3d363 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -27,7 +27,7 @@ const routes: Array = [ path: '/initiatives', name: RouteNames.INITIATIVES, component: () => import('../views/InitiativesView.vue'), - meta: { title: 'Initiatives' } + meta: { requiresAuth: true, title: 'Initiatives' } }, { path: '/stylings', diff --git a/frontend/src/store/chefsStore.ts b/frontend/src/store/chefsStore.ts deleted file mode 100644 index a191fdbb..00000000 --- a/frontend/src/store/chefsStore.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { defineStore } from 'pinia'; -import { computed, ref } from 'vue'; - -import { chefsService } from '@/services'; -import { useAppStore } from '@/store'; - -import type { Ref } from 'vue'; - -export type ChefsStoreState = { - hello: Ref; -}; - -export const useChefsStore = defineStore('chefs', () => { - // Store - const appStore = useAppStore(); - - // State - const state: ChefsStoreState = { - hello: ref(null) - }; - - // Getters - const getters = { - getHello: computed(() => state.hello.value) - }; - - // Actions - async function helloWorld() { - try { - appStore.beginIndeterminateLoading(); - //state.hello.value = (await helloService.helloWorld()).data; - } finally { - appStore.endIndeterminateLoading(); - } - } - - return { - // State - ...state, - - // Getters - ...getters, - - // Actions - helloWorld - }; -}); - -export default useChefsStore; diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts index 7e90c9e7..414fd236 100644 --- a/frontend/src/store/index.ts +++ b/frontend/src/store/index.ts @@ -1,4 +1,3 @@ export { default as useAppStore } from './appStore'; export { default as useAuthStore } from './authStore'; export { default as useConfigStore } from './configStore'; -export { default as useChefsStore } from './chefsStore'; diff --git a/frontend/src/utils/formatters.ts b/frontend/src/utils/formatters.ts index 12972923..2c02a58f 100644 --- a/frontend/src/utils/formatters.ts +++ b/frontend/src/utils/formatters.ts @@ -1,7 +1,6 @@ import { format, parseJSON } from 'date-fns'; import { useConfigStore } from '@/store'; -import type { IdentityProvider } from '@/types'; function _dateFnsFormat(value: string, formatter: string) { const formatted = ''; diff --git a/frontend/tests/unit/components/layout/Navbar.spec.ts b/frontend/tests/unit/components/layout/Navbar.spec.ts index a821c39a..3b444597 100644 --- a/frontend/tests/unit/components/layout/Navbar.spec.ts +++ b/frontend/tests/unit/components/layout/Navbar.spec.ts @@ -54,10 +54,9 @@ describe('Navbar.vue', () => { } }); const linkEle = wrapper.findAll('a'); - expect(linkEle).toHaveLength(3); + expect(linkEle).toHaveLength(2); expect(linkEle[0].text()).toBe('Home'); - expect(linkEle[1].text()).toBe('Stylings'); - expect(linkEle[2].text()).toBe('Secured'); + expect(linkEle[1].text()).toBe('Initiatives'); }); it('shows correct navbar when false', async () => { @@ -77,9 +76,8 @@ describe('Navbar.vue', () => { } }); const linkEle = wrapper.findAll('a'); - expect(linkEle).toHaveLength(2); + expect(linkEle).toHaveLength(1); expect(linkEle[0].text()).toBe('Home'); - expect(linkEle[1].text()).toBe('Stylings'); }); }); }); diff --git a/frontend/tests/unit/store/helloStore.spec.ts b/frontend/tests/unit/store/helloStore.spec.ts deleted file mode 100644 index 360ee713..00000000 --- a/frontend/tests/unit/store/helloStore.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { setActivePinia, createPinia } from 'pinia'; - -import { helloService } from '@/services'; -import { useAppStore, useHelloStore } from '@/store'; - -import type { StoreGeneric } from 'pinia'; -import type { SpyInstance } from 'vitest'; - -beforeEach(() => { - setActivePinia(createPinia()); - vi.clearAllMocks(); -}); - -describe('Hello Store', () => { - let appStore: StoreGeneric; - let helloStore: StoreGeneric; - - let beginIndeterminateLoadingSpy: SpyInstance; - let endIndeterminateLoadingSpy: SpyInstance; - - let helloWorldSpy: SpyInstance; - - beforeEach(() => { - appStore = useAppStore(); - helloStore = useHelloStore(); - - beginIndeterminateLoadingSpy = vi.spyOn(appStore, 'beginIndeterminateLoading'); - endIndeterminateLoadingSpy = vi.spyOn(appStore, 'endIndeterminateLoading'); - helloWorldSpy = vi.spyOn(helloService, 'helloWorld'); - }); - - it('calls the service', async () => { - helloWorldSpy.mockReturnValue({ data: 'Hello world!' } as any); - - await helloStore.helloWorld(); - - expect(beginIndeterminateLoadingSpy).toHaveBeenCalledTimes(1); - expect(helloWorldSpy).toHaveBeenCalledTimes(1); - expect(helloStore.getHello).toBe('Hello world!'); - expect(endIndeterminateLoadingSpy).toHaveBeenCalledTimes(1); - }); -});