-
-
Notifications
You must be signed in to change notification settings - Fork 615
/
context-runner-impl.ts
75 lines (63 loc) · 2.53 KB
/
context-runner-impl.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import * as _ from 'lodash';
import { InternalRequest, Request, ValidationHalt, contextsKey } from '../base';
import { Context, ReadonlyContext } from '../context';
import { ContextBuilder } from '../context-builder';
import { SelectFields, selectFields as baseSelectFields } from '../select-fields';
import { Result } from '../validation-result';
import { ContextRunner } from './context-runner';
export class ResultWithContext extends Result {
constructor(readonly context: ReadonlyContext) {
super(error => error, context.errors);
}
}
export class ContextRunnerImpl implements ContextRunner {
constructor(
private readonly builderOrContext: ContextBuilder | Context,
private readonly selectFields: SelectFields = baseSelectFields,
) {}
async run(req: Request, options: { dryRun?: boolean } = {}) {
const context =
this.builderOrContext instanceof Context
? this.builderOrContext
: this.builderOrContext.build();
const instances = this.selectFields(req, context.fields, context.locations);
context.addFieldInstances(instances);
const haltedInstances = new Set<string>();
for (const contextItem of context.stack) {
const promises = context.getData({ requiredOnly: true }).map(async instance => {
const { location, path } = instance;
const instanceKey = `${location}:${path}`;
if (haltedInstances.has(instanceKey)) {
return;
}
try {
await contextItem.run(context, instance.value, {
req,
location,
path,
});
// An instance is mutable, so if an item changed its value, there's no need to call getData again
const newValue = instance.value;
// Checks whether the value changed.
// Avoids e.g. undefined values being set on the request if it didn't have the key initially.
const reqValue = path !== '' ? _.get(req[location], path) : req[location];
if (!options.dryRun && reqValue !== instance.value) {
path !== '' ? _.set(req[location], path, newValue) : _.set(req, location, newValue);
}
} catch (e) {
if (e instanceof ValidationHalt) {
haltedInstances.add(instanceKey);
return;
}
throw e;
}
});
await Promise.all(promises);
}
if (!options.dryRun) {
const internalReq = req as InternalRequest;
internalReq[contextsKey] = (internalReq[contextsKey] || []).concat(context);
}
return new ResultWithContext(context);
}
}