Skip to content

Commit

Permalink
Merge pull request #64 from kenneth-gray/refactor-server
Browse files Browse the repository at this point in the history
Refactor business logic so that express can be extracted to the edge
  • Loading branch information
kenneth-gray authored Feb 3, 2023
2 parents 1a8b166 + 4d19085 commit 819c305
Show file tree
Hide file tree
Showing 17 changed files with 979 additions and 691 deletions.
1 change: 1 addition & 0 deletions example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,6 @@ run({
uiPath: '/scenarios-ui',
modifyScenariosPath: '/modify',
resetScenariosPath: '/reset',
cookieMode: true,
},
});
87 changes: 46 additions & 41 deletions src/Html.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { UiGroups } from './types';
import { Groups } from './types';

function Html({
updatedScenarios,
Expand All @@ -9,14 +9,10 @@ function Html({
other,
}: {
uiPath: string;
groups: UiGroups;
other: Array<{ name: string; checked: boolean }>;
groups: Groups;
other: Array<{ id: string; selected: boolean }>;
updatedScenarios?: string[];
}) {
if (uiPath[uiPath.length - 1] !== '/') {
uiPath = uiPath + '/';
}

return (
<html lang="en">
<head>
Expand All @@ -25,7 +21,10 @@ function Html({
<title>
{updatedScenarios ? 'Updated - ' : ''}Scenarios - Data Mocks Server
</title>
<link rel="stylesheet" href={`${uiPath}index.css`} />
<link
rel="stylesheet"
href={`${uiPath}${uiPath.slice(-1) === '/' ? '' : '/'}index.css`}
/>
</head>
<body>
<main>
Expand All @@ -39,55 +38,61 @@ function Html({
<a href={uiPath}>Refresh page</a>
</p>
<div className="stack0">
{groups.map(group => (
<fieldset className="stack-3" key={group.name}>
<legend>
<h2 className="group-title">{group.name}</h2>
</legend>
<div className="stack-3">
<div>
<input
type="radio"
id={`none-${group.name}`}
name={group.name}
value=""
defaultChecked={group.noneChecked}
/>
<label htmlFor={`none-${group.name}`}>
No &rsquo;{group.name}&rsquo; scenario
</label>
</div>
{group.scenarios.map(scenario => (
<div key={scenario.name}>
{groups.map(group => {
const noneSelected = group.scenarios.every(
scenario => !scenario.selected,
);

return (
<fieldset className="stack-3" key={group.name}>
<legend>
<h2 className="group-title">{group.name}</h2>
</legend>
<div className="stack-3">
<div>
<input
type="radio"
id={scenario.name}
id={`none-${group.name}`}
name={group.name}
value={scenario.name}
defaultChecked={scenario.checked}
value=""
defaultChecked={noneSelected}
/>
<label htmlFor={scenario.name}>{scenario.name}</label>
<label htmlFor={`none-${group.name}`}>
No &rsquo;{group.name}&rsquo; scenario
</label>
</div>
))}
</div>
</fieldset>
))}
{group.scenarios.map(scenario => (
<div key={scenario.id}>
<input
type="radio"
id={scenario.id}
name={group.name}
value={scenario.id}
defaultChecked={scenario.selected}
/>
<label htmlFor={scenario.id}>{scenario.id}</label>
</div>
))}
</div>
</fieldset>
);
})}
{!other.length ? null : (
<fieldset className="stack-3">
<legend>
<h2>Other</h2>
</legend>
<div className="stack-3">
{other.map(scenario => (
<div key={scenario.name}>
<div key={scenario.id}>
<input
type="checkbox"
id={scenario.name}
id={scenario.id}
name="scenarios"
value={scenario.name}
defaultChecked={scenario.checked}
value={scenario.id}
defaultChecked={scenario.selected}
/>
<label htmlFor={scenario.name}>{scenario.name}</label>
<label htmlFor={scenario.id}>{scenario.id}</label>
</div>
))}
</div>
Expand Down
219 changes: 165 additions & 54 deletions src/apis.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,188 @@
import { RequestHandler, Response, Request } from 'express';
import { ScenarioMap } from './types';
import { getScenarios as utilsGetScenarios } from './utils/get-scenarios';
import { getScenarioIdsFromCookie } from './cookies';
import {
Context,
DefaultScenario,
GetCookie,
Result,
ScenarioMap,
SetCookie,
} from './types';
import { getAllScenarios } from './utils/get-all-scenarios';
import { getContextFromScenarios } from './utils/get-context-from-scenarios';
import { updateScenariosAndContext } from './utils/update-scenarios-and-context';

export { modifyScenarios, resetScenarios, getScenarios };
export { resetScenarios, modifyScenarios, getScenarios };

function resetScenarios({
setCookie,
setServerContext,
setServerSelectedScenarioIds,
defaultScenario,
scenarioMap,
cookieMode,
}: {
setCookie: SetCookie;
setServerContext: (context: Context) => void;
setServerSelectedScenarioIds: (selectedScenarioIds: string[]) => void;
defaultScenario: DefaultScenario;
scenarioMap: ScenarioMap;
cookieMode: boolean;
}): Result {
updateScenariosAndContext({
updatedScenarioIds: [],
setCookie,
setServerContext,
setServerSelectedScenarioIds,
defaultScenario,
scenarioMap,
cookieMode,
});

return {
status: 204,
};
}

function getScenarios({
getCookie,
setCookie,
getServerSelectedScenarioIds,
cookieMode,
defaultScenario,
scenarioMap,
}: {
getCookie: GetCookie;
setCookie: SetCookie;
getServerSelectedScenarioIds: () => string[];
cookieMode: boolean;
defaultScenario: DefaultScenario;
scenarioMap: ScenarioMap;
}): Result {
const allScenarios = getAllScenarios(
scenarioMap,
getSelectedScenarioIds({
getCookie,
setCookie,
getServerSelectedScenarioIds,
cookieMode,
defaultScenario,
}),
);

return {
status: 200,
headers: {
'content-type': 'application/json',
},
response: allScenarios,
};
}

function modifyScenarios({
scenarioNames,
updatedScenarioIds,
scenarioIds,
scenarioMap,
updateScenariosAndContext,
cookieMode,
defaultScenario,
setCookie,
setServerContext,
setServerSelectedScenarioIds,
}: {
scenarioNames: string[];
updatedScenarioIds: unknown;
scenarioIds: string[];
scenarioMap: ScenarioMap;
updateScenariosAndContext: (res: Response, scenarios: string[]) => void;
}): RequestHandler {
return ({ body: { scenarios: scenariosBody } }: Request, res: Response) => {
if (!Array.isArray(scenariosBody)) {
res.status(400).json({
cookieMode: boolean;
defaultScenario: DefaultScenario;
setCookie: SetCookie;
setServerContext: (context: Context) => void;
setServerSelectedScenarioIds: (selectedScenarioIds: string[]) => void;
}) {
if (!isStringArray(updatedScenarioIds)) {
return {
status: 400,
headers: {
'content-type': 'application/json',
},
response: {
message:
'"scenarios" must be an array of scenario names (empty array allowed)',
});
return;
}
},
};
}

const scenariosByGroup: { [key: string]: number } = {};
for (const scenario of scenariosBody) {
if (!scenarioNames.includes(scenario)) {
res.status(400).json({
const scenariosByGroup: Record<string, string> = {};
for (const scenario of updatedScenarioIds) {
if (!scenarioIds.includes(scenario)) {
return {
status: 400,
headers: {
'content-type': 'application/json',
},
response: {
message: `Scenario "${scenario}" does not exist`,
});
return;
}
},
};
}

const scenarioMock = scenarioMap[scenario];
if (!Array.isArray(scenarioMock) && scenarioMock.group) {
const { group } = scenarioMock;
if (scenariosByGroup[group]) {
res.status(400).json({
const scenarioMock = scenarioMap[scenario];
if (!Array.isArray(scenarioMock) && scenarioMock.group) {
const { group } = scenarioMock;
if (scenariosByGroup[group]) {
return {
status: 400,
headers: {
'content-type': 'application/json',
},
response: {
message: `Scenario "${scenario}" cannot be selected, because scenario "${scenariosByGroup[group]}" from group "${group}" has already been selected`,
});
return;
}

scenariosByGroup[group] = scenario;
},
};
}

scenariosByGroup[group] = scenario;
}
}

updateScenariosAndContext(res, scenariosBody);
updateScenariosAndContext({
cookieMode,
defaultScenario,
updatedScenarioIds,
scenarioMap,
setCookie,
setServerContext,
setServerSelectedScenarioIds,
});

res.sendStatus(204);
};
return { status: 204 };
}

function resetScenarios({
updateScenariosAndContext,
}: {
updateScenariosAndContext: (res: Response, scenarios: string[]) => void;
}): RequestHandler {
return (_, res: Response) => {
updateScenariosAndContext(res, []);
res.sendStatus(204);
};
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every(item => typeof item === 'string');
}

function getScenarios({
scenarioMap,
getScenarioNames,
function getSelectedScenarioIds({
getCookie,
setCookie,
getServerSelectedScenarioIds,
cookieMode,
defaultScenario,
}: {
scenarioMap: ScenarioMap;
getScenarioNames: (req: Request, res: Response) => string[];
}): RequestHandler {
return (req: Request, res: Response) => {
const data = utilsGetScenarios(scenarioMap, getScenarioNames(req, res));
getCookie: GetCookie;
setCookie: SetCookie;
getServerSelectedScenarioIds: () => string[];
cookieMode: boolean;
defaultScenario: DefaultScenario;
}) {
if (cookieMode) {
return getScenarioIdsFromCookie({
getCookie,
setCookie,
defaultValue: {
context: getContextFromScenarios([defaultScenario]),
scenarios: [],
},
});
}

res.json(data);
};
return getServerSelectedScenarioIds();
}
Loading

0 comments on commit 819c305

Please sign in to comment.