Skip to content

Commit 6f798fa

Browse files
committed
wip
1 parent 813750e commit 6f798fa

7 files changed

Lines changed: 222 additions & 105 deletions

File tree

packages/client/src/clients/guide/client.ts

Lines changed: 105 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Knock from "../../knock";
77

88
import {
99
DEFAULT_GROUP_KEY,
10-
SelectionResult,
10+
// SelectionResult,
1111
byKey,
1212
checkStateIfThrottled,
1313
findDefaultGroup,
@@ -46,7 +46,9 @@ import {
4646
SelectFilterParams,
4747
SelectGuideOpts,
4848
SelectGuidesOpts,
49-
SelectQueryParams,
49+
SelectQueryLimit,
50+
SelectionResult,
51+
// SelectQueryParams,
5052
StepMessageState,
5153
StoreState,
5254
TargetParams,
@@ -151,7 +153,16 @@ const safeJsonParseDebugParams = (value: string): DebugState => {
151153
}
152154
};
153155

154-
const select = (state: StoreState, filters: SelectFilterParams = {}) => {
156+
type SelectQueryMetadata = {
157+
limit: SelectQueryLimit;
158+
opts: SelectGuideOpts;
159+
};
160+
161+
const select = (
162+
state: StoreState,
163+
filters: SelectFilterParams,
164+
metadata: SelectQueryMetadata,
165+
) => {
155166
// A map of selected guides as values, with its order index as keys.
156167
const result = new SelectionResult();
157168

@@ -180,7 +191,8 @@ const select = (state: StoreState, filters: SelectFilterParams = {}) => {
180191
result.set(index, guide);
181192
}
182193

183-
result.metadata = { guideGroup: defaultGroup, filters };
194+
result.metadata = { guideGroup: defaultGroup, filters, ...metadata };
195+
184196
return result;
185197
};
186198

@@ -614,29 +626,35 @@ export class KnockGuideClient {
614626
`[Guide] .selectGuides (filters: ${formatFilters(filters)}; state: ${formatState(state)})`,
615627
);
616628

629+
// 1. First, call selectGuide() using the same filters to ensure we have a
630+
// group stage open and respect throttling. This isn't the real query, but
631+
// rather it's a shortcut ahead of handling the actual query result below.
617632
const selectedGuide = this.selectGuide(state, filters, {
618633
...opts,
619-
// Do not record this selectGuide() query, since this is a short to check
620-
// throttling, and we should be recording the actual selectGuides query
621-
// (as needed).
634+
// Don't record this result, not the actual query result we need.
622635
recordSelectQuery: false,
623636
});
624637

625-
// Record AFTER the selectGuide() query to ensure we have a group stage open
626-
// to be able to record.
638+
// 2. Now make the actual select query with the provided filters and opts,
639+
// and record the result (as needed). By default, we only record the result
640+
// while in debugging.
627641
const { recordSelectQuery = !!state.debug?.debugging } = opts;
628-
this.maybeRecordSelectQuery(
629-
{ ...filters, limit: "one" },
630-
{ ...opts, recordSelectQuery },
631-
);
642+
const metadata: SelectQueryMetadata = {
643+
limit: "all",
644+
opts: { ...opts, recordSelectQuery },
645+
};
646+
const result = select(state, filters, metadata);
647+
this.maybeRecordSelectResult(result);
632648

649+
// 3. Stop if there is not at least one guide to return.
633650
if (!selectedGuide) {
634651
return [];
635652
}
636653

637654
// There should be at least one guide to return here now.
638-
const guides = [...select(state, filters).values()];
655+
const guides = [...result.values()];
639656

657+
// 4. If throttled, filter out any throttled guides.
640658
if (!opts.includeThrottled && checkStateIfThrottled(state)) {
641659
const unthrottledGuides = guides.filter(
642660
(g) => g.bypass_global_group_limit,
@@ -702,15 +720,15 @@ export class KnockGuideClient {
702720
this.stage = this.openGroupStage(); // Assign here to make tsc happy
703721
}
704722

705-
// Must come AFTER we ensure a group stage exists, so we can record select
706-
// queries.
723+
// Must come AFTER we ensure a group stage exists above, so we can record
724+
// select queries. By default, we only record the result while in debugging.
707725
const { recordSelectQuery = !!state.debug?.debugging } = opts;
708-
this.maybeRecordSelectQuery(
709-
{ ...filters, limit: "one" },
710-
{ ...opts, recordSelectQuery },
711-
);
712-
713-
const result = select(state, filters);
726+
const metadata: SelectQueryMetadata = {
727+
limit: "one",
728+
opts: { ...opts, recordSelectQuery },
729+
};
730+
const result = select(state, filters, metadata);
731+
this.maybeRecordSelectResult(result);
714732

715733
if (result.size === 0) {
716734
this.knock.log("[Guide] Selection found zero result");
@@ -731,10 +749,12 @@ export class KnockGuideClient {
731749

732750
// Check if inside the throttle window (i.e. throttled) and if so stop and
733751
// return undefined unless explicitly given the option to include throttled.
734-
if (!opts.includeThrottled && checkStateIfThrottled(state)) {
735-
this.knock.log(`[Guide] Throttling the selected guide: ${guide.key}`);
736-
return undefined;
737-
}
752+
const throttled = !opts.includeThrottled && checkStateIfThrottled(state);
753+
754+
// if (!opts.includeThrottled && checkStateIfThrottled(state)) {
755+
// this.knock.log(`[Guide] Throttling the selected guide: ${guide.key}`);
756+
// return undefined;
757+
// }
738758

739759
switch (this.stage.status) {
740760
case "open": {
@@ -747,6 +767,11 @@ export class KnockGuideClient {
747767
this.knock.log(`[Guide] Patching the group stage: ${guide.key}`);
748768
this.stage.ordered[index] = guide.key;
749769

770+
if (throttled) {
771+
this.knock.log(`[Guide] Throttling the selected guide: ${guide.key}`);
772+
return undefined;
773+
}
774+
750775
const ret = this.stage.resolved === guide.key ? guide : undefined;
751776
this.knock.log(
752777
`[Guide] Returning \`${ret?.key}\` (stage: ${formatGroupStage(this.stage)})`,
@@ -755,6 +780,11 @@ export class KnockGuideClient {
755780
}
756781

757782
case "closed": {
783+
if (throttled) {
784+
this.knock.log(`[Guide] Throttling the selected guide: ${guide.key}`);
785+
return undefined;
786+
}
787+
758788
const ret = this.stage.resolved === guide.key ? guide : undefined;
759789
this.knock.log(
760790
`[Guide] Returning \`${ret?.key}\` (stage: ${formatGroupStage(this.stage)})`,
@@ -764,36 +794,66 @@ export class KnockGuideClient {
764794
}
765795
}
766796

767-
private maybeRecordSelectQuery(
768-
params: SelectQueryParams,
769-
opts: SelectGuideOpts,
770-
) {
797+
private maybeRecordSelectResult(result: SelectionResult) {
798+
if (!result.metadata) return;
799+
800+
const { opts, filters, limit } = result.metadata;
771801
if (!opts.recordSelectQuery) return;
802+
if (!filters.key && !filters.type) return;
772803
if (!this.stage || this.stage.status === "closed") return;
773-
if (!params.key && !params.type) return;
774-
775-
// Deep merge into the query logs:
776-
const queriesByKey = this.stage.queries.key || {};
777-
if (params.key) {
778-
queriesByKey[params.key] = {
779-
...(queriesByKey[params.key] || {}),
780-
...{ [params.limit]: opts },
804+
805+
// Deep merge to accumulate the results.
806+
const queriedByKey = this.stage.results.key || {};
807+
if (filters.key) {
808+
queriedByKey[filters.key] = {
809+
...(queriedByKey[filters.key] || {}),
810+
...{ [limit]: result },
781811
};
782812
}
783-
const queriesByType = this.stage.queries.type || {};
784-
if (params.type) {
785-
queriesByType[params.type] = {
786-
...(queriesByType[params.type] || {}),
787-
...{ [params.limit]: opts },
813+
const queriedByType = this.stage.results.type || {};
814+
if (filters.type) {
815+
queriedByType[filters.type] = {
816+
...(queriedByType[filters.type] || {}),
817+
...{ [limit]: result },
788818
};
789819
}
790820

791821
this.stage = {
792822
...this.stage,
793-
queries: { key: queriesByKey, type: queriesByType },
823+
results: { key: queriedByKey, type: queriedByType },
794824
};
795825
}
796826

827+
// private maybeRecordSelectQuery(
828+
// params: SelectQueryParams,
829+
// opts: SelectGuideOpts,
830+
// ) {
831+
// if (!opts.recordSelectQuery) return;
832+
// if (!this.stage || this.stage.status === "closed") return;
833+
// if (!params.key && !params.type) return;
834+
//
835+
// // Deep merge into the query logs:
836+
// const queriesByKey = this.stage.queries.key || {};
837+
// if (params.key) {
838+
// queriesByKey[params.key] = {
839+
// ...(queriesByKey[params.key] || {}),
840+
// ...{ [params.limit]: opts },
841+
// };
842+
// }
843+
// const queriesByType = this.stage.queries.type || {};
844+
// if (params.type) {
845+
// queriesByType[params.type] = {
846+
// ...(queriesByType[params.type] || {}),
847+
// ...{ [params.limit]: opts },
848+
// };
849+
// }
850+
//
851+
// this.stage = {
852+
// ...this.stage,
853+
// queries: { key: queriesByKey, type: queriesByType },
854+
// };
855+
// }
856+
797857
getStage() {
798858
return this.stage;
799859
}
@@ -813,7 +873,7 @@ export class KnockGuideClient {
813873
this.stage = {
814874
status: "open",
815875
ordered: [],
816-
queries: {},
876+
results: {},
817877
timeoutId,
818878
};
819879

packages/client/src/clients/guide/helpers.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,14 @@ import {
33
GuideActivationUrlRuleData,
44
GuideData,
55
GuideGroupData,
6-
KnockGuide,
6+
// KnockGuide,
77
KnockGuideActivationUrlPattern,
88
SelectFilterParams,
9+
// SelectGuideOpts,
10+
// SelectQueryLimit,
911
StoreState,
1012
} from "./types";
1113

12-
type SelectionResultMetadata = {
13-
guideGroup: GuideGroupData;
14-
filters: SelectFilterParams;
15-
};
16-
17-
// Extends the map class to allow having metadata on it, which is used to record
18-
// the guide group context for the selection result (though currently only a
19-
// default global group is supported).
20-
export class SelectionResult<K = number, V = KnockGuide> extends Map<K, V> {
21-
metadata: SelectionResultMetadata | undefined;
22-
23-
constructor() {
24-
super();
25-
}
26-
}
27-
2814
export const formatGroupStage = (stage: GroupStage) => {
2915
return `status=${stage.status}, resolved=${stage.resolved}`;
3016
};

packages/client/src/clients/guide/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export type {
1414
SelectGuidesOpts as KnockSelectGuidesOpts,
1515
StoreState as KnockGuideClientStoreState,
1616
GroupStage as KnockGuideClientGroupStage,
17+
SelectionResult as KnockGuideSelectionResult,
1718
} from "./types";

packages/client/src/clients/guide/types.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
import { GenericData } from "@knocklabs/types";
22

3+
type SelectionResultMetadata = {
4+
guideGroup: GuideGroupData;
5+
// Additional info about the underlying select query behind the result.
6+
filters: SelectFilterParams;
7+
limit: SelectQueryLimit;
8+
opts: SelectGuideOpts;
9+
};
10+
11+
// Extends the map class to allow having metadata on it, which is used to record
12+
// the guide group context for the selection result (though currently only a
13+
// default global group is supported).
14+
export class SelectionResult<K = number, V = KnockGuide> extends Map<K, V> {
15+
metadata: SelectionResultMetadata | undefined;
16+
17+
constructor() {
18+
super();
19+
}
20+
}
21+
322
//
423
// Fetch guides API
524
//
@@ -231,6 +250,7 @@ export type SelectFilterParams = {
231250

232251
export type SelectGuideOpts = {
233252
includeThrottled?: boolean;
253+
// XXX: record result
234254
recordSelectQuery?: boolean;
235255
};
236256

@@ -248,24 +268,27 @@ export type ConstructorOpts = {
248268
throttleCheckInterval?: number;
249269
};
250270

251-
export type SelectQueryParams = SelectFilterParams & {
252-
limit: "one" | "all";
253-
};
271+
// i.e. useGuide vs useGuides
272+
export type SelectQueryLimit = "one" | "all";
273+
274+
// export type SelectQueryParams = SelectFilterParams & {
275+
// limit: "one" | "all";
276+
// };
254277

255-
type SelectGuideOptsByLimit = {
256-
one?: SelectGuideOpts;
257-
all?: SelectGuideOpts;
278+
type SelectionResultByLimit = {
279+
one?: SelectionResult;
280+
all?: SelectionResult;
258281
};
259282

260-
type RecordedSelectQueries = {
261-
key?: Record<KnockGuide["key"], SelectGuideOptsByLimit>;
262-
type?: Record<KnockGuide["key"], SelectGuideOptsByLimit>;
283+
type RecordedSelectionResults = {
284+
key?: Record<KnockGuide["key"], SelectionResultByLimit>;
285+
type?: Record<KnockGuide["type"], SelectionResultByLimit>;
263286
};
264287

265288
export type GroupStage = {
266289
status: "open" | "closed" | "patch";
267290
ordered: Array<KnockGuide["key"]>;
268291
resolved?: KnockGuide["key"];
269-
queries: RecordedSelectQueries;
270292
timeoutId: ReturnType<typeof setTimeout> | null;
293+
results: RecordedSelectionResults;
271294
};

0 commit comments

Comments
 (0)