Skip to content

Commit 2c66ca2

Browse files
authored
Merge pull request #99 from Relewise/feature/user-query
Feature: user query
2 parents 34a83f8 + 2642f12 commit 2c66ca2

File tree

7 files changed

+321
-1
lines changed

7 files changed

+321
-1
lines changed

packages/integrations/src/builders/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export * from './productcategories';
44
export * from './contentcategories';
55
export * from './content';
66
export * from './brands';
7-
export * from './companies';
7+
export * from './companies';
8+
export * from './users';
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { UserQueryCriteria } from "@relewise/client";
2+
3+
export class CriteriaBuilder {
4+
5+
private userQueryCriteria: UserQueryCriteria;
6+
7+
constructor() {
8+
this.userQueryCriteria = {};
9+
}
10+
11+
public byAuthenticatedId(authenticatedId: string): this {
12+
this.userQueryCriteria.authenticatedId = authenticatedId;
13+
14+
return this;
15+
}
16+
17+
public byTemporaryId(temporaryId: string): this {
18+
this.userQueryCriteria.temporaryId = temporaryId;
19+
20+
return this;
21+
}
22+
23+
public byEmail(email: string): this {
24+
this.userQueryCriteria.email = email;
25+
26+
return this;
27+
}
28+
29+
public byIdentifier(key: string, value: string): this {
30+
if (!this.userQueryCriteria.identifiers)
31+
this.userQueryCriteria.identifiers = {};
32+
33+
this.userQueryCriteria.identifiers[key] = value;
34+
35+
return this;
36+
}
37+
38+
public byIdentifiers(identifiers: Record<string, string>): this {
39+
if (!this.userQueryCriteria.identifiers) {
40+
this.userQueryCriteria.identifiers = {};
41+
}
42+
43+
this.userQueryCriteria.identifiers = {
44+
...this.userQueryCriteria.identifiers,
45+
...identifiers
46+
};
47+
48+
return this;
49+
}
50+
51+
public language(language: string): this {
52+
this.userQueryCriteria.language = { value: language };
53+
54+
return this;
55+
}
56+
57+
public currency(currency: string): this {
58+
this.userQueryCriteria.currency = { value: currency };
59+
60+
return this;
61+
}
62+
63+
64+
build(): UserQueryCriteria {
65+
return this.userQueryCriteria;
66+
}
67+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './criteriaBuilder';
2+
export * from './userQueryBuilder';
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { UserQuery } from "@relewise/client";
2+
import { CriteriaBuilder } from "./criteriaBuilder";
3+
4+
export class UserQueryBuilder {
5+
6+
private userQuery: UserQuery;
7+
8+
constructor() {
9+
this.userQuery = {
10+
$type: "Relewise.Client.Requests.Queries.UserQuery, Relewise.Client",
11+
criteria: []
12+
}
13+
}
14+
15+
public criteria(criteria: (criteria: CriteriaBuilder) => void): this {
16+
const localCriteriaBuilder = new CriteriaBuilder();
17+
18+
criteria(localCriteriaBuilder);
19+
20+
this.userQuery.criteria.push(localCriteriaBuilder.build());
21+
22+
return this;
23+
}
24+
25+
public language(language: string): this {
26+
this.userQuery.language = { value: language };
27+
28+
return this;
29+
}
30+
31+
public currency(currency: string): this {
32+
this.userQuery.currency = { value: currency };
33+
34+
return this;
35+
}
36+
37+
build(): UserQuery {
38+
return this.userQuery;
39+
}
40+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { RelewiseClient, RelewiseClientOptions, RelewiseRequestOptions, UserQuery, UserDetailsCollectionResponse } from '@relewise/client';
2+
3+
export class DataAccessor extends RelewiseClient {
4+
5+
constructor(protected readonly datasetId: string, protected readonly apiKey: string, options?: RelewiseClientOptions) {
6+
super(datasetId, apiKey, options);
7+
}
8+
9+
public async queryUsers(request: UserQuery, options?: RelewiseRequestOptions): Promise<UserDetailsCollectionResponse | undefined> {
10+
return this.request<UserQuery, UserDetailsCollectionResponse>('UserQuery', request, options);
11+
}
12+
}

packages/integrations/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './integrator';
2+
export * from './dataAccessor';
23
export * from './builders';
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { expect, test } from '@jest/globals';
2+
import { DataAccessor, UserQueryBuilder } from '../../../src';
3+
import { DataValueFactory, MultiCurrency, Multilingual, MultilingualDataValue, Tracker, UserFactory } from '@relewise/client';
4+
import { randomUUID } from 'crypto';
5+
const { npm_config_API_KEY: API_KEY, npm_config_DATASET_ID: DATASET_ID, npm_config_SERVER_URL: SERVER_URL } = process.env;
6+
7+
const dataAccessor = new DataAccessor(DATASET_ID!, API_KEY!, { serverUrl: SERVER_URL });
8+
const tracker = new Tracker(DATASET_ID!, API_KEY!, { serverUrl: SERVER_URL });
9+
10+
test('Query Users when no user found', async() => {
11+
const randomId = randomUUID();
12+
13+
const query = new UserQueryBuilder()
14+
.criteria(c => c.byAuthenticatedId(randomId))
15+
.build();
16+
17+
const result = await dataAccessor.queryUsers(query);
18+
19+
expect(result?.results).toBeDefined();
20+
expect(result?.results).toHaveLength(1);
21+
expect(result?.results![0]).toHaveLength(0);
22+
});
23+
24+
test('Query Users when user found by authenticated id', async() => {
25+
const authenticatedId = "some authenticated id";
26+
27+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byAuthenticatedId(authenticatedId)});
28+
29+
const query = new UserQueryBuilder()
30+
.criteria(c => c.byAuthenticatedId(authenticatedId))
31+
.build();
32+
33+
const result = await dataAccessor.queryUsers(query);
34+
35+
expect(result?.results).toBeDefined();
36+
expect(result?.results).toHaveLength(1);
37+
expect(result?.results![0]).toHaveLength(1);
38+
expect(result?.results![0][0].authenticatedId).toBe(authenticatedId);
39+
});
40+
41+
test('Query Users when user found by temporary id', async() => {
42+
const temporaryId = "some temporary id";
43+
44+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byTemporaryId(temporaryId)});
45+
46+
const query = new UserQueryBuilder()
47+
.criteria(c => c.byTemporaryId(temporaryId))
48+
.build();
49+
50+
const result = await dataAccessor.queryUsers(query);
51+
52+
expect(result?.results).toBeDefined();
53+
expect(result?.results).toHaveLength(1);
54+
expect(result?.results![0]).toHaveLength(1);
55+
expect(result?.results![0][0].temporaryId).toBe(temporaryId);
56+
});
57+
58+
test('Query Users when user found by email', async() => {
59+
const email = "some@email.com";
60+
61+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byEmail(email)});
62+
63+
const query = new UserQueryBuilder()
64+
.criteria(c => c.byEmail(email))
65+
.build();
66+
67+
const result = await dataAccessor.queryUsers(query);
68+
69+
expect(result?.results).toBeDefined();
70+
expect(result?.results).toHaveLength(1);
71+
expect(result?.results![0]).toHaveLength(1);
72+
expect(result?.results![0][0].email).toBe(email);
73+
});
74+
75+
test('Query Users when user found by identifier', async() => {
76+
const key = "SomeKey";
77+
const value = "SomeValue";
78+
79+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byIdentifier(key, value)});
80+
81+
const query = new UserQueryBuilder()
82+
.criteria(c => c.byIdentifier(key, value))
83+
.build();
84+
85+
const result = await dataAccessor.queryUsers(query);
86+
87+
expect(result?.results).toBeDefined();
88+
expect(result?.results).toHaveLength(1);
89+
expect(result?.results![0]).toHaveLength(1);
90+
expect(result?.results![0][0].identifiers).toBeDefined();
91+
92+
// The API always to lower cases indentifer keys
93+
expect(result?.results![0][0].identifiers![key.toLowerCase()]).toBe(value);
94+
});
95+
96+
test('Query Users when user found by identifier', async() => {
97+
const key = "SomeKey";
98+
const value = "SomeValue";
99+
100+
const key2 = "SomeKey2";
101+
const value2 = "SomeValue2";
102+
103+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byIdentifiers({ [key]: value, [key2]: value2 })});
104+
105+
const query = new UserQueryBuilder()
106+
.criteria(c => c.byIdentifiers({ [key]: value, [key2]: value2 }))
107+
.build();
108+
109+
const result = await dataAccessor.queryUsers(query);
110+
111+
expect(result?.results).toBeDefined();
112+
expect(result?.results).toHaveLength(1);
113+
expect(result?.results![0]).toHaveLength(1);
114+
expect(result?.results![0][0].identifiers).toBeDefined();
115+
116+
// The API always to lower cases indentifer keys
117+
expect(result?.results![0][0].identifiers![key.toLowerCase()]).toBe(value);
118+
expect(result?.results![0][0].identifiers![key2.toLowerCase()]).toBe(value2);
119+
});
120+
121+
test('Query Users when user found by authenticated id', async() => {
122+
const authenticatedId = "some authenticated id";
123+
const temporaryId = "some temporary id";
124+
125+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byAuthenticatedId(authenticatedId)});
126+
await tracker.trackProductView({ productId: "SomeProduct", user: UserFactory.byTemporaryId(temporaryId)});
127+
128+
const query = new UserQueryBuilder()
129+
.criteria(c => c.byAuthenticatedId(authenticatedId))
130+
.criteria(c => c.byTemporaryId(temporaryId))
131+
.build();
132+
133+
const result = await dataAccessor.queryUsers(query);
134+
135+
expect(result?.results).toBeDefined();
136+
expect(result?.results).toHaveLength(2);
137+
expect(result?.results![0]).toHaveLength(1);
138+
expect(result?.results![0][0].authenticatedId).toBe(authenticatedId);
139+
expect(result?.results![1]).toHaveLength(1);
140+
expect(result?.results![1][0].temporaryId).toBe(temporaryId);
141+
});
142+
143+
test('Query Users when no language or currency provided', async() => {
144+
const authenticatedId = "some authenticated id";
145+
146+
const user = UserFactory.byAuthenticatedId(authenticatedId);
147+
148+
const multilingualKey = "multilingual";
149+
const multiCurrencyKey = "multiCurrency";
150+
151+
user.data = {};
152+
user.data[multilingualKey] = DataValueFactory.multilingual([{ language: "da", value: "123" }, { language: "en", value: "456" }]);
153+
user.data[multiCurrencyKey] = DataValueFactory.multiCurrency([{ currency: "DKK", amount: 123 }, { currency: "EUR", amount: 123 }]);
154+
155+
await tracker.trackProductView({ productId: "SomeProduct", user: user});
156+
157+
const query = new UserQueryBuilder()
158+
.criteria(c => c.byAuthenticatedId(authenticatedId))
159+
.build();
160+
161+
const result = await dataAccessor.queryUsers(query);
162+
163+
expect(result?.results).toBeDefined();
164+
expect(result?.results).toHaveLength(1);
165+
expect(result?.results![0]).toHaveLength(1);
166+
expect((result?.results![0][0].data![multilingualKey].value as Multilingual).values).toHaveLength(2)
167+
expect((result?.results![0][0].data![multiCurrencyKey].value as MultiCurrency).values).toHaveLength(2)
168+
});
169+
170+
test('Query Users when a language is provided', async() => {
171+
const authenticatedId = "some authenticated id";
172+
173+
const user = UserFactory.byAuthenticatedId(authenticatedId);
174+
175+
const multilingualKey = "multilingual";
176+
177+
const da = "da";
178+
const en = "en";
179+
180+
user.data = {};
181+
user.data[multilingualKey] = DataValueFactory.multilingual([{ language: da, value: "123" }, { language: en, value: "456" }]);
182+
183+
await tracker.trackProductView({ productId: "SomeProduct", user: user});
184+
185+
const query = new UserQueryBuilder()
186+
.criteria(c => c.byAuthenticatedId(authenticatedId))
187+
.language(da)
188+
.build();
189+
190+
const result = await dataAccessor.queryUsers(query);
191+
192+
expect(result?.results).toBeDefined();
193+
expect(result?.results).toHaveLength(1);
194+
expect(result?.results![0]).toHaveLength(1);
195+
expect((result?.results![0][0].data![multilingualKey].value as Multilingual).values).toHaveLength(1)
196+
expect((result?.results![0][0].data![multilingualKey].value as Multilingual).values![0].language.value).toBe(da)
197+
});

0 commit comments

Comments
 (0)