Skip to content

Commit 76ec554

Browse files
committed
Refactor rule-part availability separate from description definitions
Previously, we allowed the description methods to return undefined, and anything undefined was hidden in the list. This is not a great way to encode that information. Similarly, the selection pickers themselves included logic to check for version support for individual rule parts. Now, there's a single method for both matchers and handlers that defines this explicitly in one place, so we can manually enable/disable them with our own rules directly.
1 parent 87d01dc commit 76ec554

File tree

6 files changed

+246
-161
lines changed

6 files changed

+246
-161
lines changed

src/components/mock/handler-selection.tsx

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
HandlerClass,
1111
Handler,
1212
HandlerClassKey,
13-
HandlerKeys,
13+
HandlerClassKeyLookup,
1414
HandlerLookup,
1515
isPaidHandlerClass
1616
} from '../../model/rules/rules';
@@ -29,27 +29,19 @@ import {
2929
} from '../../model/rules/definitions/http-rule-definitions';
3030

3131
import { Select } from '../common/inputs';
32-
import {
33-
serverVersion,
34-
versionSatisfies,
35-
FROM_FILE_HANDLER_SERVER_RANGE,
36-
PASSTHROUGH_TRANSFORMS_RANGE
37-
} from '../../services/service-versions';
3832

3933
const getHandlerKey = (h: HandlerClass | Handler) =>
40-
HandlerKeys.get(h as any) || HandlerKeys.get(h.constructor as any);
34+
HandlerClassKeyLookup.get(h as any) || HandlerClassKeyLookup.get(h.constructor as any);
4135
const getHandlerClassByKey = (k: HandlerClassKey) => HandlerLookup[k];
4236

4337
const HandlerOptions = (p: { handlers: Array<HandlerClass> }) => <>{
4438
p.handlers.map((handler): JSX.Element | null => {
45-
const key = getHandlerKey(handler);
46-
const description = summarizeHandlerClass(handler);
39+
const key = getHandlerKey(handler)!;
40+
const description = summarizeHandlerClass(key);
4741

48-
return description
49-
? <option key={key} value={key}>
50-
{ description }
51-
</option>
52-
: null;
42+
return <option key={key} value={key}>
43+
{ description }
44+
</option>;
5345
})
5446
}</>;
5547

@@ -85,41 +77,23 @@ const instantiateHandler = (
8577
}
8678
}
8779

