Skip to content

Commit

Permalink
fix(#139): improve executed queries on authentication modal (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
followynne committed Sep 15, 2024
1 parent 8fe464c commit fb6ff0f
Show file tree
Hide file tree
Showing 16 changed files with 775 additions and 447 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

[![DotNET-build](https://github.com/serilog-contrib/serilog-ui/actions/workflows/DotNET-build.yml/badge.svg?branch=master)](https://github.com/serilog-contrib/serilog-ui/actions/workflows/DotNET-build.yml)
[![DotNET Coverage](https://sonarcloud.io/api/project_badges/measure?project=followynne_serilog-ui&metric=coverage)](https://sonarcloud.io/summary/new_code?id=followynne_serilog-ui)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=followynne_serilog-ui&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=followynne_serilog-ui)

[![JS-build](https://github.com/serilog-contrib/serilog-ui/actions/workflows/JS-build.yml/badge.svg?branch=master)](https://github.com/serilog-contrib/serilog-ui/actions/workflows/JS-build.yml)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=followynne_serilog-ui_assets&metric=coverage)](https://sonarcloud.io/summary/new_code?id=followynne_serilog-ui_assets)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=followynne_serilog-ui_assets&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=followynne_serilog-ui_assets)

A simple Serilog log viewer for the following sinks:

Expand Down
60 changes: 30 additions & 30 deletions src/Serilog.Ui.Web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,62 +14,62 @@
"test:ui": "vitest --ui --reporter=default"
},
"dependencies": {
"@fontsource/mononoki": "^5.0.11",
"@mantine/core": "^7.12.1",
"@mantine/dates": "^7.12.1",
"@mantine/hooks": "^7.12.1",
"@mantine/notifications": "^7.12.1",
"@tabler/icons-react": "^3.12.0",
"@tanstack/react-query": "^5.51.23",
"dayjs": "^1.11.12",
"jose": "^5.6.3",
"@fontsource/mononoki": "^5.1.0",
"@mantine/core": "^7.12.2",
"@mantine/dates": "^7.12.2",
"@mantine/hooks": "^7.12.2",
"@mantine/notifications": "^7.12.2",
"@tabler/icons-react": "^3.16.0",
"@tanstack/react-query": "^5.56.2",
"dayjs": "^1.11.13",
"jose": "^5.9.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.2",
"react-router-dom": "^6.26.1",
"react-hook-form": "^7.53.0",
"react-router-dom": "^6.26.2",
"xml-formatter": "^3.6.3"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@faker-js/faker": "^9.0.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^22.4.0",
"@types/react": "^18.3.3",
"@types/node": "^22.5.4",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"@vitest/coverage-istanbul": "^2.0.5",
"@vitest/ui": "^2.0.5",
"@vitest/coverage-istanbul": "^2.1.0",
"@vitest/ui": "^2.1.0",
"@welldone-software/why-did-you-render": "^8.0.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-html": "^8.1.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.9.0",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^7.1.0",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react": "^7.36.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-testing-library": "^6.3.0",
"eslint-plugin-vitest": "^0.5.4",
"eslint-plugin-vitest-globals": "^1.5.0",
"happy-dom": "^14.12.3",
"msw": "^2.3.5",
"postcss": "^8.4.41",
"happy-dom": "^15.7.4",
"msw": "^2.4.6",
"postcss": "^8.4.45",
"postcss-preset-mantine": "^1.17.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.3.3",
"prettier-plugin-organize-imports": "^4.0.0",
"shiki": "^1.13.0",
"shiki": "^1.17.6",
"testing-library-selector": "^0.3.1",
"typescript": "^5.5.4",
"typescript-eslint": "^8.1.0",
"vite": "^5.4.1",
"vite-plugin-checker": "^0.7.2",
"typescript": "^5.6.2",
"typescript-eslint": "^8.5.0",
"vite": "^5.4.5",
"vite-plugin-checker": "^0.8.0",
"vite-plugin-mkcert": "^1.17.6",
"vite-tsconfig-paths": "^5.0.1",
"vitest": "^2.0.5",
"vitest": "^2.1.0",
"vitest-sonar-reporter": "^2.0.0"
},
"engines": {
Expand Down
8 changes: 5 additions & 3 deletions src/Serilog.Ui.Web/src/__tests__/_setup/mocks/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import dayjs from 'dayjs';
import { HttpResponse, http } from 'msw';
import { http, HttpResponse } from 'msw';
import {
AuthType,
EncodedSeriLogObject,
LogLevel,
SearchParameters,
SortDirectionOptions,
SortPropertyOptions,
} from '../../../types/types';
import { dbKeysMock, fakeLogs, fakeLogs2ndTable, fakeLogs3rdTable } from './samples';
import { defaultAuthType } from '../../../app/hooks/useSerilogUiProps.tsx';

export const developmentListenersHost = ['https://localhost:3001'];

Expand All @@ -20,7 +22,7 @@ const tableLogs = (table: string | null) => {
export const handlers = developmentListenersHost.flatMap((dlh) => [
http.get(`${dlh}/api/logs`, ({ request }) => {
const auth = request.headers.get('authorization');
if (!auth) return HttpResponse.error();
if (defaultAuthType !== AuthType.Custom && !auth) return HttpResponse.error();

const req = new URL(request.url);
const params = getSearchParams(req.searchParams);
Expand Down Expand Up @@ -49,7 +51,7 @@ export const handlers = developmentListenersHost.flatMap((dlh) => [
http.get(`${dlh}/api/keys`, ({ request }) => {
const auth = request.headers.get('authorization');

return !auth ? HttpResponse.error() : HttpResponse.json(dbKeysMock);
return defaultAuthType !== AuthType.Custom && !auth ? HttpResponse.error() : HttpResponse.json(dbKeysMock);
}),
]);

Expand Down
4 changes: 3 additions & 1 deletion src/Serilog.Ui.Web/src/__tests__/_setup/mocks/samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ export const fakeLogs: SearchResult = {

export const fakeLogs2ndTable: SearchResult = {
...fakeLogs,
logs: faker.helpers.multiple(createRandomLogWithAdditionalColumns, { count: 95 }),
logs: faker.helpers.multiple(() => createRandomLogWithAdditionalColumns(), {
count: 95,
}),
total: 95,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ vi.mock('../../../app/hooks/useAuthProperties', () => ({
headers: { headers: headers() },
routePrefix: '',
},
isHeaderReady: true,
}),
}));

Expand Down
4 changes: 4 additions & 0 deletions src/Serilog.Ui.Web/src/__tests__/hooks/useQueryAuth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const mockedProps = {
};
const mockAuthProps = {
authHeader: '',
isHeaderReady: false,
};
const mockTableKeys = {
refetch: vi.fn(),
Expand Down Expand Up @@ -47,6 +48,7 @@ describe('useQueryAuth', () => {
async (response) => {
mockedProps.blockHomeAccess = true;
mockAuthProps.authHeader = 'ready';
mockAuthProps.isHeaderReady = true;
mockTableKeys.refetch.mockResolvedValueOnce({ data: response });

renderHook(() => useQueryAuth());
Expand All @@ -61,6 +63,8 @@ describe('useQueryAuth', () => {
it('runs request and set true if response is array with element', async () => {
mockedProps.blockHomeAccess = true;
mockAuthProps.authHeader = 'ready';
mockAuthProps.isHeaderReady = true;

mockTableKeys.refetch.mockResolvedValueOnce({ data: ['key'] });

renderHook(() => useQueryAuth());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@ import { Button, Modal } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconLockCheck, IconLockOpen } from '@tabler/icons-react';
import { useSerilogUiProps } from 'app/hooks/useSerilogUiProps';
import { Suspense, lazy, memo } from 'react';
import { lazy, memo, Suspense } from 'react';
import { AuthType } from 'types/types';
import { useAuthProperties } from '../../hooks/useAuthProperties';
import { isStringGuard } from '../../util/guards';

const BasicModal = lazy(() => import('./BasicModal'));
const JwtModal = lazy(() => import('./JwtModal'));

const AuthorizeButton = () => {
const [opened, { open, close }] = useDisclosure(false);
const { authType } = useSerilogUiProps();
const { authHeader } = useAuthProperties();
const { isHeaderReady } = useAuthProperties();

if (![AuthType.Basic, AuthType.Jwt].includes(authType ?? AuthType.Custom)) return null;

const isHeaderReady = isStringGuard(authHeader);

return (
<>
<Button color="green" size="compact-md" onClick={open}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { Alert, Button, Fieldset, Group, PasswordInput, TextInput } from '@mantine/core';
import { IconAlertTriangleFilled } from '@tabler/icons-react';
import { useAuthProperties } from 'app/hooks/useAuthProperties';
import { isStringGuard } from 'app/util/guards';
import { ChangeEvent, useState } from 'react';

const BasicModal = ({ onClose }: { onClose: () => void }) => {
const { authHeader, basic_pwd, basic_user, clearAuthState, saveAuthState } =
const { isHeaderReady, basic_pwd, basic_user, clearAuthState, saveAuthState } =
useAuthProperties();
const [user, setUser] = useState(basic_user ?? '');
const [pwd, setPwd] = useState(basic_pwd ?? '');

const isHeaderReady = isStringGuard(authHeader);

const onSave = async () => {
saveAuthState({ basic_pwd: pwd, basic_user: user });
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Button, Fieldset, Group, PasswordInput } from '@mantine/core';
import { useState, type ChangeEvent } from 'react';
import { type ChangeEvent, useState } from 'react';
import { useAuthProperties } from '../../hooks/useAuthProperties';
import { isStringGuard } from '../../util/guards';

const JwtModal = ({ onClose }: { onClose: () => void }) => {
const { authHeader, clearAuthState, jwt_bearerToken, saveAuthState } =
const { isHeaderReady, clearAuthState, jwt_bearerToken, saveAuthState } =
useAuthProperties();
const [currentInput, setCurrentInput] = useState(jwt_bearerToken ?? '');

const isHeaderReady = isStringGuard(authHeader);

const onSave = async () => {
saveAuthState({ jwt_bearerToken: currentInput });
};
Expand Down
26 changes: 13 additions & 13 deletions src/Serilog.Ui.Web/src/app/hooks/useAuthProperties.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import {
IAuthPropertiesData,
IAuthPropertiesStorageKeys,
checkErrors,
clearAuth,
getAuthorizationHeader,
IAuthPropertiesData,
IAuthPropertiesStorageKeys,
initialAuthProps,
saveAuthKey,
} from 'app/util/auth';
import { createRequestInit } from 'app/util/queries';
import {
createContext,
useCallback,
useContext,
useMemo,
useState,
type ReactNode,
} from 'react';
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from 'react';
import { useSerilogUiProps } from './useSerilogUiProps';
import { isStringGuard } from '../util/guards.ts';
import { AuthType } from '../../types/types.ts';

interface AuthProps {
authProps: IAuthPropertiesData;
authHeader: string;
isHeaderReady?: boolean;
fetchInfo: {
headers: RequestInit;
routePrefix?: string;
Expand All @@ -35,6 +31,7 @@ interface AuthProps {
const AuthPropertiesContext = createContext<AuthProps>({
authProps: {},
authHeader: '',
isHeaderReady: false,
fetchInfo: {
headers: {},
},
Expand All @@ -59,7 +56,8 @@ export const AuthPropertiesProvider = ({
() => getAuthorizationHeader(authInfo, authType),
[authInfo, authType],
);

const isHeaderReady = authType === AuthType.Custom || isStringGuard(authHeader);

const fetchInfo = useMemo(
() => ({
headers: createRequestInit(authType, authHeader),
Expand Down Expand Up @@ -100,11 +98,12 @@ export const AuthPropertiesProvider = ({
() => ({
authProps: authInfo,
authHeader,
isHeaderReady,
fetchInfo,
saveAuthState,
clearAuthState,
}),
[authInfo, authHeader, clearAuthState, fetchInfo, saveAuthState],
[authInfo, authHeader, isHeaderReady, clearAuthState, fetchInfo, saveAuthState],
);

return (
Expand All @@ -115,12 +114,13 @@ export const AuthPropertiesProvider = ({
};

export const useAuthProperties = () => {
const { authProps, authHeader, fetchInfo, clearAuthState, saveAuthState } =
const { authProps, authHeader, fetchInfo, isHeaderReady, clearAuthState, saveAuthState } =
useContext(AuthPropertiesContext);

return {
...authProps,
authHeader,
isHeaderReady,
clearAuthState,
fetchInfo,
saveAuthState,
Expand Down
5 changes: 2 additions & 3 deletions src/Serilog.Ui.Web/src/app/hooks/useQueryAuth.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { isArrayGuard, isStringGuard } from 'app/util/guards';
import { isArrayGuard } from 'app/util/guards';
import { useEffect } from 'react';
import { useAuthProperties } from './useAuthProperties';
import { useQueryTableKeys } from './useQueryTableKeys';
import { useSerilogUiProps } from './useSerilogUiProps';

export const useQueryAuth = () => {
const { blockHomeAccess, setAuthenticatedFromAccessDenied } = useSerilogUiProps();
const { authHeader } = useAuthProperties();
const { isHeaderReady } = useAuthProperties();
const { refetch } = useQueryTableKeys();
const isHeaderReady = isStringGuard(authHeader);

useEffect(() => {
if (!blockHomeAccess) return;
Expand Down
4 changes: 3 additions & 1 deletion src/Serilog.Ui.Web/src/app/hooks/useQueryLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { useAuthProperties } from './useAuthProperties';
import { useSearchForm } from './useSearchForm';

const useQueryLogs = () => {
const { fetchInfo } = useAuthProperties();
const { fetchInfo, isHeaderReady } = useAuthProperties();
const { getValues, watch } = useSearchForm();
const currentDbKey = watch('table');

return useQuery({
enabled: false,
queryKey: ['get-logs'],
queryFn: async () => {
if (!isHeaderReady) return null;

return currentDbKey
? await fetchLogs(getValues(), fetchInfo.headers, fetchInfo.routePrefix)
: null;
Expand Down
4 changes: 3 additions & 1 deletion src/Serilog.Ui.Web/src/app/hooks/useQueryTableKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { useSerilogUiProps } from './useSerilogUiProps';

export const useQueryTableKeys = (shouldNotify = false) => {
const { blockHomeAccess, setAuthenticatedFromAccessDenied } = useSerilogUiProps();
const { authHeader, fetchInfo } = useAuthProperties();
const { authHeader, isHeaderReady, fetchInfo } = useAuthProperties();

return useQuery({
queryKey: ['get-keys', fetchInfo.routePrefix, authHeader],
queryFn: async () => {
if (!isHeaderReady) return [];

if (fetchInfo?.routePrefix === undefined) return [];

const result = await fetchKeys(
Expand Down
Loading

0 comments on commit fb6ff0f

Please sign in to comment.