UNPKG

ttf-api

Version:

The TrueToForm API SDK

364 lines (340 loc) 11 kB
import { createClient, sessionIdKey, userIdKey } from "./client"; import { analyticsEventsWrapper } from "./analytics"; /** * Creates an SDK instance with the provided API key. */ const sdk = (apiKey) => { const client = createClient(apiKey); const isStale = async () => { try { const eTag = localStorage.getItem("ttf-sdk-eTag"); const response = await client.get("stale-check", null, eTag); const { currentETag } = response; localStorage.setItem("ttf-sdk-eTag", currentETag); return eTag !== currentETag; } catch (error) { console.error("Error checking if SDK is stale:", error); return true; } }; const getAnalyticsEvents = analyticsEventsWrapper(client); const createRequestEvent = (props) => { const createCustomEvent = getAnalyticsEvents({}).addCustomEvent; return createCustomEvent("server_request", props); }; const initAnalytics = async () => { return client.post("analytics/init").then((response) => { const { userId, sessionId } = response; localStorage.setItem(userIdKey, userId); localStorage.setItem(sessionIdKey, sessionId); return response; }); }; const getGarment = (id) => { const startTime = Date.now(); if (!id) { throw new Error("garment ID is required"); } return client.get(`garments/${id}`).finally(() => { createRequestEvent({ name: "get_garment", startTime, garmentId: id }); }); }; const getSurvey = (id) => { const startTime = Date.now(); if (!id) { throw new Error("survey ID is required"); } return client.get(`surveys/${id}`).finally(() => { createRequestEvent({ name: "get_survey", startTime, surveyId: id }); }); }; const createSurvey = (surveyData) => { const startTime = Date.now(); if (!surveyData) { throw new Error("survey data is required"); } // TODO: Validate survey data on SDK side return client.post("surveys", surveyData).finally(() => { createRequestEvent({ name: "create_survey", startTime }); }); }; const getMeasurements = (id) => { const startTime = Date.now(); if (!id) { throw new Error("measurement ID is required"); } return client.get(`measurements/${id}`).finally(() => { createRequestEvent({ name: "get_measurements", startTime, measurementId: id, }); }); }; const createMeasurements = (measurementData) => { const startTime = Date.now(); if (!measurementData) { throw new Error("measurement data is required"); } return client.post("measurements", measurementData).finally(() => { createRequestEvent({ name: "create_measurements", startTime }); }); }; const getScan = (id) => { const startTime = Date.now(); if (!id) { throw new Error("scan ID is required"); } return client.get(`scans/${id}`).finally(() => { createRequestEvent({ name: "get_scan", startTime, scanId: id }); }); }; const createScan = (sessionId) => { const startTime = Date.now(); if (!sessionId) { throw new Error("session ID is required"); } return client.post("scans", { sessionId }).finally(() => { createRequestEvent({ name: "create_scan", startTime }); }); }; const getPrediction = (id) => { const startTime = Date.now(); if (!id) { throw new Error("prediction ID is required"); } return client.get(`predictions/${id}`).finally(() => { createRequestEvent({ name: "get_prediction", startTime, predictionId: id, }); }); }; const createSession = () => { const startTime = Date.now(); return client.post("sessions").finally(() => { createRequestEvent({ name: "create_session", startTime }); }); }; const getSessionStatus = (sessionId) => { const startTime = Date.now(); if (!sessionId) { throw new Error("session ID is required"); } return client.get(`sessions/${sessionId}/status`).finally(() => { createRequestEvent({ name: "get_session_status", startTime }); }); }; const getRealtimeSession = (sessionId, { onChange, onSuccess, onError }) => { let unsubscribe = () => {}; async function handleRealtimeSession() { try { const startTime = Date.now(); const controller = new AbortController(); const signal = controller.signal; unsubscribe = () => controller.abort(); const resBody = await client .get(`sessions/${sessionId}`, signal) .finally(() => { createRequestEvent({ name: "get_realtime_session", startTime, sessionId, }); }); const reader = resBody?.getReader(); const decoder = new TextDecoder(); let { done, value } = await reader.read(); while (!done) { const chunk = decoder.decode(value, { stream: true }); try { const data = chunk ?.split("\n") .filter((str) => str) .map((line) => { return JSON.parse(line); }); console.log("SSE data:", data); // loop through the data array for (let i = 0; i < data.length; i++) { const item = data[i]; console.log("SSE item:", item); switch (item.type) { case "SESSION_DATA": if (onChange && typeof onChange === "function") { try { onChange(item.payload?.progress); } catch (error) { console.error("Error calling onChange:", error); } } if (onSuccess && typeof onSuccess === "function") { const isReady = item.payload?.progress === "READY"; if (isReady) { try { onSuccess(item.payload); } catch (error) { console.error("Error calling onSuccess:", error); } } } if (onError && typeof onError === "function") { const isFailed = item.payload?.progress === "FAILED"; if (isFailed) { try { onError(item.payload); } catch (error) { console.error("Error calling onError:", error); } } } break; case "ERROR": console.error("SSE error:", data); if (onError && typeof onError === "function") { try { onError(item); } catch (error) { console.error("Error calling onError:", error); } } break; default: break; } } } catch (parseError) { console.error("Error parsing SSE data:", parseError); if (onError && typeof onError === "function") { try { onError({ type: "ERROR", message: "Parsing error" }); } catch (error) { console.error("Error calling onError:", error); } } } // Continue reading the stream ({ done, value } = await reader.read()); } } catch (error) { console.error("Stream error:", error); if (onError && typeof onError === "function") { // call onError only if the error is not an abort error if (error.name !== "AbortError") { onError({ type: "ERROR", message: error.message }); } } } } handleRealtimeSession(); return unsubscribe; }; const createScanPrediction = async (garmentId, sessionId) => { const startTime = Date.now(); if (!garmentId) { throw new Error("garment ID is required"); } if (!sessionId) { throw new Error("session ID is required"); } const scan = await createScan(sessionId); if (!scan || !scan.id) { throw new Error("Failed to create scan"); } return client .post("predictions", { garmentId, creatorId: scan.id }) .finally(() => { createRequestEvent({ name: "create_scan_prediction", startTime, garmentId, sessionId, }); }); }; const createSurveyPrediction = async (garmentId, surveyData) => { const startTime = Date.now(); if (!garmentId) { throw new Error("garment ID is required"); } if (!surveyData) { throw new Error("survey data is required"); } const survey = await createSurvey(surveyData); if (!survey || !survey.id) { throw new Error("Failed to create survey"); } return client .post("predictions", { garmentId, creatorId: survey.id }) .finally(() => { createRequestEvent({ name: "create_survey_prediction", startTime, garmentId, surveyId: survey.id, }); }); }; const createMeasurementsPrediction = async (garmentId, measurementsData) => { const startTime = Date.now(); if (!garmentId) { throw new Error("garment ID is required"); } if (!measurementsData) { throw new Error("measurements data is required"); } const measurements = await createMeasurements(measurementsData); if (!measurements || !measurements.id) { throw new Error("Failed to create measurements"); } return client .post("predictions", { garmentId, creatorId: measurements.id }) .finally(() => { createRequestEvent({ name: "create_measurements_prediction", startTime, garmentId, measurementsId: measurements.id, }); }); }; const createPredictionFromId = (garmentId, creatorId) => { const startTime = Date.now(); if (!garmentId) { throw new Error("garment ID is required"); } if (!creatorId) { throw new Error("creator ID is required"); } return client.post("predictions", { garmentId, creatorId }).finally(() => { createRequestEvent({ name: "create_background_prediction", startTime, garmentId, creatorId, }); }); }; return { isStale, initAnalytics, getGarment, createSurvey, getSurvey, createMeasurements, getMeasurements, createScan, getScan, getPrediction, createSession, createScanPrediction, createSurveyPrediction, createMeasurementsPrediction, createPredictionFromId, getRealtimeSession, getSessionStatus, getAnalyticsEvents, }; }; export default sdk;