Skip to content
This repository was archived by the owner on Nov 15, 2024. It is now read-only.

Commit 54e6af3

Browse files
committed
Added get_marketing_consent
1 parent 6006462 commit 54e6af3

File tree

8 files changed

+153
-2
lines changed

8 files changed

+153
-2
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,28 @@ Creates a new user and subscription trial. The magic link sent to the user is tr
224224
- `fingerprint`: The physical hardware id.
225225

226226

227+
### `get_marketing_consent`
228+
229+
Returns the selected marketing consent level for a user. An error with the code `awaiting_consent` will be returned if the user hasn't set a consent level yet.
230+
231+
#### Request
232+
233+
```json
234+
{
235+
"user_id": "user_000000C2kdCzNlbL1BqR5FeMatItU"
236+
}
237+
```
238+
239+
#### Response
240+
241+
```json
242+
{
243+
"level": "general"
244+
}
245+
```
246+
247+
- `level`: The consent level selected. Either `general` or `none`.
248+
227249
## Webhooks
228250

229251
Webhooks URL's comprise of two parts; the webhook indicator, followed by the provider value. Each provider has an example below.

src/app/get-marketing-consent.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Context, GetMarketingConsentRequest, GetMarketingConsentResponse } from '../types';
2+
import Squawk from '../utils/squawk';
3+
4+
export default async function getMarketingConsent(
5+
ctx: Context,
6+
request: GetMarketingConsentRequest,
7+
): Promise<GetMarketingConsentResponse> {
8+
const user = await ctx.app.dbClient.users.findUser(request.userId);
9+
10+
try {
11+
const consent = await ctx.app.dbClient.marketingConsent.findUserMarketingConsent(user.id);
12+
13+
return { level: consent.level };
14+
} catch (error) {
15+
if (!(error instanceof Squawk))
16+
throw error;
17+
18+
if (error.code === 'not_found')
19+
throw new Squawk('awaiting_consent');
20+
21+
throw error;
22+
}
23+
}

