Skip to content

Commit

Permalink
migrate analytics params from clientId/accountId to teamId/projectId (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jnsdls authored Feb 25, 2025
1 parent 1fa36c1 commit 5612542
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 230 deletions.
181 changes: 152 additions & 29 deletions apps/dashboard/src/@/api/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,72 @@
import { fetchAnalytics } from "data/analytics/fetch-analytics";
import "server-only";

import type {
AnalyticsQueryParams,
AnalyticsQueryParamsV2,
EcosystemWalletStats,
InAppWalletStats,
RpcMethodStats,
TransactionStats,
UserOpStats,
WalletStats,
WalletUserStats,
} from "types/analytics";
import { getAuthToken } from "../../app/api/lib/getAuthToken";
import { getChains } from "./chain";

function buildSearchParams(
params: AnalyticsQueryParams | AnalyticsQueryParamsV2,
): URLSearchParams {
const searchParams = new URLSearchParams();
async function fetchAnalytics(
input: string | URL,
init?: RequestInit,
): Promise<Response> {
const token = await getAuthToken();

// v1 params
if ("clientId" in params && params.clientId) {
searchParams.append("clientId", params.clientId);
if (!token) {
throw new Error("You are not authorized to perform this action");
}
if ("accountId" in params && params.accountId) {
searchParams.append("accountId", params.accountId);

const [pathname, searchParams] = input.toString().split("?");
if (!pathname) {
throw new Error("Invalid input, no pathname provided");
}

// v2 params
if ("teamId" in params && params.teamId) {
searchParams.append("teamId", params.teamId);
// create a new URL object for the analytics server
const ANALYTICS_SERVICE_URL = new URL(
process.env.ANALYTICS_SERVICE_URL || "https://analytics.thirdweb.com",
);
ANALYTICS_SERVICE_URL.pathname = pathname;
for (const param of searchParams?.split("&") || []) {
const [key, value] = param.split("=");
if (!key || !value) {
throw new Error("Invalid input, no key or value provided");
}
ANALYTICS_SERVICE_URL.searchParams.append(
decodeURIComponent(key),
decodeURIComponent(value),
);
}
if ("projectId" in params && params.projectId) {
// client id DEBUG OVERRIDE
// ANALYTICS_SERVICE_URL.searchParams.delete("clientId");
// ANALYTICS_SERVICE_URL.searchParams.delete("accountId");
// ANALYTICS_SERVICE_URL.searchParams.append(
// "clientId",
// "...",
// );

return fetch(ANALYTICS_SERVICE_URL, {
...init,
headers: {
"content-type": "application/json",
...init?.headers,
authorization: `Bearer ${token}`,
},
});
}

function buildSearchParams(params: AnalyticsQueryParams): URLSearchParams {
const searchParams = new URLSearchParams();

searchParams.append("teamId", params.teamId);

if (params.projectId) {
searchParams.append("projectId", params.projectId);
}

Expand All @@ -46,7 +84,7 @@ function buildSearchParams(
}

export async function getWalletConnections(
params: AnalyticsQueryParamsV2,
params: AnalyticsQueryParams,
): Promise<WalletStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
Expand All @@ -70,7 +108,7 @@ export async function getWalletConnections(
}

export async function getInAppWalletUsage(
params: AnalyticsQueryParamsV2,
params: AnalyticsQueryParams,
): Promise<InAppWalletStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
Expand All @@ -94,7 +132,7 @@ export async function getInAppWalletUsage(
}

export async function getUserOpUsage(
params: AnalyticsQueryParamsV2,
params: AnalyticsQueryParams,
): Promise<UserOpStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
Expand All @@ -118,7 +156,7 @@ export async function getUserOpUsage(
}

export async function getAggregateUserOpUsage(
params: Omit<AnalyticsQueryParamsV2, "period">,
params: Omit<AnalyticsQueryParams, "period">,
): Promise<UserOpStats> {
const [userOpStats, chains] = await Promise.all([
getUserOpUsage({ ...params, period: "all" }),
Expand Down Expand Up @@ -152,7 +190,7 @@ export async function getAggregateUserOpUsage(
}

export async function getClientTransactions(
params: AnalyticsQueryParamsV2,
params: AnalyticsQueryParams,
): Promise<TransactionStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
Expand Down Expand Up @@ -180,7 +218,7 @@ export async function getRpcMethodUsage(
): Promise<RpcMethodStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
`v1/rpc/evm-methods?${searchParams.toString()}`,
`v2/rpc/evm-methods?${searchParams.toString()}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
Expand All @@ -201,7 +239,7 @@ export async function getWalletUsers(
): Promise<WalletUserStats[]> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(
`v1/wallets/users?${searchParams.toString()}`,
`v2/wallet-connects/users?${searchParams.toString()}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
Expand All @@ -220,23 +258,108 @@ export async function getWalletUsers(
return json.data as WalletUserStats[];
}

type ActiveStatus = {
bundler: boolean;
storage: boolean;
rpc: boolean;
nebula: boolean;
sdk: boolean;
insight: boolean;
pay: boolean;
inAppWallet: boolean;
ecosystemWallet: boolean;
};

export async function isProjectActive(
params: AnalyticsQueryParams,
): Promise<boolean> {
): Promise<ActiveStatus> {
const searchParams = buildSearchParams(params);
const res = await fetchAnalytics(`v1/active?${searchParams.toString()}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
const res = await fetchAnalytics(
`v2/active-usage?${searchParams.toString()}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
},
);

if (res?.status !== 200) {
const reason = await res?.text();
console.error(
`Failed to fetch project active status: ${res?.status} - ${res.statusText} - ${reason}`,
);
return false;
return {
bundler: false,
storage: false,
rpc: false,
nebula: false,
sdk: false,
insight: false,
pay: false,
inAppWallet: false,
ecosystemWallet: false,
} as ActiveStatus;
}

const json = await res.json();
return json.data.isActive as boolean;
return json.data as ActiveStatus;
}

export async function getEcosystemWalletUsage(args: {
teamId: string;
ecosystemSlug: string;
ecosystemPartnerId?: string;
projectId?: string;
from?: Date;
to?: Date;
period?: "day" | "week" | "month" | "year" | "all";
}) {
const {
ecosystemSlug,
ecosystemPartnerId,
teamId,
projectId,
from,
to,
period,
} = args;

const searchParams = new URLSearchParams();
// required params
searchParams.append("ecosystemSlug", ecosystemSlug);
searchParams.append("teamId", teamId);

// optional params
if (ecosystemPartnerId) {
searchParams.append("ecosystemPartnerId", ecosystemPartnerId);
}
if (projectId) {
searchParams.append("projectId", projectId);
}
if (from) {
searchParams.append("from", from.toISOString());
}
if (to) {
searchParams.append("to", to.toISOString());
}
if (period) {
searchParams.append("period", period);
}
const res = await fetchAnalytics(
`v2/wallets/connects/${ecosystemSlug}?${searchParams.toString()}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
},
);

if (res?.status !== 200) {
console.error("Failed to fetch ecosystem wallet stats");
return null;
}

const json = await res.json();

return json.data as EcosystemWalletStats[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,25 @@ import {
getWalletConnections,
getWalletUsers,
} from "@/api/analytics";
import { redirect } from "next/navigation";

import type {
InAppWalletStats,
WalletStats,
WalletUserStats,
} from "types/analytics";

import {
type DurationId,
type Range,
getLastNDaysRange,
} from "components/analytics/date-range-selector";

import { redirect } from "next/navigation";
import { type WalletId, getWalletInfo } from "thirdweb/wallets";
import type {
InAppWalletStats,
WalletStats,
WalletUserStats,
} from "types/analytics";
import { AnalyticsHeader } from "../../../../components/Analytics/AnalyticsHeader";
import { CombinedBarChartCard } from "../../../../components/Analytics/CombinedBarChartCard";
import { EmptyState } from "../../../../components/Analytics/EmptyState";
import { PieChartCard } from "../../../../components/Analytics/PieChartCard";

import { getTeamBySlug } from "@/api/team";
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import type { Account } from "@3rdweb-sdk/react/hooks/useApi";
import { getValidAccount } from "app/account/settings/getAccount";
import { EmptyStateCard } from "app/team/components/Analytics/EmptyStateCard";
import { Suspense } from "react";
import { TotalSponsoredChartCardUI } from "../../_components/TotalSponsoredCard";
Expand All @@ -51,7 +46,6 @@ export default async function TeamOverviewPage(props: {
props.searchParams,
]);

const account = await getValidAccount(`/team/${params.team_slug}`);
const team = await getTeamBySlug(params.team_slug);

if (!team) {
Expand All @@ -75,7 +69,6 @@ export default async function TeamOverviewPage(props: {
<Suspense fallback={<GenericLoadingPage />}>
<OverviewPageContent
teamId={team.id}
account={account}
range={range}
interval={interval}
searchParams={searchParams}
Expand All @@ -88,12 +81,11 @@ export default async function TeamOverviewPage(props: {

async function OverviewPageContent(props: {
teamId: string;
account: Account;
range: Range;
interval: "day" | "week";
searchParams: SearchParams;
}) {
const { teamId, account, range, interval, searchParams } = props;
const { teamId, range, interval, searchParams } = props;

const [
walletConnections,
Expand All @@ -113,7 +105,7 @@ async function OverviewPageContent(props: {
}),
// Time series data for wallet users
getWalletUsers({
accountId: account.id,
teamId: teamId,
from: range.from,
to: range.to,
period: interval,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getEcosystemWalletUsage } from "@/api/analytics";
import {
type Range,
getLastNDaysRange,
} from "components/analytics/date-range-selector";
import { RangeSelector } from "components/analytics/range-selector";
import { getEcosystemWalletUsage } from "data/analytics/wallets/ecosystem";
import { EcosystemWalletUsersChartCard } from "./EcosystemWalletUsersChartCard";
import { EcosystemWalletsSummary } from "./Summary";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { LoadingChartState } from "components/analytics/empty-chart-state";
import { Suspense } from "react";
import type { AnalyticsQueryParamsV2 } from "types/analytics";
import type { AnalyticsQueryParams } from "types/analytics";
import { getClientTransactions } from "../../../../../../@/api/analytics";
import { TransactionsChartsUI } from "./TransactionCharts";

export function TransactionsCharts(
props: AnalyticsQueryParamsV2 & {
props: AnalyticsQueryParams & {
searchParams: { [key: string]: string | string[] | undefined };
},
) {
Expand All @@ -24,7 +24,7 @@ export function TransactionsCharts(
}

async function TransactionsChartCardAsync(
props: AnalyticsQueryParamsV2 & {
props: AnalyticsQueryParams & {
searchParams: { [key: string]: string | string[] | undefined };
},
) {
Expand Down
Loading

0 comments on commit 5612542

Please sign in to comment.