Skip to content

Commit

Permalink
PSP-9133 : Implement check/warning on frontend that backend and datab… (
Browse files Browse the repository at this point in the history
#4497)

Co-authored-by: Herrera <[email protected]>
  • Loading branch information
eddherrera and Herrera authored Nov 26, 2024
1 parent de5ac18 commit ee340a1
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 38 deletions.
16 changes: 8 additions & 8 deletions source/backend/api/Controllers/HealthController.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Pims.Api.Services;
using Swashbuckle.AspNetCore.Annotations;
using Model = Pims.Api.Models.Health;

Expand All @@ -14,19 +14,17 @@ namespace Pims.Api.Controllers
[Route("health")]
public class HealthController : ControllerBase
{
#region Variables
private readonly IWebHostEnvironment _environment;
#endregion
private readonly IEnvironmentService _environmentService;

#region Constructors

/// <summary>
/// Creates a new instances of a HealthController class, initializes it with the specified arguments.
/// </summary>
/// <param name="environment"></param>
public HealthController(IWebHostEnvironment environment)
/// <param name="environmentService"></param>
public HealthController(IEnvironmentService environmentService)
{
_environment = environment;
_environmentService = environmentService;
}
#endregion

Expand All @@ -42,7 +40,9 @@ public HealthController(IWebHostEnvironment environment)
[SwaggerOperation(Tags = new[] { "health" })]
public IActionResult Environment()
{
return new JsonResult(new Model.EnvModel(_environment));
var environment = _environmentService.GetEnvironmentVariables();

return new JsonResult(environment);
}
#endregion
}
Expand Down
5 changes: 5 additions & 0 deletions source/backend/api/Models/Health/EnvModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class EnvModel
/// get/set - The information version.
/// </summary>
public string InformationalVersion { get; set; }

/// <summary>
/// get/set - The DB version.
/// </summary>
public string DBVersion { get; set; }
#endregion

#region Constructors
Expand Down
28 changes: 28 additions & 0 deletions source/backend/api/Services/EnvironmentService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Hosting;
using Pims.Api.Models.Health;
using Pims.Dal.Repositories;

namespace Pims.Api.Services
{
public class EnvironmentService : IEnvironmentService
{
private readonly IWebHostEnvironment _environment;
private readonly ISystemConstantRepository _systemConstantRepository;

public EnvironmentService(IWebHostEnvironment environment, ISystemConstantRepository systemConstantRepository)
{
_environment = environment;
_systemConstantRepository = systemConstantRepository;
}

public EnvModel GetEnvironmentVariables()
{
EnvModel environment = new(_environment)
{
DBVersion = _systemConstantRepository.GetDataBaseVersion(),
};

return environment;
}
}
}
9 changes: 9 additions & 0 deletions source/backend/api/Services/IEnvironmentService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Pims.Api.Models.Health;

namespace Pims.Api.Services
{
public interface IEnvironmentService
{
EnvModel GetEnvironmentVariables();
}
}
1 change: 1 addition & 0 deletions source/backend/api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ private static void AddPimsApiServices(IServiceCollection services)
services.AddScoped<IDispositionStatusSolver, DispositionStatusSolver>();
services.AddScoped<IPropertyOperationService, PropertyOperationService>();
services.AddScoped<ITakeInteractionSolver, TakeInteractionSolver>();
services.AddScoped<IEnvironmentService, EnvironmentService>();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ namespace Pims.Dal.Repositories
public interface ISystemConstantRepository : IRepository<PimsStaticVariable>
{
IEnumerable<PimsStaticVariable> GetAll();

string GetDataBaseVersion();
}
}
6 changes: 6 additions & 0 deletions source/backend/dal/Repositories/SystemConstantRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public IEnumerable<PimsStaticVariable> GetAll()
{
return this.Context.PimsStaticVariables.AsNoTracking().ToArray();
}

public string GetDataBaseVersion()
{
return this.Context.PimsStaticVariables.Where(x => x.StaticVariableName == "DBVERSION")
.AsNoTracking().FirstOrDefault()?.StaticVariableValue ?? string.Empty;
}
#endregion
}
}
90 changes: 72 additions & 18 deletions source/frontend/src/components/common/ApiVersionInfo.test.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,96 @@
import { cleanup, render, waitForElementToBeRemoved } from '@testing-library/react';