src/db/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Db, MongoClient } from 'mongodb';
33
import AccessTokens from './access-tokens';
44
import Authorizations from './authorizations';
55
import Identifiers from './identifiers';
6+
import MarketingConsents from './marketing-consent';
67
import ProviderMappings from './provider-mappings';
78
import RefreshTokens from './refresh-tokens';
89
import Subscriptions from './subscriptions';
@@ -16,6 +17,7 @@ export default class DbClient {
1617
accessTokens!: AccessTokens;
1718
authorizations!: Authorizations;
1819
identifiers!: Identifiers;
20+
marketingConsent!: MarketingConsents;
1921
providerMappings!: ProviderMappings;
2022
refreshTokens!: RefreshTokens;
2123
subscriptions!: Subscriptions;
@@ -34,6 +36,7 @@ export default class DbClient {
3436
this.authorizations = new Authorizations(this.db);
3537
this.accessTokens = new AccessTokens(this.db);
3638
this.identifiers = new Identifiers(this.db);
39+
this.marketingConsent = new MarketingConsents(this.db);
3740
this.providerMappings = new ProviderMappings(this.db);
3841
this.refreshTokens = new RefreshTokens(this.db);
3942
this.subscriptions = new Subscriptions(this.db);
@@ -47,6 +50,7 @@ export default class DbClient {
4750
await this.authorizations.setupIndexes();
4851
await this.accessTokens.setupIndexes();
4952
await this.identifiers.setupIndexes();
53+
await this.marketingConsent.setupIndexes();
5054
await this.providerMappings.setupIndexes();
5155
await this.refreshTokens.setupIndexes();
5256
await this.subscriptions.setupIndexes();

src/db/marketing-consent.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import ksuid from '@cuvva/ksuid';
2+
import { Db } from 'mongodb';
3+
4+
import Squawk from '../utils/squawk';
5+
import Collection from './nest-collection';
6+
7+
export type ConsentLevel = 'none' | 'general';
8+
9+
export interface MarketingConsent {
10+
id: string;
11+
userId: string;
12+
level: ConsentLevel;
13+
createdAt: string;
14+
updatedAt: string | null;
15+
}
16+
17+
export default class MarketingConsents extends Collection<MarketingConsent> {
18+
constructor(db: Db) {
19+
super(db, 'marketing-consents');
20+
}
21+
22+
async setupIndexes() {
23+
await this.collection.createIndex({ userId: 1 }, { unique: true });
24+
}
25+
26+
async upsertMarketingConsent(userId: string, level: ConsentLevel) {
27+
const id = ksuid.generate('mktngconsent').toString();
28+
const now = new Date().toISOString();
29+
30+
await this.collection.updateOne({ userId }, {
31+
$set: {
32+
level,
33+
updatedAt: now,
34+
},
35+
$setOnInsert: {
36+
_id: id,
37+
userId,
38+
createdAt: now,
39+
removedAt: null,
40+
},
41+
}, { upsert: true });
42+
}
43+
44+
async findUserMarketingConsent(userId: string) {
45+
const identifier = await this.collection.findOne({ userId });
46+
47+
if (!identifier)
48+
throw new Squawk('not_found');
49+
50+
return this.convertFromMongoDocument(identifier);
51+
}
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"type": "object",
3+
"additionalProperties": false,
4+
5+
"required": [
6+
"user_id"
7+
],
8+
9+
"properties": {
10+
"user_id": {
11+
"type": "string",
12+
"minLength": 1
13+
}
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import getMarketingConsentImpl from '../../app/get-marketing-consent';
2+
import { Context, GetMarketingConsentRequest } from '../../types';
3+
import Squawk from '../../utils/squawk';
4+
import getMarketingConsentSchema from './get-marketing-consent.json';
5+
6+
export default async function getMarketingConsent(ctx: Context, request: GetMarketingConsentRequest) {
7+
if (!ctx.auth)
8+
throw new Squawk('access_denied');
9+
10+
if (ctx.auth.type !== 'internal' && ctx.auth.userId !== request.userId)
11+
throw new Squawk('access_denied');
12+
13+
return await getMarketingConsentImpl(ctx, request);
14+
}
15+
16+
export { getMarketingConsentSchema };

src/interface/rpc/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APIGatewayProxyEventV2 } from 'aws-lambda';
2-
import camelCaseKeys from 'camelcase-keys';
2+
import * as camelCaseKeys from 'camelcase-keys';
33
import { validate } from 'jsonschema';
44
import { Logger } from 'tslog';
55

@@ -8,6 +8,7 @@ import Squawk from '../../utils/squawk';
88
import authenticateUser, { authenticateUserSchema } from './authenticate-user';
99
import createTrialAndMagicLink, { createTrialAndMagicLinkSchema } from './create-trial-and-magic-link';
1010
import enrolAlphaUser, { enrolAlphaUserSchema } from './enrol-alpha-user';
11+
import getMarketingConsent, { getMarketingConsentSchema } from './get-marketing-consent';
1112
import getSubscriptionStatus, { getSubscriptionStatusSchema } from './get-subscription-status';
1213
import getSubscriptionStatus20201214, { getSubscriptionStatusSchema20201214 } from './get-subscription-status-2020-12-14';
1314
import getUser, { getUserSchema } from './get-user';
@@ -27,6 +28,10 @@ const v20201214: VersionSet = {
2728
impl: authenticateUser,
2829
schema: authenticateUserSchema,
2930
},
31+
get_marketing_consent: {
32+
impl: getMarketingConsent,
33+
schema: getMarketingConsentSchema,
34+
},
3035
get_subscription_status: {
3136
impl: getSubscriptionStatus20201214,
3237
schema: getSubscriptionStatusSchema20201214,

src/types.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SESClient } from '@aws-sdk/client-ses';
2-
import { CancelType } from 'db/subscriptions';
2+
import { ConsentLevel } from './db/marketing-consent';
3+
import { CancelType } from './db/subscriptions';
34
import { Stripe } from 'stripe';
45
import { Logger } from 'tslog';
56

@@ -147,6 +148,19 @@ export interface GetUserResponse {
147148
}[];
148149
}
149150

151+
export interface GetMarketingConsentRequest {
152+
userId: string;
153+
}
154+
155+
export interface GetMarketingConsentResponse {
156+
level: ConsentLevel;
157+
}
158+
159+
export interface SetMarketingConsentRequest {
160+
userId: string;
161+
level: ConsentLevel;
162+
}
163+
150164
export interface EnrolAlphaUserRequest {
151165
email: string;
152166
}

0 commit comments

Comments
 (0)