Skip to content

Commit fb4e419

Browse files
committed
utm
1 parent d03e093 commit fb4e419

9 files changed

Lines changed: 40 additions & 56 deletions

File tree

src/billing/types/paymentData.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Utm } from '@hawk.so/types';
2+
13
/**
24
* Data for setting up recurring payments
35
*/
@@ -83,13 +85,7 @@ export interface PaymentData {
8385
/**
8486
* UTM parameters captured when promo was applied
8587
*/
86-
promoUtm?: {
87-
source?: string;
88-
medium?: string;
89-
campaign?: string;
90-
content?: string;
91-
term?: string;
92-
};
88+
promoUtm?: Utm;
9389
/**
9490
* True if this is card linking operation – charging minimal amount of money to validate card info
9591
*/

src/models/user.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import objectHasOnlyProps from '../utils/objectHasOnlyProps';
77
import { NotificationsChannelsDBScheme } from '../types/notification-channels';
88
import { BankCard, UserDBScheme } from '@hawk.so/types';
99
import { v4 as uuid } from 'uuid';
10+
import type { Utm } from '@hawk.so/types';
1011

1112
/**
1213
* Utility type for making specific fields optional
@@ -139,7 +140,7 @@ export default class UserModel extends AbstractModel<Omit<UserDBScheme, '_id'>>
139140
/**
140141
* UTM parameters from signup - Data form where user went to sign up. Used for analytics purposes
141142
*/
142-
public utm?: UserDBScheme['utm'];
143+
public utm?: Utm;
143144

144145
/**
145146
* External identities for SSO (keyed by workspaceId)

src/models/usersFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import UserModel from './user';
33
import { Collection, Db, OptionalId } from 'mongodb';
44
import DataLoaders from '../dataLoaders';
55
import { UserDBScheme } from '@hawk.so/types';
6+
import type { Utm } from '@hawk.so/types';
67

78
/**
89
* Users factory to work with User Model
@@ -66,7 +67,7 @@ export default class UsersFactory extends AbstractModelFactory<Omit<UserDBScheme
6667
public async create(
6768
email: string,
6869
password?: string,
69-
utm?: UserDBScheme['utm']
70+
utm?: Utm
7071
): Promise<UserModel> {
7172
const generatedPassword = password || (await UserModel.generatePassword());
7273
const hashedPassword = await UserModel.hashPassword(generatedPassword);

src/resolvers/billingNew.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import * as telegram from '../utils/telegram';
1414
import { TelegramBotURLs } from '../utils/telegram';
1515
import PromoCodeService, { PromoCodeError, PromoCodeErrorCode, PromoCodePreviewResult } from '../utils/promoCodeService';
1616
import { publish } from '../rabbitmq';
17+
import type { Utm } from '@hawk.so/types';
18+
import { validateUtmParams } from '../utils/utm/utm';
1719

1820
/**
1921
* The amount we will debit to confirm the subscription.
@@ -30,13 +32,7 @@ interface ComposePaymentArgs {
3032
tariffPlanId: string;
3133
shouldSaveCard?: boolean;
3234
promoCode?: string;
33-
promoUtm?: {
34-
source?: string;
35-
medium?: string;
36-
campaign?: string;
37-
content?: string;
38-
term?: string;
39-
};
35+
promoUtm?: Utm;
4036
};
4137
}
4238

@@ -47,13 +43,7 @@ interface PreviewPromoCodeArgs {
4743
input: {
4844
workspaceId: string;
4945
value: string;
50-
utm?: {
51-
source?: string;
52-
medium?: string;
53-
campaign?: string;
54-
content?: string;
55-
term?: string;
56-
};
46+
utm?: Utm;
5747
};
5848
}
5949

@@ -132,7 +122,8 @@ export default {
132122
finalAmount?: number;
133123
discountAmount?: number;
134124
}> {
135-
const { workspaceId, tariffPlanId, shouldSaveCard, promoCode, promoUtm } = input;
125+
const { workspaceId, tariffPlanId, shouldSaveCard, promoCode } = input;
126+
const promoUtm = validateUtmParams(input.promoUtm);
136127

137128
if (!workspaceId || !tariffPlanId || !user?.id) {
138129
throw new UserInputError('No workspaceId, tariffPlanId or user id provided');
@@ -186,7 +177,7 @@ export default {
186177
originalAmount: pricing.originalAmount,
187178
finalAmount: pricing.finalAmount,
188179
discountAmount: pricing.discountAmount,
189-
promoUtm,
180+
...(promoUtm && Object.keys(promoUtm).length > 0 ? { promoUtm } : {}),
190181
};
191182
} catch (error) {
192183
throwPromoCodeGraphQLError(error);
@@ -361,7 +352,7 @@ debug: ${Boolean(workspace.isDebug)}`
361352
};
362353
}
363354

364-
await promoCodeService.applyGrantPlan(input.value, user.id, workspace, input.utm);
355+
await promoCodeService.applyGrantPlan(input.value, user.id, workspace, validateUtmParams(input.utm));
365356

366357
await publish('cron-tasks', 'cron-tasks/limiter', JSON.stringify({
367358
type: 'unblock-workspace',

src/resolvers/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { SenderWorkerTaskType } from '../types/userNotifications';
88
import { TaskPriorities, emailNotification } from '../utils/emailNotifications';
99
import isE2E from '../utils/isE2E';
1010
import { dateFromObjectId } from '../utils/dates';
11-
import { UserDBScheme } from '@hawk.so/types';
1211
import * as telegram from '../utils/telegram';
1312
import { MongoError } from 'mongodb';
13+
import type { Utm } from '@hawk.so/types';
1414
import { validateUtmParams } from '../utils/utm/utm';
1515

1616
/**
@@ -43,7 +43,7 @@ export default {
4343
*/
4444
async signUp(
4545
_obj: undefined,
46-
{ email, utm }: { email: string; utm?: UserDBScheme['utm'] },
46+
{ email, utm }: { email: string; utm?: Utm },
4747
{ factories }: ResolverContextBase
4848
): Promise<boolean | string> {
4949
const validatedUtm = validateUtmParams(utm);

src/typeDefs/billing.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ input ComposePaymentInput {
244244
"""
245245
UTM parameters captured when promo code was applied
246246
"""
247-
promoUtm: PromoCodeUtmInput
247+
promoUtm: UtmInput
248248
}
249249
250250
"""
@@ -264,18 +264,7 @@ input PreviewPromoCodeInput {
264264
"""
265265
UTM parameters captured when promo code was applied
266266
"""
267-
utm: PromoCodeUtmInput
268-
}
269-
270-
"""
271-
UTM data stored with promo usage
272-
"""
273-
input PromoCodeUtmInput {
274-
source: String
275-
medium: String
276-
campaign: String
277-
content: String
278-
term: String
267+
utm: UtmInput
279268
}
280269
281270
"""

src/utils/checksumService.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import jwt, { Secret } from 'jsonwebtoken';
2+
import type { Utm } from '@hawk.so/types';
23

34
export type ChecksumData = PlanPurchaseChecksumData | CardLinkChecksumData;
45

@@ -50,13 +51,7 @@ interface PlanPurchaseChecksumData {
5051
/**
5152
* UTM parameters captured when promo was applied
5253
*/
53-
promoUtm?: {
54-
source?: string;
55-
medium?: string;
56-
campaign?: string;
57-
content?: string;
58-
term?: string;
59-
};
54+
promoUtm?: Utm;
6055
}
6156

6257
interface CardLinkChecksumData {

src/utils/promoCodeService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { ObjectId } from 'mongodb';
22
import {
33
PromoCodeBenefit,
4-
PromoCodeBenefitType,
5-
PromoCodeUsageDBScheme
4+
PromoCodeBenefitType
65
} from '@hawk.so/types';
76
import PlanModel from '../models/plan';
87
import PromoCodeModel from '../models/promoCode';
98
import WorkspaceModel from '../models/workspace';
109
import { ContextFactories } from '../types/graphql';
10+
import type { Utm } from '@hawk.so/types';
1111

1212
const PROMO_CODE_REGEXP = /^[A-Z0-9_-]+$/;
1313
const DEFAULT_MIN_FINAL_PRICE = 1;
@@ -135,7 +135,7 @@ export interface PromoCodePreviewResult {
135135
/**
136136
* UTM data stored with promo code usage.
137137
*/
138-
export type PromoCodeUtm = PromoCodeUsageDBScheme['utm'];
138+
export type PromoCodeUtm = Utm;
139139

140140
/**
141141
* Normalizes promo code value before DB lookup.
@@ -475,7 +475,7 @@ export default class PromoCodeService {
475475
finalAmount: params.finalAmount,
476476
discountAmount: params.discountAmount,
477477
appliedAt: new Date(),
478-
utm: params.utm,
478+
...(params.utm && Object.keys(params.utm).length > 0 ? { utm: params.utm } : {}),
479479
});
480480
} catch (error) {
481481
if ((error as { code?: number }).code === 11000) {

src/utils/utm/utm.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
import type { Utm } from '@hawk.so/types';
2+
13
/**
24
* Valid UTM parameter keys
35
*/
4-
const VALID_UTM_KEYS = ['source', 'medium', 'campaign', 'content', 'term'];
6+
const VALID_UTM_KEYS = ['source', 'medium', 'campaign', 'content', 'term'] as const;
7+
8+
/**
9+
* Checks that passed key is supported UTM field.
10+
*
11+
* @param key - UTM object key
12+
*/
13+
function isValidUtmKey(key: string): key is keyof Utm {
14+
return (VALID_UTM_KEYS as readonly string[]).includes(key);
15+
}
516

617
/**
718
* Regular expression for valid UTM characters
@@ -19,16 +30,16 @@ const MAX_UTM_VALUE_LENGTH = 50;
1930
* @param {Object} utm - UTM parameters to validate
2031
* @returns {Object} - filtered valid UTM parameters
2132
*/
22-
export function validateUtmParams(utm: any): Record<string, string> | undefined {
33+
export function validateUtmParams(utm: any): Utm | undefined {
2334
if (!utm || typeof utm !== 'object' || Array.isArray(utm)) {
2435
return undefined;
2536
}
2637

27-
const result: Record<string, string> = {};
38+
const result: Utm = {};
2839

2940
for (const [key, value] of Object.entries(utm)) {
3041
// 1) Remove keys that are not VALID_UTM_KEYS
31-
if (!VALID_UTM_KEYS.includes(key)) {
42+
if (!isValidUtmKey(key)) {
3243
continue;
3344
}
3445

0 commit comments

Comments
 (0)