Skip to content

Commit

Permalink
feat: cloud identifyUser
Browse files Browse the repository at this point in the history
  • Loading branch information
VojtechVidra committed Feb 6, 2024
1 parent 79496b2 commit 539c30f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 38 deletions.
34 changes: 34 additions & 0 deletions workspaces/js/src/cloud/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { DebugEvent, TrackingEvent } from "../types";
import { version } from "../lib/version";
import { hash } from "../utils";
import { log } from "../log";
import { api } from "./api";

export const saveEvent = async ({
apiUrl,
event,
}: {
apiUrl: string;
event: DebugEvent | TrackingEvent;
}): Promise<{ referenceId: string } | undefined> => {
const { flowHash, flowId, type, projectId = "", stepIndex, stepHash, userId, location } = event;
return api(apiUrl)
.sendEvent({
eventTime: new Date().toISOString(),
flowHash,
flowId,
projectId,
type,
stepHash,
stepIndex: stepIndex?.toString(),
userHash: userId ? await hash(userId) : undefined,
sdkVersion: version,
targetElement: "targetElement" in event ? event.targetElement : undefined,
location,
})
.then((res) => ({ referenceId: res.id }))
.catch((err) => {
log.error("Failed to send event to cloud\n", err);
return undefined;
});
};
45 changes: 14 additions & 31 deletions workspaces/js/src/cloud/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { startFlow } from "../public-methods";
import { init as flowsInit } from "../init";
import type { DebugEvent, FlowsCloudOptions, TrackingEvent } from "../types";
import type { FlowsCloudOptions, IdentifyUserOptions } from "../types";
import { hash } from "../utils";
import { log } from "../log";
import { validateFlowsOptions, validateCloudFlowsOptions } from "../validation";
import { version } from "../lib/version";
import { api } from "./api";
import { loadStyle } from "./style";
import { saveEvent } from "./event";

export * from "../index";

let _options: FlowsCloudOptions | null = null;

export const init = async (options: FlowsCloudOptions): Promise<void> => {
const cloudValidationResult = validateCloudFlowsOptions(options);
const coreValidationResult = validateFlowsOptions(options);
Expand All @@ -22,6 +24,7 @@ export const init = async (options: FlowsCloudOptions): Promise<void> => {
validationResult.error.value,
);
if (!validationResult.valid) return;
_options = options;

const apiUrl = options.customApiUrl ?? "https://api.flows-cloud.com";

Expand All @@ -40,43 +43,17 @@ export const init = async (options: FlowsCloudOptions): Promise<void> => {
);
});

const saveEvent = async (
event: DebugEvent | TrackingEvent,
): Promise<{ referenceId: string } | undefined> => {
const { flowHash, flowId, type, projectId = "", stepIndex, stepHash, userId, location } = event;

return api(apiUrl)
.sendEvent({
eventTime: new Date().toISOString(),
flowHash,
flowId,
projectId,
type,
stepHash,
stepIndex: stepIndex?.toString(),
userHash: userId ? await hash(userId) : undefined,
sdkVersion: version,
targetElement: "targetElement" in event ? event.targetElement : undefined,
location,
})
.then((res) => ({ referenceId: res.id }))
.catch((err) => {
log.error("Failed to send event to cloud\n", err);
return undefined;
});
};

return flowsInit({
...options,
flows: [...(options.flows ?? []), ...(flows || [])],
tracking: (event) => {
options.tracking?.(event);
void saveEvent(event);
void saveEvent({ apiUrl, event });
},
_debug: async (event) => {
if (event.type === "invalidateTooltipError") {
if (event.referenceId) void api(apiUrl).deleteEvent(event.referenceId);
} else return saveEvent(event);
} else return saveEvent({ event, apiUrl });
},
onLocationChange: (pathname, context) => {
const params = new URLSearchParams(pathname.split("?")[1] ?? "");
Expand Down Expand Up @@ -107,4 +84,10 @@ export const init = async (options: FlowsCloudOptions): Promise<void> => {
});
};

// TODO: identifyUser should load flows according to the userHash
export const identifyUser = (options: IdentifyUserOptions): void => {
if (!_options) {
log.error("Cannot identify user before Flows are initialized.");
return;
}
void init({ ..._options, ...options });
};
13 changes: 6 additions & 7 deletions workspaces/js/src/flows-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class FlowsContext {
return this;
}

projectId = "";
projectId?: string;
userId?: string;
userProperties?: UserProperties;
flowsById?: Record<string, Flow>;
Expand All @@ -98,18 +98,17 @@ export class FlowsContext {
onIncompleteFlowStart?: (flowId: string, context: FlowsContext) => void;

updateFromOptions(options: FlowsInitOptions): void {
if (options.projectId) this.projectId = options.projectId;
this.projectId = options.projectId;
this.onNextStep = options.onNextStep;
this.onPrevStep = options.onPrevStep;
this.tracking = options.tracking;
this.debug = options._debug;
if (options.seenFlows) this.seenFlows = [...options.seenFlows];
this.seenFlows = options.seenFlows ? [...options.seenFlows] : [];
this.onSeenFlowsChange = options.onSeenFlowsChange;
this.onLocationChange = options.onLocationChange;
this.onIncompleteFlowStart = options.onIncompleteFlowStart;
this.rootElement = options.rootElement;
this.flowsById = {
...this.flowsById,
...options.flows?.reduce(
(acc, flow) => {
acc[flow.id] = flow;
Expand All @@ -118,13 +117,13 @@ export class FlowsContext {
{} as Record<string, Flow>,
),
};
this.updateUser(options);
this.updateFromOptions(options);
this.startInstancesFromLocalStorage();
}

updateUser = (options: IdentifyUserOptions): this => {
this.userId = options.userId ?? this.userId;
this.userProperties = options.userProperties ?? this.userProperties;
this.userId = options.userId;
this.userProperties = options.userProperties;
return this;
};

Expand Down

0 comments on commit 539c30f

Please sign in to comment.