import IApiVersion from '@/hooks/pims-api/interfaces/IApiVersion';

import { ApiVersionInfo } from './ApiVersionInfo';
import { act, render, waitForEffects, RenderOptions } from '@/utils/test-utils';
import { useApiHealth } from '@/hooks/pims-api/useApiHealth';

const defaultVersion: IApiVersion = {
environment: 'test',
version: '11.1.1.1',
fileVersion: '11.1.1.1',
informationalVersion: '11.1.1-1.999',
informationalVersion: '11.1.1-93.999',
dbVersion: '93.00',
};

const mockGetVersion = vi.fn(async () => {
return Promise.resolve({ data: defaultVersion });
});
const mockGetVersionApi = vi.fn();
const mockGetLiveApi = vi.fn();
const mockGetReady = vi.fn();

vi.mock('@/hooks/pims-api/useApiHealth', () => ({
useApiHealth: () => ({
getVersion: mockGetVersion,
}),
}));
vi.mock('@/hooks/pims-api/useApiHealth');
vi.mocked(useApiHealth).mockReturnValue({
getVersion: mockGetVersionApi,
getLive: mockGetLiveApi,
getReady: mockGetReady,
});

describe('ApiVersionInfo suite', () => {
const setup = (renderOptions: RenderOptions = {}) => {
const utils = render(<ApiVersionInfo />, {
...renderOptions,
});

return {
...utils,
};
};

afterEach(() => {
mockGetVersion.mockClear();
cleanup();
vi.clearAllMocks();
});

beforeEach(() => {
import.meta.env.VITE_PACKAGE_VERSION = '11.1.1-93.999';
mockGetVersionApi.mockResolvedValue({ data: defaultVersion } as any);
});

it('Displays version component', async () => {
const { asFragment, getByText } = render(<ApiVersionInfo />);
await waitForElementToBeRemoved(() => getByText('api unavailable'));
const { asFragment } = setup();
await waitForEffects();

expect(asFragment()).toMatchSnapshot();
});

it('Displays version information', async () => {
const { findByText } = render(<ApiVersionInfo />);
const element = await findByText(`v${defaultVersion.informationalVersion}`);
const { getByTestId } = setup();
await waitForEffects();

const element = getByTestId(`version-tag`);
expect(element).toHaveTextContent('v11.1.1-93.999');
expect(mockGetVersionApi).toHaveBeenCalledTimes(1);
});

it('Does not display version warning', async () => {
const { queryByTestId } = setup();
await waitForEffects();

const element = queryByTestId(`version-mismatch-warning`);
expect(element).not.toBeInTheDocument();
expect(mockGetVersionApi).toHaveBeenCalledTimes(1);
});

it('Does display version warning when API missmatch', async () => {
mockGetVersionApi.mockResolvedValue({
data: { ...defaultVersion, informationalVersion: 'xx' },
} as any);

const { queryByTestId } = setup();
await waitForEffects();

const element = queryByTestId(`version-mismatch-warning`);
expect(element).toBeInTheDocument();
expect(mockGetVersionApi).toHaveBeenCalledTimes(1);
});

it('Does display version warning when DB missmatch', async () => {
mockGetVersionApi.mockResolvedValue({
data: { ...defaultVersion, dbVersion: '00' },
} as any);

const { queryByTestId } = setup();
await waitForEffects();

const element = queryByTestId(`version-mismatch-warning`);
expect(element).toBeInTheDocument();
expect(mockGetVersion).toHaveBeenCalledTimes(1);
expect(mockGetVersionApi).toHaveBeenCalledTimes(1);
});
});
79 changes: 75 additions & 4 deletions source/frontend/src/components/common/ApiVersionInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import React from 'react';
import { AiOutlineExclamationCircle } from 'react-icons/ai';
import styled from 'styled-components';