88-
const supportsFileHandlers = () =>
89-
_.isString(serverVersion.value) &&
90-
versionSatisfies(serverVersion.value, FROM_FILE_HANDLER_SERVER_RANGE);
91-
92-
const supportsTransforms = () =>
93-
_.isString(serverVersion.value) &&
94-
versionSatisfies(serverVersion.value, PASSTHROUGH_TRANSFORMS_RANGE);
95-
9680
export const HandlerSelector = inject('rulesStore', 'accountStore')(observer((p: {
9781
rulesStore?: RulesStore,
9882
accountStore?: AccountStore,
83+
availableHandlers: Array<HandlerClass>,
9984
value: Handler,
10085
onChange: (handler: Handler) => void
10186
}) => {
102-
const allHandlers = [
103-
StaticResponseHandler,
104-
supportsFileHandlers() && FromFileResponseHandler,
105-
PassThroughHandler,
106-
ForwardToHostHandler,
107-
supportsTransforms() && TransformingHandler,
108-
RequestBreakpointHandler,
109-
ResponseBreakpointHandler,
110-
RequestAndResponseBreakpointHandler,
111-
TimeoutHandler,
112-
CloseConnectionHandler
113-
].filter(Boolean);
114-
115-
// Do some type tricks to make TS understand that we've filtered 'false' out of the handlers.
116-
type DefinedHandler = Exclude<typeof allHandlers[number], false>;
117-
118-
const [ availableHandlers, needProHandlers ] = _.partition<DefinedHandler>(
119-
allHandlers as DefinedHandler[],
87+
let [ allowedHandlers, needProHandlers ] = _.partition(
88+
p.availableHandlers,
12089
(handlerClass) => p.accountStore!.isPaidUser || !isPaidHandlerClass(handlerClass)
12190
);
12291

92+
// Pull the breakpoint handlers to the top, since they're kind of separate
93+
allowedHandlers = _.sortBy(allowedHandlers, h =>
94+
getHandlerKey(h)!.includes('breakpoint') ? 0 : 1
95+
);
96+
12397
return <HandlerSelect
12498
value={getHandlerKey(p.value)}
12599
onChange={(event) => {
@@ -129,7 +103,7 @@ export const HandlerSelector = inject('rulesStore', 'accountStore')(observer((p:
129103
p.onChange(handler);
130104
}}
131105
>
132-
<HandlerOptions handlers={availableHandlers} />
106+
<HandlerOptions handlers={allowedHandlers} />
133107
{ needProHandlers.length &&
134108
<optgroup label='With HTTP Toolkit Pro:'>
135109
<HandlerOptions handlers={needProHandlers} />

src/components/mock/matcher-config.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export function MatcherConfiguration(props:
4242
? MatcherLookup[props.matcher.type as MatcherClassKey]
4343
: props.matcherClass!;
4444

45+
// No matcher class selected yet - no config to show:
46+
if (!matcherClass) return null;
47+
4548
const configProps = {
4649
matcher: matcher as any,
4750
matcherIndex: props.matcherIndex,
@@ -71,7 +74,7 @@ export function MatcherConfiguration(props:
7174
case matchers.JsonBodyFlexibleMatcher:
7275
return <JsonBodyIncludingMatcherConfig {...configProps} />;
7376
default:
74-
return null;
77+
throw new Error(`Cannot render config component for ${matcherClass.name}`);
7578
}
7679
}
7780

src/components/mock/matcher-selection.tsx

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,14 @@ import * as React from 'react';
33
import { observer } from 'mobx-react';
44
import { action, observable } from 'mobx';
55

6-
import { matchers } from 'mockttp';
7-
86
import { styled } from '../../styles';
97
import { Icon } from '../../icons';
10-
import {
11-
serverVersion as serverVersionObservable,
12-
versionSatisfies,
13-
HOST_MATCHER_SERVER_RANGE,
14-
BODY_MATCHING_RANGE
15-
} from '../../services/service-versions';
168
import { Button, Select } from '../common/inputs';
179

1810
import {
1911
Matcher,
2012
MatcherClass,
21-
MatcherKeys,
13+
MatcherClassKeyLookup,
2214
MatcherLookup,
2315
MatcherClassKey,
2416
InitialMatcher,
@@ -31,12 +23,9 @@ import {
3123

3224
import { MatcherConfiguration } from './matcher-config';
3325

34-
const getMatcherKey = (m: MatcherClass | Matcher | undefined) => {
35-
if (m === undefined) return '';
26+
const getMatcherKey = (m: MatcherClass | Matcher | undefined) =>
27+
MatcherClassKeyLookup.get(m as any) || MatcherClassKeyLookup.get(m?.constructor as any);
3628

37-
return MatcherKeys.get(m as any) ||
38-
MatcherKeys.get(m.constructor as any);
39-
};
4029
const getMatcherClassByKey = (k: MatcherClassKey) => MatcherLookup[k];
4130

4231
const MatcherRow = styled.li`
@@ -129,14 +118,12 @@ export class ExistingMatcherRow extends React.Component<ExistingMatcherRowProps>
129118

130119
const MatcherOptions = (p: { matchers: Array<MatcherClass> }) => <>{
131120
p.matchers.map((matcher): JSX.Element | null => {
132-
const key = getMatcherKey(matcher);
133-
const description = summarizeMatcherClass(matcher);
134-
135-
return description
136-
? <option key={key} value={key}>
137-
{ description }
138-
</option>
139-
: null;
121+
const key = getMatcherKey(matcher)!;
122+
const description = summarizeMatcherClass(key);
123+
124+
return <option key={key} value={key}>
125+
{ description }
126+
</option>;
140127
})
141128
}</>
142129

@@ -153,6 +140,7 @@ const LowlightedOption = styled.option`
153140
@observer
154141
export class NewMatcherRow extends React.Component<{
155142
onAdd: (matcher: Matcher) => void,
143+
availableMatchers: MatcherClass[],
156144
existingMatchers: Matcher[]
157145
}> {
158146

@@ -204,6 +192,8 @@ export class NewMatcherRow extends React.Component<{
204192
}
205193

206194
render() {
195+
const { availableMatchers } = this.props;
196+
207197
const {
208198
matcherClass,
209199
draftMatchers,
@@ -213,27 +203,6 @@ export class NewMatcherRow extends React.Component<{
213203
saveMatcher
214204
} = this;
215205

216-
const serverVersion = serverVersionObservable.state === 'fulfilled'
217-
? serverVersionObservable.value as string
218-
: undefined;
219-
220-
const availableMatchers = [
221-
...(versionSatisfies(serverVersion, HOST_MATCHER_SERVER_RANGE) ? [
222-
matchers.HostMatcher
223-
] : []),
224-
matchers.SimplePathMatcher,
225-
matchers.RegexPathMatcher,
226-
matchers.QueryMatcher,
227-
matchers.ExactQueryMatcher,
228-
matchers.HeaderMatcher,
229-
...(versionSatisfies(serverVersion, BODY_MATCHING_RANGE) ? [
230-
matchers.RawBodyMatcher,
231-
matchers.RawBodyIncludesMatcher,
232-
matchers.JsonBodyMatcher,
233-
matchers.JsonBodyFlexibleMatcher
234-
] : [])
235-
];
236-
237206
return <MatcherRow>
238207
<MatcherInputsContainer>
239208
<Select

src/components/mock/mock-rule-row.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,34 @@ import * as polished from 'polished';
44
import { observer, inject, disposeOnUnmount, Observer } from 'mobx-react';
55
import { action, observable, reaction } from 'mobx';
66
import { Method, matchers } from 'mockttp';
7-
import { Draggable, DraggingStyle, NotDraggingStyle, DraggableStateSnapshot } from 'react-beautiful-dnd';
7+
import {
8+
Draggable,
9+
DraggingStyle,
10+
NotDraggingStyle,
11+
DraggableStateSnapshot
12+
} from 'react-beautiful-dnd';
813

914
import { styled, css } from '../../styles';
1015
import { Icon } from '../../icons';
1116

1217
import { getMethodColor } from '../../model/events/categorization';
13-
import { HtkMockRule, Matcher, Handler, isPaidHandler } from '../../model/rules/rules';
18+
import {
19+
HtkMockRule,
20+
Matcher,
21+
Handler,
22+
isPaidHandler,
23+
getAvailableAdditionalMatchers,
24+
getAvailableHandlers
25+
} from '../../model/rules/rules';
1426
import { ItemPath } from '../../model/rules/rules-structure';
1527
import {
1628
summarizeMatcher,
1729
summarizeHandler
1830
} from '../../model/rules/rule-descriptions';
1931
import { AccountStore } from '../../model/account/account-store';
32+
import {
33+
serverVersion as serverVersionObservable
34+
} from '../../services/service-versions';
2035

2136
import { clickOnEnter, noPropagation } from '../component-utils';
2237
import { GetProOverlay } from '../account/pro-placeholders';
@@ -290,8 +305,10 @@ export class RuleRow extends React.Component<{
290305
getPro
291306
} = this.props.accountStore!;
292307

308+
const ruleType = rule.type;
309+
293310
// Hide non-HTTP rules (...for now)
294-
if (rule.type !== 'http') return null;
311+
if (ruleType !== 'http') return null;
295312

296313
const initialMatcher = rule.matchers.length ? rule.matchers[0] : undefined;
297314

@@ -304,6 +321,13 @@ export class RuleRow extends React.Component<{
304321
method = undefined;
305322
}
306323

324+
const serverVersion = serverVersionObservable.state === 'fulfilled'
325+
? serverVersionObservable.value as string
326+
: undefined;
327+
328+
const availableMatchers = getAvailableAdditionalMatchers(ruleType, serverVersion);
329+
const availableHandlers = getAvailableHandlers(ruleType, serverVersion);
330+
307331
// Handlers are in demo mode (uneditable, behind a 'Get Pro' overlay), either if the rule
308332
// has a handler you can't use, or you've picked a Pro handler and its been put in demoHandler
309333
const isHandlerDemo = !isPaidUser && (this.demoHandler || isPaidHandler(rule.handler));
@@ -378,6 +402,7 @@ export class RuleRow extends React.Component<{
378402

379403
{ rule.matchers.length > 0 &&
380404
<NewMatcherRow
405+
availableMatchers={availableMatchers}
381406
existingMatchers={rule.matchers}
382407
onAdd={this.addMatcher}
383408
/>
@@ -400,20 +425,21 @@ export class RuleRow extends React.Component<{
400425
<HandlerSelector
401426
value={ruleHandler}
402427
onChange={this.updateHandler}
428+
availableHandlers={availableHandlers}
403429
/>
404430

405431
{ isHandlerDemo
406432
// If you select a paid handler with an unpaid account,
407433
// show a handler demo with a 'Get Pro' overlay:
408434
? <GetProOverlay getPro={getPro} source={`rule-${ruleHandler.type}`}>
409435
<HandlerConfiguration
410-
ruleType={rule.type}
436+
ruleType={ruleType}
411437
handler={ruleHandler}
412438
onChange={_.noop}
413439
/>
414440
</GetProOverlay>
415441
: <HandlerConfiguration
416-
ruleType={rule.type}
442+
ruleType={ruleType}
417443
handler={ruleHandler}
418444
onChange={this.updateHandler}
419445
/>

0 commit comments

Comments
 (0)