Skip to content

Commit

Permalink
Introduce an equipment query
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Haarhoff committed May 30, 2024
1 parent db3aac1 commit a038472
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/queries/equipment/construct-view-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {pipe} from 'fp-ts/lib/function';
import * as E from 'fp-ts/Either';
import {Dependencies} from '../../dependencies';
import * as TE from 'fp-ts/TaskEither';
import {readModels} from '../../read-models';
import {
FailureWithStatus,
failureWithStatus,
} from '../../types/failureWithStatus';
import {ViewModel} from './view-model';
import {User} from '../../types';
import {StatusCodes} from 'http-status-codes';
import {sequenceS} from 'fp-ts/lib/Apply';

export type Params = {equipmentId: string; user: User};

export const constructViewModel =
(deps: Dependencies) =>
(params: Params): TE.TaskEither<FailureWithStatus, ViewModel> =>
pipe(
deps.getAllEvents(),
TE.map(events => ({
user: E.right(params.user),
name: pipe(
params.equipmentId,
readModels.equipment.get(events),
E.fromOption(() =>
failureWithStatus('No such equipment', StatusCodes.NOT_FOUND)()
),
E.map(equipment => equipment.name)
),
})),
TE.chainEitherK(sequenceS(E.Apply))
);
61 changes: 61 additions & 0 deletions src/queries/equipment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {Request, Response} from 'express';
import * as t from 'io-ts';
import {flow, pipe} from 'fp-ts/lib/function';
import * as TE from 'fp-ts/TaskEither';
import {Dependencies} from '../../dependencies';
import {getUserFromSession} from '../../authentication';
import {logInRoute} from '../../authentication/configure-auth-routes';
import {
FailureWithStatus,
failureWithStatus,
} from '../../types/failureWithStatus';
import {StatusCodes} from 'http-status-codes';
import {constructViewModel} from './construct-view-model';
import {Params} from './construct-view-model';
import {render} from './render';
import * as E from 'fp-ts/Either';
import {sequenceS} from 'fp-ts/lib/Apply';
import {formatValidationErrors} from 'io-ts-reporters';

const notLoggedIn = () =>
failureWithStatus('You are not logged in.', StatusCodes.UNAUTHORIZED)();

const invalidParams = flow(
formatValidationErrors,
failureWithStatus('Invalid request parameters', StatusCodes.BAD_REQUEST)
);

const getParams =
(deps: Dependencies) =>
(req: Request): E.Either<FailureWithStatus, Params> =>
pipe(
{
user: pipe(
req.session,
getUserFromSession(deps),
E.fromOption(notLoggedIn)
),
equipmentId: pipe(
req.params,
t.strict({equipment: t.string}).decode,
E.mapLeft(invalidParams),
E.map(params => params.equipment)
),
},
sequenceS(E.Apply)
);

export const equipment =
(deps: Dependencies) => async (req: Request, res: Response) => {
await pipe(
req,
getParams(deps),
TE.fromEither,
TE.chain(constructViewModel(deps)),
TE.map(render),
TE.matchW(
() => res.redirect(logInRoute),
page => res.status(200).send(page)
)
)();
};
11 changes: 11 additions & 0 deletions src/queries/equipment/render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {pipe} from 'fp-ts/lib/function';
import {pageTemplate} from '../../templates';
import {html} from '../../types/html';
import * as O from 'fp-ts/Option';
import {ViewModel} from './view-model';

export const render = (viewModel: ViewModel) =>
pipe(
html`<h1>${viewModel.name}</h1>`,
pageTemplate(viewModel.name, O.some(viewModel.user))
);
6 changes: 6 additions & 0 deletions src/queries/equipment/view-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {User} from '../../types';

export type ViewModel = {
user: User;
name: string;
};
2 changes: 2 additions & 0 deletions src/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {superUsers} from './super-users';
import asyncHandler from 'express-async-handler';
import {area} from './area';
import {allEquipment} from './all-equipment';
import {equipment} from './equipment';

export const queries = {
allEquipment: flow(allEquipment, asyncHandler),
areas: flow(areas, asyncHandler),
area: flow(area, asyncHandler),
equipment: flow(equipment, asyncHandler),
superUsers: flow(superUsers, asyncHandler),
landing: flow(landing, asyncHandler),
};
18 changes: 18 additions & 0 deletions src/read-models/equipment/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {pipe} from 'fp-ts/lib/function';
import * as O from 'fp-ts/Option';
import {DomainEvent, isEventOfType} from '../../types';
import * as RA from 'fp-ts/ReadonlyArray';

type Equipment = {
name: string;
};

export const get =
(events: ReadonlyArray<DomainEvent>) =>
(equipmentId: string): O.Option<Equipment> =>
pipe(
events,
RA.filter(isEventOfType('EquipmentAdded')),
RA.filter(event => event.id === equipmentId),
RA.head
);
2 changes: 2 additions & 0 deletions src/read-models/equipment/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {get} from './get';
import {getAll} from './get-all';
import {getForArea} from './get-for-area';

export const equipment = {
get,
getAll,
getForArea,
};
1 change: 1 addition & 0 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const createRouter = (deps: Dependencies, conf: Config): Router => {
'/equipment/add',
http.formPost(deps, commands.equipment.add, '/equipment')
);
router.get('/equipment/:equipment', queries.equipment(deps));

router.get('/super-users', queries.superUsers(deps));
router.get(
Expand Down

0 comments on commit a038472

Please sign in to comment.