Skip to content

Commit 65feb91

Browse files
authored
Fix SelfDescribingJson type regression from #1330 (#1347)
Include some Snowtype-generated code as tests.
1 parent 834ae43 commit 65feb91

File tree

10 files changed

+223
-35
lines changed

10 files changed

+223
-35
lines changed

api-docs/docs/browser-tracker/browser-tracker.api.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,7 @@ export interface ClientSession extends Record<string, unknown> {
137137
}
138138

139139
// @public
140-
export interface CommonEventProperties<T extends {
141-
[_: string]: unknown;
142-
} = Record<string, unknown>> {
140+
export interface CommonEventProperties<T = Record<string, unknown>> {
143141
context?: Array<SelfDescribingJson<T>> | null;
144142
timestamp?: Timestamp | null;
145143
}
@@ -433,16 +431,14 @@ Array<ContextPrimitive> | ContextPrimitive
433431
];
434432

435433
// @public
436-
export interface SelfDescribingEvent {
437-
event: SelfDescribingJson;
434+
export interface SelfDescribingEvent<T = Record<string, unknown>> {
435+
event: SelfDescribingJson<T>;
438436
}
439437

440438
// @public
441-
export type SelfDescribingJson<T extends {
442-
[_: string]: unknown;
443-
} = Record<string, unknown>> = {
439+
export type SelfDescribingJson<T = Record<string, unknown>> = {
444440
schema: string;
445-
data: T;
441+
data: T extends any[] ? never : T extends {} ? T : never;
446442
};
447443

448444
// @public
@@ -574,7 +570,7 @@ export interface TrackerCore {
574570
export function trackPageView(event?: PageViewEvent & CommonEventProperties, trackers?: Array<string>): void;
575571

576572
// @public
577-
export function trackSelfDescribingEvent(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array<string>): void;
573+
export function trackSelfDescribingEvent<T = Record<string, unknown>>(event: SelfDescribingEvent<T> & CommonEventProperties, trackers?: Array<string>): void;
578574

579575
// @public
580576
export function trackStructEvent(event: StructuredEvent & CommonEventProperties, trackers?: Array<string>): void;

api-docs/docs/node-tracker/node-tracker.api.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export function buildRemoveFromCart(event: RemoveFromCartEvent): PayloadBuilder;
9898
export function buildScreenView(event: ScreenViewEvent): PayloadBuilder;
9999

100100
// @public
101-
export function buildSelfDescribingEvent(event: SelfDescribingEvent): PayloadBuilder;
101+
export function buildSelfDescribingEvent<T = Record<string, unknown>>(event: SelfDescribingEvent<T>): PayloadBuilder;
102102

103103
// @public
104104
export function buildSiteSearch(event: SiteSearchEvent): PayloadBuilder;
@@ -459,16 +459,14 @@ export interface ScreenViewEvent {
459459
}
460460

461461
// @public
462-
export interface SelfDescribingEvent {
463-
event: SelfDescribingJson;
462+
export interface SelfDescribingEvent<T = Record<string, unknown>> {
463+
event: SelfDescribingJson<T>;
464464
}
465465

466466
// @public
467-
export type SelfDescribingJson<T extends {
468-
[_: string]: unknown;
469-
} = Record<string, unknown>> = {
467+
export type SelfDescribingJson<T = Record<string, unknown>> = {
470468
schema: string;
471-
data: T;
469+
data: T extends any[] ? never : T extends {} ? T : never;
472470
};
473471

474472
// @public
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@snowplow/browser-plugin-snowplow-ecommerce",
5+
"comment": "",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@snowplow/browser-plugin-snowplow-ecommerce"
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@snowplow/tracker-core",
5+
"comment": "Fix regression in SelfDescribingJson type from #1330",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@snowplow/tracker-core"
10+
}

libraries/tracker-core/src/contexts.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ export interface PluginContexts {
246246
/**
247247
* Returns list of contexts from all active plugins
248248
*/
249-
addPluginContexts: (additionalContexts?: SelfDescribingJson[] | null) => SelfDescribingJson[];
249+
addPluginContexts: <T = Record<string, unknown>>(
250+
additionalContexts?: SelfDescribingJson<T>[] | null
251+
) => SelfDescribingJson[];
250252
}
251253

252254
export function pluginContexts(plugins: Array<CorePlugin>): PluginContexts {
@@ -257,8 +259,10 @@ export function pluginContexts(plugins: Array<CorePlugin>): PluginContexts {
257259
* @returns userContexts combined with commonContexts
258260
*/
259261
return {
260-
addPluginContexts: (additionalContexts?: SelfDescribingJson[] | null): SelfDescribingJson[] => {
261-
const combinedContexts: SelfDescribingJson[] = additionalContexts ? [...additionalContexts] : [];
262+
addPluginContexts: <T = Record<string, unknown>>(additionalContexts?: SelfDescribingJson<T>[] | null) => {
263+
const combinedContexts: SelfDescribingJson<T | Record<string, unknown>>[] = additionalContexts
264+
? [...additionalContexts]
265+
: [];
262266

263267
plugins.forEach((plugin) => {
264268
try {
@@ -270,7 +274,7 @@ export function pluginContexts(plugins: Array<CorePlugin>): PluginContexts {
270274
}
271275
});
272276

273-
return combinedContexts;
277+
return combinedContexts as SelfDescribingJson[];
274278
},
275279
};
276280
}

libraries/tracker-core/src/core.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import { LOG } from './logger';
4545
* Export interface for any Self-Describing JSON such as context or Self Describing events
4646
* @typeParam T - The type of the data object within a SelfDescribingJson
4747
*/
48-
export type SelfDescribingJson<T extends { [_: string]: unknown } = Record<string, unknown>> = {
48+
export type SelfDescribingJson<T = Record<string, unknown>> = {
4949
/**
5050
* The schema string
5151
* @example 'iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0'
@@ -54,14 +54,14 @@ export type SelfDescribingJson<T extends { [_: string]: unknown } = Record<strin
5454
/**
5555
* The data object which should conform to the supplied schema
5656
*/
57-
data: T;
57+
data: T extends any[] ? never : T extends {} ? T : never;
5858
};
5959

6060
/**
6161
* Export interface for any Self-Describing JSON which has the data attribute as an array
6262
* @typeParam T - The type of the data object within the SelfDescribingJson data array
6363
*/
64-
export type SelfDescribingJsonArray<T extends { [_: string]: unknown } = Record<string, unknown>> = {
64+
export type SelfDescribingJsonArray<T = Record<string, unknown>> = {
6565
/**
6666
* The schema string
6767
* @example 'iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-1'
@@ -119,7 +119,7 @@ function getTimestamp(timestamp?: Timestamp | null): TimestampPayload {
119119
}
120120

121121
/** Additional data points to set when tracking an event */
122-
export interface CommonEventProperties<T extends { [_: string]: unknown } = Record<string, unknown>> {
122+
export interface CommonEventProperties<T = Record<string, unknown>> {
123123
/** Add context to an event by setting an Array of Self Describing JSON */
124124
context?: Array<SelfDescribingJson<T>> | null;
125125
/** Set the true timestamp or overwrite the device sent timestamp on an event */
@@ -382,9 +382,9 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore
382382
* @param timestamp - Timestamp of the event
383383
* @returns Payload after the callback is applied or undefined if the event is skipped
384384
*/
385-
function track(
385+
function track<C = Record<string, unknown>>(
386386
pb: PayloadBuilder,
387-
context?: Array<SelfDescribingJson> | null,
387+
context?: Array<SelfDescribingJson<C>> | null,
388388
timestamp?: Timestamp | null
389389
): Payload | undefined {
390390
pb.withJsonProcessor(payloadJsonProcessor(encodeBase64));
@@ -565,9 +565,9 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore
565565
* A custom event type, allowing for an event to be tracked using your own custom schema
566566
* and a data object which conforms to the supplied schema
567567
*/
568-
export interface SelfDescribingEvent {
568+
export interface SelfDescribingEvent<T = Record<string, unknown>> {
569569
/** The Self Describing JSON which describes the event */
570-
event: SelfDescribingJson;
570+
event: SelfDescribingJson<T>;
571571
}
572572

573573
/**
@@ -578,7 +578,7 @@ export interface SelfDescribingEvent {
578578
* @param event - Contains the properties and schema location for the event
579579
* @returns PayloadBuilder to be sent to {@link @snowplow/tracker-core#TrackerCore.track}
580580
*/
581-
export function buildSelfDescribingEvent(event: SelfDescribingEvent): PayloadBuilder {
581+
export function buildSelfDescribingEvent<T = Record<string, unknown>>(event: SelfDescribingEvent<T>): PayloadBuilder {
582582
const {
583583
event: { schema, data },
584584
} = event,

plugins/browser-plugin-snowplow-ecommerce/src/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CommonEventProperties, SelfDescribingJson } from '@snowplow/tracker-core';
1+
import { CommonEventProperties } from '@snowplow/tracker-core';
22

33
/**
44
* Type/Schema for an ecommerce Action
@@ -317,10 +317,9 @@ export interface User {
317317
email?: string;
318318
}
319319

320-
export interface CommonEcommerceEventProperties<T extends { [_: string]: unknown } = Record<string, unknown>>
321-
extends CommonEventProperties<T> {
320+
export interface CommonEcommerceEventProperties<T = Record<string, unknown>> extends CommonEventProperties<T> {
322321
/** Add context to an event by setting an Array of Self Describing JSON */
323-
context?: Array<SelfDescribingJson<T>>;
322+
context?: Exclude<CommonEventProperties<T>['context'], null>;
324323
}
325324

326325
export type ListViewEvent = { name: string; products: Product[] };

trackers/browser-tracker/src/api.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,10 @@ export function trackStructEvent(event: StructuredEvent & CommonEventProperties,
400400
* @param event - The event information
401401
* @param trackers - The tracker identifiers which the event will be sent to
402402
*/
403-
export function trackSelfDescribingEvent(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array<string>) {
403+
export function trackSelfDescribingEvent<T = Record<string, unknown>>(
404+
event: SelfDescribingEvent<T> & CommonEventProperties,
405+
trackers?: Array<string>
406+
) {
404407
dispatchToTrackers(trackers, (t) => {
405408
t.core.track(buildSelfDescribingEvent({ event: event.event }), event.context, event.timestamp);
406409
});
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
describe('Snowtype compat', () => {
2+
it('pass if this snowtype-generated file passes typechecks', () => {});
3+
});
4+
// generated with Snowtype v0.8.4, package import on below line needs to be updated to '../src'
5+
6+
import { trackSelfDescribingEvent, CommonEventProperties, SelfDescribingJson } from '../src';
7+
// Automatically generated by Snowtype
8+
9+
/**
10+
* Schema for an example event
11+
*/
12+
export type SubscriptionFunnel = {
13+
/**
14+
* the action of the funnel step
15+
*/
16+
action?: null | string;
17+
/**
18+
* the number of the funnel step
19+
*/
20+
step: number;
21+
/**
22+
* the type of subscription the user is signing up to
23+
*/
24+
subscription_type?: null | string;
25+
};
26+
27+
/**
28+
* Creates a Snowplow Event Specification entity.
29+
*/
30+
export function createEventSpecification(eventSpecification: EventSpecification) {
31+
return {
32+
schema: 'iglu:com.snowplowanalytics.snowplow/event_specification/jsonschema/1-0-2',
33+
data: eventSpecification,
34+
};
35+
}
36+
37+
/**
38+
* Automatically attached context for event specifications
39+
*/
40+
interface EventSpecification {
41+
id: string;
42+
name: string;
43+
data_product_id: string;
44+
data_product_name: string;
45+
}
46+
47+
type ContextsOrTimestamp<T = any> = Omit<CommonEventProperties<T>, 'context'> & {
48+
context?: SelfDescribingJson<T>[] | null | undefined;
49+
};
50+
/**
51+
* Track a Snowplow event for SubscriptionFunnel.
52+
* Schema for an example event
53+
*/
54+
export function trackSubscriptionFunnel<T extends {} = any>(
55+
subscriptionFunnel: SubscriptionFunnel & ContextsOrTimestamp<T>,
56+
trackers?: string[]
57+
) {
58+
const { context, timestamp, ...data } = subscriptionFunnel;
59+
const event: SelfDescribingJson<typeof data> = {
60+
schema: 'iglu:com.example/subscription_funnel/jsonschema/1-0-0',
61+
data,
62+
};
63+
64+
trackSelfDescribingEvent(
65+
{
66+
event,
67+
context,
68+
timestamp,
69+
},
70+
trackers
71+
);
72+
}
73+
74+
/**
75+
* Creates a Snowplow SubscriptionFunnel entity.
76+
*/
77+
export function createSubscriptionFunnel(subscriptionFunnel: SubscriptionFunnel) {
78+
return {
79+
schema: 'iglu:com.example/subscription_funnel/jsonschema/1-0-0',
80+
data: subscriptionFunnel,
81+
};
82+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import test from 'ava';
2+
test('pass if this snowtype-generated file passes typechecks', () => {});
3+
// generated with Snowtype v0.8.4, package import on below line needs to be updated to '../src'
4+
5+
import { buildSelfDescribingEvent, SelfDescribingJson, Timestamp, Tracker } from '../src';
6+
// Automatically generated by Snowtype
7+
8+
/**
9+
* Schema for an example event
10+
*/
11+
export type SubscriptionFunnel = {
12+
/**
13+
* the action of the funnel step
14+
*/
15+
action?: null | string;
16+
/**
17+
* the number of the funnel step
18+
*/
19+
step: number;
20+
/**
21+
* the type of subscription the user is signing up to
22+
*/
23+
subscription_type?: null | string;
24+
};
25+
26+
interface CommonEventProperties<T = Record<string, unknown>> {
27+
/** Add context to an event by setting an Array of Self Describing JSON */
28+
context?: Array<SelfDescribingJson<T>> | null;
29+
/** Set the true timestamp or overwrite the device sent timestamp on an event */
30+
timestamp?: Timestamp | null;
31+
}
32+
33+
/**
34+
* Creates a Snowplow Event Specification entity.
35+
*/
36+
export function createEventSpecification(eventSpecification: EventSpecification) {
37+
return {
38+
schema: 'iglu:com.snowplowanalytics.snowplow/event_specification/jsonschema/1-0-2',
39+
data: eventSpecification,
40+
};
41+
}
42+
43+
/**
44+
* Automatically attached context for event specifications
45+
*/
46+
interface EventSpecification {
47+
id: string;
48+
name: string;
49+
data_product_id: string;
50+
data_product_name: string;
51+
}
52+
53+
type ContextsOrTimestamp<T = any> = Omit<CommonEventProperties<T>, 'context'> & {
54+
context?: SelfDescribingJson<T>[] | null | undefined;
55+
};
56+
57+
/**
58+
* Track a Snowplow event for SubscriptionFunnel.
59+
* Schema for an example event
60+
*/
61+
export function trackSubscriptionFunnel<T extends {} = any>(
62+
tracker: Tracker,
63+
subscriptionFunnel: SubscriptionFunnel & ContextsOrTimestamp<T>
64+
) {
65+
const { context, timestamp, ...data } = subscriptionFunnel;
66+
tracker.track(
67+
buildSelfDescribingEvent({
68+
event: {
69+
schema: 'iglu:com.example/subscription_funnel/jsonschema/1-0-0',
70+
data,
71+
},
72+
}),
73+
context,
74+
timestamp
75+
);
76+
}
77+
78+
/**
79+
* Creates a Snowplow SubscriptionFunnel entity.
80+
*/
81+
export function createSubscriptionFunnel(subscriptionFunnel: SubscriptionFunnel) {
82+
return {
83+
schema: 'iglu:com.example/subscription_funnel/jsonschema/1-0-0',
84+
data: subscriptionFunnel,
85+
};
86+
}

0 commit comments

Comments
 (0)