Skip to content

Commit 964a2f7

Browse files
authored
Move resolve directly in web-sdk (#159)
1 parent f56e47e commit 964a2f7

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

lib/edge/resolve.test.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { getConfig } from "../config";
2+
import { parseResolveResponse, Resolve } from "./resolve";
3+
4+
describe("resolve", () => {
5+
test("forwards identifier when present", () => {
6+
const config = getConfig({ host: "host", site: "site" });
7+
8+
const fetchSpy = jest.spyOn(window, "fetch");
9+
10+
Resolve(config, "id");
11+
expect(fetchSpy).toHaveBeenCalledWith(
12+
expect.objectContaining({
13+
method: "GET",
14+
url: `https://host/site/v1/resolve?id=id&osdk=web-0.0.0-experimental&cookies=yes`,
15+
})
16+
);
17+
18+
Resolve(config);
19+
expect.objectContaining({
20+
method: "GET",
21+
url: `https://host/site/v1/resolve?osdk=web-0.0.0-experimental&cookies=yes`,
22+
});
23+
});
24+
});
25+
26+
describe("parseResolveResponse", () => {
27+
test("parses expected responses", () => {
28+
const empty = { clusters: [], lmpid: "" };
29+
30+
const cases = [
31+
// Unexpected response types return empty response
32+
{ input: {}, output: empty },
33+
{ input: null, output: empty },
34+
{ input: undefined, output: empty },
35+
{ input: 1, output: empty },
36+
{ input: true, output: empty },
37+
{ input: [], output: empty },
38+
39+
{ input: { clusters: [null] }, output: empty },
40+
{ input: { clusters: [undefined] }, output: empty },
41+
{ input: { clusters: [1] }, output: empty },
42+
{ input: { clusters: [true] }, output: empty },
43+
{ input: { clusters: [[]] }, output: empty },
44+
45+
{ input: { clusters: [{ ids: {}, traits: {} }] }, output: empty },
46+
{ input: { clusters: [{ ids: null, traits: null }] }, output: empty },
47+
{ input: { clusters: [{ ids: undefined, traits: undefined }] }, output: empty },
48+
{ input: { clusters: [{ ids: 1, traits: 1 }] }, output: empty },
49+
{ input: { clusters: [{ ids: true, traits: true }] }, output: empty },
50+
{ input: { clusters: [{ ids: [], traits: [] }] }, output: empty },
51+
52+
{ input: { clusters: [{ ids: [null], traits: [null] }] }, output: empty },
53+
{ input: { clusters: [{ ids: [undefined], traits: [undefined] }] }, output: empty },
54+
{ input: { clusters: [{ ids: [1], traits: [1] }] }, output: empty },
55+
{ input: { clusters: [{ ids: [true], traits: [true] }] }, output: empty },
56+
57+
// Additional properties are skipped
58+
{
59+
input: {
60+
clusters: [
61+
{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }], additional: "property" },
62+
],
63+
},
64+
output: {
65+
clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }],
66+
lmpid: "",
67+
},
68+
},
69+
{
70+
input: { clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }] },
71+
output: {
72+
clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }],
73+
lmpid: "",
74+
},
75+
},
76+
77+
// Lmpid is returned when matching expected type
78+
{ input: { lmpid: 1 }, output: { clusters: [], lmpid: "" } },
79+
{ input: { lmpid: null }, output: { clusters: [], lmpid: "" } },
80+
{ input: { lmpid: undefined }, output: { clusters: [], lmpid: "" } },
81+
{ input: { lmpid: "lmpid" }, output: { clusters: [], lmpid: "lmpid" } },
82+
];
83+
84+
for (const c of cases) {
85+
expect(parseResolveResponse(c.input)).toEqual(c.output);
86+
}
87+
});
88+
});

lib/edge/resolve.ts

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { ResolvedConfig } from "../config";
2+
import { fetch } from "../core/network";
3+
4+
type ResolveTrait = {
5+
key: string;
6+
value: string;
7+
};
8+
9+
type ResolveCluster = {
10+
ids: string[];
11+
traits: ResolveTrait[];
12+
};
13+
14+
type ResolveResponse = {
15+
clusters: ResolveCluster[];
16+
lmpid?: string;
17+
};
18+
19+
async function Resolve(config: ResolvedConfig, id?: string): Promise<ResolveResponse> {
20+
const searchParams = new URLSearchParams();
21+
if (typeof id === "string") {
22+
searchParams.append("id", id);
23+
}
24+
const path = "/v1/resolve?" + searchParams.toString();
25+
26+
const response = await fetch<unknown>(path, config, {
27+
method: "GET",
28+
headers: { Accept: "application/json" },
29+
});
30+
31+
return parseResolveResponse(response);
32+
}
33+
34+
function parseResolveResponse(resolveResponse: unknown): ResolveResponse {
35+
const response: ResolveResponse = { clusters: [], lmpid: "" };
36+
37+
if (typeof resolveResponse !== "object" || resolveResponse === null) {
38+
return response;
39+
}
40+
41+
if ("lmpid" in resolveResponse && typeof resolveResponse?.lmpid === "string") {
42+
response.lmpid = resolveResponse.lmpid;
43+
}
44+
45+
if (!("clusters" in resolveResponse) || !Array.isArray(resolveResponse?.clusters)) {
46+
return response;
47+
}
48+
49+
for (const c of resolveResponse.clusters) {
50+
const cluster: ResolveCluster = { ids: [], traits: [] };
51+
52+
if (Array.isArray(c?.ids)) {
53+
for (const id of c.ids) {
54+
if (typeof id === "string") {
55+
cluster.ids.push(id);
56+
}
57+
}
58+
}
59+
60+
if (Array.isArray(c?.traits)) {
61+
for (const trait of c.traits) {
62+
if (typeof trait?.key === "string" && typeof trait?.value === "string") {
63+
cluster.traits.push({ key: trait.key, value: trait.value });
64+
}
65+
}
66+
}
67+
68+
if (cluster.ids.length > 0 || cluster.traits.length > 0) {
69+
response.clusters.push(cluster);
70+
}
71+
}
72+
73+
return response;
74+
}
75+
76+
export { Resolve, ResolveResponse, parseResolveResponse };

lib/sdk.ts

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { WitnessProperties } from "./edge/witness";
55
import type { ProfileTraits } from "./edge/profile";
66
import { Identify } from "./edge/identify";
77
import { Uid2Token } from "./edge/uid2_token";
8+
import { Resolve, ResolveResponse } from "./edge/resolve";
89
import { Site, SiteResponse, SiteFromCache } from "./edge/site";
910
import {
1011
TargetingKeyValues,
@@ -99,6 +100,11 @@ class OptableSDK {
99100
return Tokenize(this.dcn, id);
100101
}
101102

103+
async resolve(id?: string): Promise<ResolveResponse> {
104+
await this.init;
105+
return Resolve(this.dcn, id);
106+
}
107+
102108
static eid(email: string): string {
103109
return email ? "e:" + sha256.hex(email.toLowerCase().trim()) : "";
104110
}

0 commit comments

Comments
 (0)