import IApiVersion from '@/hooks/pims-api/interfaces/IApiVersion';
import { useApiHealth } from '@/hooks/pims-api/useApiHealth';
import useDeepCompareEffect from '@/hooks/util/useDeepCompareEffect';

import { InlineFlexDiv } from './styles';
import TooltipWrapper from './TooltipWrapper';

/**
* Provides a way to display the API version information.
* Makes an AJAX request to the API for the version information.
* @returns ApiVersionInfo component.
*/
export const ApiVersionInfo = () => {
const { getVersion } = useApiHealth();
const [version, setVersion] = React.useState<IApiVersion>();
const [version, setVersion] = React.useState<IApiVersion>(null);

useDeepCompareEffect(() => {
let isActive = true;
Expand All @@ -27,11 +32,77 @@ export const ApiVersionInfo = () => {
};
}, [getVersion]);

const findDBVersion = (frontEndVersion: string): string => {
// remove suffix
const frontEndVersionClean = frontEndVersion.substring(0, frontEndVersion.lastIndexOf('.'));
const start = frontEndVersion.lastIndexOf('-') + 1;
const end = frontEndVersion.lastIndexOf('.');

// Get DB Version.
const dbVersion = frontEndVersionClean.substring(start, end);
// Convert to 2 decimal version so it matches DB record.
const numericVersion = parseFloat(dbVersion).toFixed(2);

return numericVersion.toString();
};

const frontEndVersion = import.meta.env.VITE_PACKAGE_VERSION;
const frontEndDBVersion = findDBVersion(frontEndVersion);

const apiVersionMismatch = version?.informationalVersion !== frontEndVersion;
const dbVersionMismatch = version?.dbVersion !== frontEndDBVersion;

const versionMismatchMsg = (apiMismatch: boolean, dbMismatch: boolean): string => {
let msg = '';
if (apiMismatch || dbMismatch) {
msg = msg.concat(
`Warning: There is a version mismatch with the backend.
API: ${version?.informationalVersion}; DB: ${version?.dbVersion}`,
);
}

return msg;
};

return (
<div className="version" data-testid="version">
{version?.informationalVersion ? `v${version.informationalVersion ?? ''}` : 'api unavailable'}
</div>
<StyledContainer>
<div className="version" data-testid="version-tag">
{`v${frontEndVersion ?? ''}`}
</div>

{(apiVersionMismatch || dbVersionMismatch) && (
<VersionMissmatchDiv data-testid="version-mismatch-warning">
<TooltipWrapper
tooltipId="warning"
tooltip={versionMismatchMsg(apiVersionMismatch, dbVersionMismatch)}
className="warning"
>
<AiOutlineExclamationCircle size={20} />
</TooltipWrapper>
</VersionMissmatchDiv>
)}
</StyledContainer>
);
};

export default ApiVersionInfo;

const StyledContainer = styled.div`
display: flex;
gap: 10px;
flex-grow: 1;
flex-direction: row;
justify-content: flex-end;
align-items: center;
`;

export const VersionMissmatchDiv = styled(InlineFlexDiv)`
color: ${props => props.theme.css.textWarningColor};
background-color: ${props => props.theme.css.warningBackgroundColor};
border-radius: 0.4rem;
letter-spacing: 0.1rem;
padding: 0.2rem 0.5rem;
font-family: 'BCSans-Bold';
font-size: 1.4rem;
width: fit-content;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,41 @@
exports[`ApiVersionInfo suite > Displays version component 1`] = `
<DocumentFragment>
<div
class="version"
data-testid="version"
class="Toastify"
/>
<div />
.c0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
gap: 10px;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
-ms-flex-positive: 1;
flex-grow: 1;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
<div
class="c0"
>
v11.1.1-1.999
<div
class="version"
data-testid="version-tag"
>
v11.1.1-93.999
</div>
</div>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const defaultVersion: IApiVersion = {
version: '11.1.1.1',
fileVersion: '11.1.1.1',
informationalVersion: '11.1.1-1.999',
dbVersion: '93.00',
};

const mockGetVersion = vi.fn(async () => {
Expand Down
Loading

0 comments on commit ee340a1

Please sign in to comment.