Skip to content

Commit 6875fe2

Browse files
committed
wip
1 parent 4ae27d6 commit 6875fe2

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

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

182-
result.metadata = { guideGroup: defaultGroup, filters };
193+
result.metadata = { guideGroup: defaultGroup, filters, ...metadata };
194+
183195
return result;
184196
};
185197

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

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

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

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

636653
// There should be at least one guide to return here now.
637-
const guides = [...select(state, filters).values()];
654+
const guides = [...result.values()];
638655

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

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

714732
if (result.size === 0) {
715733
this.knock.log("[Guide] Selection found zero result");
@@ -730,10 +748,12 @@ export class KnockGuideClient {
730748

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

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

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

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

766-
private maybeRecordSelectQuery(
767-
params: SelectQueryParams,
768-
opts: SelectGuideOpts,
769-
) {
796+
private maybeRecordSelectResult(result: SelectionResult) {
797+
if (!result.metadata) return;
798+
799+
const { opts, filters, limit } = result.metadata;
770800
if (!opts.recordSelectQuery) return;
801+
if (!filters.key && !filters.type) return;
771802
if (!this.stage || this.stage.status === "closed") return;
772-
if (!params.key && !params.type) return;
773-
774-
// Deep merge into the query logs:
775-
const queriesByKey = this.stage.queries.key || {};
776-
if (params.key) {
777-
queriesByKey[params.key] = {
778-
...(queriesByKey[params.key] || {}),
779-
...{ [params.limit]: opts },
803+
804+
// Deep merge to accumulate the results.
805+
const queriedByKey = this.stage.results.key || {};
806+
if (filters.key) {
807+
queriedByKey[filters.key] = {
808+
...(queriedByKey[filters.key] || {}),
809+
...{ [limit]: result },
780810
};
781811
}
782-
const queriesByType = this.stage.queries.type || {};
783-
if (params.type) {
784-
queriesByType[params.type] = {
785-
...(queriesByType[params.type] || {}),
786-
...{ [params.limit]: opts },
812+
const queriedByType = this.stage.results.type || {};
813+
if (filters.type) {
814+
queriedByType[filters.type] = {
815+
...(queriedByType[filters.type] || {}),
816+
...{ [limit]: result },
787817
};
788818
}
789819

790820
this.stage = {
791821
...this.stage,
792-
queries: { key: queriesByKey, type: queriesByType },
822+
results: { key: queriedByKey, type: queriedByType },
793823
};
794824
}
795825

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

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)