Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAMS-283 - Add office staff sync timer #923

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/functions/lib/controllers/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ import { CamsHttpResponseInit } from '../adapters/utils/http-response';
export interface CamsController {
handleRequest(context: ApplicationContext): Promise<CamsHttpResponseInit<object | undefined>>;
}

export interface CamsTimerController {
handleTimer(context: ApplicationContext): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { OfficesController } from './offices.controller';
import { OFFICES } from '../../../../../common/src/cams/test-utilities/offices.mock';
import { CamsError } from '../../common-errors/cams-error';
import { mockCamsHttpRequest } from '../../testing/mock-data/cams-http-request-helper';
import { UnknownError } from '../../common-errors/unknown-error';

let getOffices = jest.fn();
let getOfficeAttorneys = jest.fn();
const syncOfficeStaff = jest.fn();

jest.mock('../../use-cases/offices/offices', () => {
return {
OfficesUseCase: jest.fn().mockImplementation(() => {
return {
getOffices,
getOfficeAttorneys,
syncOfficeStaff,
};
}),
};
Expand All @@ -30,6 +33,20 @@ describe('offices controller tests', () => {
jest.restoreAllMocks();
});

test('should return successful when handleTimer is called', async () => {
const controller = new OfficesController();
await expect(controller.handleTimer(applicationContext)).resolves.toBeFalsy();
expect(syncOfficeStaff).toHaveBeenCalled();
});

test('should throw error when handleTimer throws', async () => {
const error = new UnknownError('TEST_MODULE');
syncOfficeStaff.mockRejectedValue(error);
const controller = new OfficesController();
await expect(controller.handleTimer(applicationContext)).rejects.toThrow(error);
expect(syncOfficeStaff).toHaveBeenCalled();
});

test('should return successful response', async () => {
getOffices = jest.fn().mockResolvedValue(OFFICES);

Expand Down
12 changes: 10 additions & 2 deletions backend/functions/lib/controllers/offices/offices.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@ import { ApplicationContext } from '../../adapters/types/basic';
import { OfficeDetails } from '../../../../../common/src/cams/courts';
import { CamsHttpResponseInit, httpSuccess } from '../../adapters/utils/http-response';
import { getCamsError } from '../../common-errors/error-utilities';
import { CamsController } from '../controller';
import { CamsController, CamsTimerController } from '../controller';
import { CamsUserReference } from '../../../../../common/src/cams/users';
import { BadRequestError } from '../../common-errors/bad-request';

const MODULE_NAME = 'OFFICES-CONTROLLER';

export class OfficesController implements CamsController {
export class OfficesController implements CamsController, CamsTimerController {
private readonly useCase: OfficesUseCase;

constructor() {
this.useCase = new OfficesUseCase();
}

public async handleTimer(context: ApplicationContext): Promise<void> {
try {
await this.useCase.syncOfficeStaff(context);
} catch (originalError) {
throw getCamsError(originalError, MODULE_NAME);
}
}

public async handleRequest(
context: ApplicationContext,
): Promise<CamsHttpResponseInit<OfficeDetails[] | CamsUserReference[]>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,27 @@ describe('orders controller tests', () => {
jest.clearAllMocks();
});

test('should return successful when handleTimer is called', async () => {
const syncOrdersSpy = jest
.spyOn(OrdersUseCase.prototype, 'syncOrders')
.mockResolvedValue(syncResponse);

const controller = new OrdersController(applicationContext);
await expect(controller.handleTimer(applicationContext)).resolves.toBeFalsy();
expect(syncOrdersSpy).toHaveBeenCalled();
});

test('should throw error when handleTimer throws', async () => {
const error = new UnknownError('TEST_MODULE');
const syncOrdersSpy = jest
.spyOn(OrdersUseCase.prototype, 'syncOrders')
.mockRejectedValue(error);

const controller = new OrdersController(applicationContext);
await expect(controller.handleTimer(applicationContext)).rejects.toThrow(error);
expect(syncOrdersSpy).toHaveBeenCalled();
});

test('should get orders', async () => {
const mockRead = jest
.spyOn(MockHumbleQuery.prototype, 'fetchAll')
Expand Down Expand Up @@ -95,16 +116,6 @@ describe('orders controller tests', () => {
expect(updateOrderSpy).toHaveBeenCalledWith(applicationContext, id, orderTransfer);
});

test('should sync orders', async () => {
const syncOrdersSpy = jest
.spyOn(OrdersUseCase.prototype, 'syncOrders')
.mockResolvedValue(syncResponse);

const controller = new OrdersController(applicationContext);
await controller.syncOrders(applicationContext);
expect(syncOrdersSpy).toHaveBeenCalledWith(applicationContext, undefined);
});

test('should get suggested cases', async () => {
const suggestedCases = [CASE_SUMMARIES[0]];

Expand Down
17 changes: 11 additions & 6 deletions backend/functions/lib/controllers/orders/orders.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { BadRequestError } from '../../common-errors/bad-request';
import { CamsHttpResponseInit, httpSuccess } from '../../adapters/utils/http-response';
import { getCamsError } from '../../common-errors/error-utilities';
import HttpStatusCodes from '../../../../../common/src/api/http-status-codes';
import { CamsController } from '../controller';
import { CamsController, CamsTimerController } from '../controller';
import { NotFoundError } from '../../common-errors/not-found-error';

const MODULE_NAME = 'ORDERS-CONTROLLER';
Expand All @@ -33,7 +33,7 @@ export type UpdateOrderResponse = CamsHttpResponseInit;
export type SyncOrdersResponse = CamsHttpResponseInit<SyncOrdersStatus>;
export type ManageConsolidationResponse = CamsHttpResponseInit<ConsolidationOrder[]>;

export class OrdersController implements CamsController {
export class OrdersController implements CamsController, CamsTimerController {
private readonly useCase: OrdersUseCase;

constructor(context: ApplicationContext) {
Expand All @@ -46,13 +46,18 @@ export class OrdersController implements CamsController {
getConsolidationOrdersRepository(context),
);
}

public async handleTimer(context: ApplicationContext): Promise<void> {
try {
await this.useCase.syncOrders(context);
} catch (originalError) {
throw getCamsError(originalError, MODULE_NAME);
}
}

public async handleRequest(
context: ApplicationContext,
): Promise<CamsHttpResponseInit<CaseSummary[] | Order[] | SyncOrdersStatus | undefined>> {
if (!context.request) {
return this.syncOrders(context);
}

const simplePath = new URL(context.request.url).pathname.split('/')[2];

switch (simplePath) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { LoggerImpl } from '../lib/adapters/services/logger.service';
import { CamsError } from '../lib/common-errors/cams-error';
import timerTrigger from './office-staff-sync.function';
import { Timer } from '@azure/functions';
import { createMockAzureFunctionContext } from '../azure/testing-helpers';
import { OfficesController } from '../lib/controllers/offices/offices.controller';

describe('Office Staff Sync Function tests', () => {
const context = createMockAzureFunctionContext();
const timer: Timer = {
isPastDue: false,
schedule: {
adjustForDST: false,
},
scheduleStatus: {
last: '',
next: '',
lastUpdated: '',
},
};

test('Should call offices controller method handleTimer', async () => {
const handleTimer = jest
.spyOn(OfficesController.prototype, 'handleTimer')
.mockImplementation(() => Promise.resolve());
await timerTrigger(timer, context);
expect(handleTimer).toHaveBeenCalled();
});

test('Should log a camsError if handleTimer throws a CamsError', async () => {
const handleTimer = jest
.spyOn(OfficesController.prototype, 'handleTimer')
.mockRejectedValue(new CamsError('TEST_MODULE', { message: 'error' }));
const camsError = jest.spyOn(LoggerImpl.prototype, 'camsError').mockImplementation(() => {});
await timerTrigger(timer, context);
expect(handleTimer).toHaveBeenCalled();
expect(camsError).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as dotenv from 'dotenv';
import { app, InvocationContext, Timer } from '@azure/functions';
import ContextCreator from '../azure/application-context-creator';
import { initializeApplicationInsights } from '../azure/app-insights';
import { toAzureError } from '../azure/functions';
import { OfficesController } from '../lib/controllers/offices/offices.controller';

dotenv.config();

initializeApplicationInsights();

const MODULE_NAME = 'OFFICE-STAFF-SYNC-FUNCTION';

export default async function timerTrigger(
_myTimer: Timer,
invocationContext: InvocationContext,
): Promise<void> {
const logger = ContextCreator.getLogger(invocationContext);
try {
const appContext = await ContextCreator.getApplicationContext({ invocationContext, logger });
const controller = new OfficesController();
await controller.handleTimer(appContext);
} catch (error) {
toAzureError(logger, MODULE_NAME, error);
}
}

app.timer('office-staff-sync', {
schedule: '0 0 * * * *',
handler: timerTrigger,
});
43 changes: 11 additions & 32 deletions backend/functions/orders-sync/orders-sync.function.test.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,9 @@
import { LoggerImpl } from '../lib/adapters/services/logger.service';
import { CamsError } from '../lib/common-errors/cams-error';
import { OrdersController } from '../lib/controllers/orders/orders.controller';
import { SyncOrdersStatus } from '../lib/use-cases/orders/orders';
import timerTrigger from './orders-sync.function';
import { Timer } from '@azure/functions';
import { buildTestResponseSuccess, createMockAzureFunctionContext } from '../azure/testing-helpers';

const syncResponse: SyncOrdersStatus = {
options: {
txIdOverride: '10',
},
initialSyncState: {
documentType: 'ORDERS_SYNC_STATE',
txId: '464',
id: '28e35739-58cd-400b-9d4b-26969773618b',
},
finalSyncState: {
documentType: 'ORDERS_SYNC_STATE',
txId: '464',
id: '28e35739-58cd-400b-9d4b-26969773618b',
},
length: 13,
startingTxId: '10',
maxTxId: '464',
};
import { createMockAzureFunctionContext } from '../azure/testing-helpers';
import { OrdersController } from '../lib/controllers/orders/orders.controller';

describe('Orders Sync Function tests', () => {
const context = createMockAzureFunctionContext();
Expand All @@ -39,22 +19,21 @@ describe('Orders Sync Function tests', () => {
},
};

test('Should call orders controller method syncOrders', async () => {
const { camsHttpResponse } = buildTestResponseSuccess<SyncOrdersStatus>({ data: syncResponse });
const syncOrders = jest
.spyOn(OrdersController.prototype, 'syncOrders')
.mockResolvedValue(camsHttpResponse);
test('Should call orders controller method handleTimer', async () => {
const handleTimer = jest
.spyOn(OrdersController.prototype, 'handleTimer')
.mockImplementation(() => Promise.resolve());
await timerTrigger(timer, context);
expect(syncOrders).toHaveBeenCalled();
expect(handleTimer).toHaveBeenCalled();
});

test('Should log a camsError if syncOrders throws a CamsError', async () => {
const syncOrders = jest
.spyOn(OrdersController.prototype, 'syncOrders')
test('Should log a camsError if handleTimer throws a CamsError', async () => {
const handleTimer = jest
.spyOn(OrdersController.prototype, 'handleTimer')
.mockRejectedValue(new CamsError('TEST_MODULE', { message: 'error' }));
const camsError = jest.spyOn(LoggerImpl.prototype, 'camsError').mockImplementation(() => {});
await timerTrigger(timer, context);
expect(syncOrders).toHaveBeenCalled();
expect(handleTimer).toHaveBeenCalled();
expect(camsError).toHaveBeenCalled();
});
});
2 changes: 1 addition & 1 deletion backend/functions/orders-sync/orders-sync.function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default async function timerTrigger(
try {
const appContext = await ContextCreator.getApplicationContext({ invocationContext, logger });
const ordersController = new OrdersController(appContext);
await ordersController.handleRequest(appContext);
await ordersController.handleTimer(appContext);
} catch (error) {
toAzureError(logger, MODULE_NAME, error);
}
Expand Down
Loading