Skip to content

Commit

Permalink
Move resolve directly in web-sdk (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
zapo authored Jan 9, 2025
1 parent f56e47e commit 964a2f7
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
88 changes: 88 additions & 0 deletions lib/edge/resolve.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { getConfig } from "../config";
import { parseResolveResponse, Resolve } from "./resolve";

describe("resolve", () => {
test("forwards identifier when present", () => {
const config = getConfig({ host: "host", site: "site" });

const fetchSpy = jest.spyOn(window, "fetch");

Resolve(config, "id");
expect(fetchSpy).toHaveBeenCalledWith(
expect.objectContaining({
method: "GET",
url: `https://host/site/v1/resolve?id=id&osdk=web-0.0.0-experimental&cookies=yes`,
})
);

Resolve(config);
expect.objectContaining({
method: "GET",
url: `https://host/site/v1/resolve?osdk=web-0.0.0-experimental&cookies=yes`,
});
});
});

describe("parseResolveResponse", () => {
test("parses expected responses", () => {
const empty = { clusters: [], lmpid: "" };

const cases = [
// Unexpected response types return empty response
{ input: {}, output: empty },
{ input: null, output: empty },
{ input: undefined, output: empty },
{ input: 1, output: empty },
{ input: true, output: empty },
{ input: [], output: empty },

{ input: { clusters: [null] }, output: empty },
{ input: { clusters: [undefined] }, output: empty },
{ input: { clusters: [1] }, output: empty },
{ input: { clusters: [true] }, output: empty },
{ input: { clusters: [[]] }, output: empty },

{ input: { clusters: [{ ids: {}, traits: {} }] }, output: empty },
{ input: { clusters: [{ ids: null, traits: null }] }, output: empty },
{ input: { clusters: [{ ids: undefined, traits: undefined }] }, output: empty },
{ input: { clusters: [{ ids: 1, traits: 1 }] }, output: empty },
{ input: { clusters: [{ ids: true, traits: true }] }, output: empty },
{ input: { clusters: [{ ids: [], traits: [] }] }, output: empty },

{ input: { clusters: [{ ids: [null], traits: [null] }] }, output: empty },
{ input: { clusters: [{ ids: [undefined], traits: [undefined] }] }, output: empty },
{ input: { clusters: [{ ids: [1], traits: [1] }] }, output: empty },
{ input: { clusters: [{ ids: [true], traits: [true] }] }, output: empty },

// Additional properties are skipped
{
input: {
clusters: [
{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }], additional: "property" },
],
},
output: {
clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }],
lmpid: "",
},
},
{
input: { clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }] },
output: {
clusters: [{ ids: ["i4:<ip>", "e:<sha256>"], traits: [{ key: "<key>", value: "<value>" }] }],
lmpid: "",
},
},

// Lmpid is returned when matching expected type
{ input: { lmpid: 1 }, output: { clusters: [], lmpid: "" } },
{ input: { lmpid: null }, output: { clusters: [], lmpid: "" } },
{ input: { lmpid: undefined }, output: { clusters: [], lmpid: "" } },
{ input: { lmpid: "lmpid" }, output: { clusters: [], lmpid: "lmpid" } },
];

for (const c of cases) {
expect(parseResolveResponse(c.input)).toEqual(c.output);
}
});
});
76 changes: 76 additions & 0 deletions lib/edge/resolve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { ResolvedConfig } from "../config";
import { fetch } from "../core/network";

type ResolveTrait = {
key: string;
value: string;
};

type ResolveCluster = {
ids: string[];
traits: ResolveTrait[];
};

type ResolveResponse = {
clusters: ResolveCluster[];
lmpid?: string;
};

async function Resolve(config: ResolvedConfig, id?: string): Promise<ResolveResponse> {
const searchParams = new URLSearchParams();
if (typeof id === "string") {
searchParams.append("id", id);
}
const path = "/v1/resolve?" + searchParams.toString();

const response = await fetch<unknown>(path, config, {
method: "GET",
headers: { Accept: "application/json" },
});

return parseResolveResponse(response);
}

function parseResolveResponse(resolveResponse: unknown): ResolveResponse {
const response: ResolveResponse = { clusters: [], lmpid: "" };

if (typeof resolveResponse !== "object" || resolveResponse === null) {
return response;
}

if ("lmpid" in resolveResponse && typeof resolveResponse?.lmpid === "string") {
response.lmpid = resolveResponse.lmpid;
}

if (!("clusters" in resolveResponse) || !Array.isArray(resolveResponse?.clusters)) {
return response;
}

for (const c of resolveResponse.clusters) {
const cluster: ResolveCluster = { ids: [], traits: [] };

if (Array.isArray(c?.ids)) {
for (const id of c.ids) {
if (typeof id === "string") {
cluster.ids.push(id);
}
}
}

if (Array.isArray(c?.traits)) {
for (const trait of c.traits) {
if (typeof trait?.key === "string" && typeof trait?.value === "string") {
cluster.traits.push({ key: trait.key, value: trait.value });
}
}
}

if (cluster.ids.length > 0 || cluster.traits.length > 0) {
response.clusters.push(cluster);
}
}

return response;
}

export { Resolve, ResolveResponse, parseResolveResponse };
6 changes: 6 additions & 0 deletions lib/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { WitnessProperties } from "./edge/witness";
import type { ProfileTraits } from "./edge/profile";
import { Identify } from "./edge/identify";
import { Uid2Token } from "./edge/uid2_token";
import { Resolve, ResolveResponse } from "./edge/resolve";
import { Site, SiteResponse, SiteFromCache } from "./edge/site";
import {
TargetingKeyValues,
Expand Down Expand Up @@ -99,6 +100,11 @@ class OptableSDK {
return Tokenize(this.dcn, id);
}

async resolve(id?: string): Promise<ResolveResponse> {
await this.init;
return Resolve(this.dcn, id);
}

static eid(email: string): string {
return email ? "e:" + sha256.hex(email.toLowerCase().trim()) : "";
}
Expand Down

0 comments on commit 964a2f7

Please sign in to comment.