Skip to content

Commit 69973b9

Browse files
committed
Commit progress on adding a google provider
1 parent 3dcc824 commit 69973b9

File tree

16 files changed

+928
-32
lines changed

16 files changed

+928
-32
lines changed

demo/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ node_modules
22
media
33

44
.env
5+
6+
ga_credentials.json

demo/src/payload.config.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,29 @@ import Tags from "./collections/Tags";
66
import Users from "./collections/Users";
77
import Media from "./collections/Media";
88
import Homepage from "./globals/Homepage";
9-
import payloadDashboardAnalytics from "../../src/index";
10-
import { PlausibleProvider } from "../../src/types/providers";
9+
import dashboardAnalytics from "../../src/index";
10+
import { PlausibleProvider, GoogleProvider } from "../../src/types/providers";
1111

1212
const PLAUSIBLE_API_KEY = process.env.PLAUSIBLE_API_KEY;
1313
const PLAUSIBLE_HOST = process.env.PLAUSIBLE_HOST;
1414
const PLAUSIBLE_SITE_ID = process.env.PLAUSIBLE_SITE_ID;
1515

16+
const GOOGLE_PROPERTY_ID = process.env.GOOGLE_PROPERTY_ID;
17+
const GOOGLE_CREDENTIALS_FILE = process.env.GOOGLE_CREDENTIALS_FILE;
18+
1619
const plausibleProvider: PlausibleProvider = {
1720
source: "plausible",
1821
apiSecret: PLAUSIBLE_API_KEY,
1922
siteId: PLAUSIBLE_SITE_ID,
2023
host: PLAUSIBLE_HOST,
2124
};
2225

26+
const googleProvider: GoogleProvider = {
27+
source: "google",
28+
credentials: GOOGLE_CREDENTIALS_FILE,
29+
propertyId: GOOGLE_PROPERTY_ID,
30+
};
31+
2332
export default buildConfig({
2433
serverURL: "http://localhost:3000",
2534
admin: {
@@ -51,8 +60,8 @@ export default buildConfig({
5160
schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"),
5261
},
5362
plugins: [
54-
payloadDashboardAnalytics({
55-
provider: plausibleProvider,
63+
dashboardAnalytics({
64+
provider: googleProvider,
5665
access: (user: any) => {
5766
return Boolean(user);
5867
},

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
"license": "MIT",
2525
"peerDependencies": {
2626
"payload": "^1.6.16",
27-
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
27+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
28+
"@google-analytics/data": "^3.2"
2829
},
2930
"devDependencies": {
31+
"@google-analytics/data": "^3.2.1",
3032
"@types/react-router-dom": "^5.3.3",
3133
"payload": "^1.6.26",
3234
"react": "^18",

src/extendWebpackConfig.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
11
import type { Config } from "payload/config";
22
import path from "path";
33
import type { Configuration as WebpackConfig } from "webpack";
4+
import type { Provider } from "./types";
45

56
const mockModulePath = path.resolve(__dirname, "mocks/serverModule.js");
67

8+
/* Some providers may need their own list of mocked modules to avoid react errors and bundling of node modules */
9+
const aliasMap: Record<Provider["source"], any> = {
10+
google: {
11+
[path.resolve(__dirname, "./providers/google/client")]: mockModulePath,
12+
[path.resolve(__dirname, "./providers/google/getLiveData")]: mockModulePath,
13+
"@google-analytics/data": mockModulePath,
14+
url: mockModulePath,
15+
querystring: mockModulePath,
16+
fs: mockModulePath,
17+
os: mockModulePath,
18+
stream: mockModulePath,
19+
child_process: mockModulePath,
20+
util: mockModulePath,
21+
net: mockModulePath,
22+
tls: mockModulePath,
23+
assert: mockModulePath,
24+
request: mockModulePath,
25+
},
26+
plausible: {
27+
[path.resolve(__dirname, "./providers/plausible/client")]: mockModulePath,
28+
},
29+
};
30+
731
export const extendWebpackConfig =
8-
(config: Config): ((webpackConfig: WebpackConfig) => WebpackConfig) =>
32+
(
33+
config: Config,
34+
provider: Provider["source"]
35+
): ((webpackConfig: WebpackConfig) => WebpackConfig) =>
936
(webpackConfig) => {
1037
const existingWebpackConfig =
1138
typeof config.admin?.webpack === "function"
@@ -21,9 +48,7 @@ export const extendWebpackConfig =
2148
? existingWebpackConfig.resolve.alias
2249
: {}),
2350
express: mockModulePath,
24-
[path.resolve(__dirname, "./providers/plausible/client")]:
25-
mockModulePath,
26-
/* [path.resolve(__dirname, "./routes/")]: mockModulePath, */
51+
...aliasMap[provider],
2752
},
2853
},
2954
};

src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
DashboardWidgetMap,
2121
} from "./utilities/widgetMaps";
2222

23-
const payloadDashboardAnalytics =
23+
const dashboardAnalytics =
2424
(incomingConfig: DashboardAnalyticsConfig) =>
2525
(config: PayloadConfig): PayloadConfig => {
2626
const { admin, collections, globals } = config;
@@ -67,7 +67,7 @@ const payloadDashboardAnalytics =
6767
],
6868
}),
6969
},
70-
webpack: extendWebpackConfig(config),
70+
webpack: extendWebpackConfig(config, provider.source),
7171
},
7272
endpoints: [
7373
...endpoints,
@@ -137,4 +137,4 @@ const payloadDashboardAnalytics =
137137
return processedConfig;
138138
};
139139

140-
export default payloadDashboardAnalytics;
140+
export default dashboardAnalytics;

src/providers/google/client.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { GoogleProvider } from "../../types/providers";
2+
import type { Metrics, Properties } from "../../types/widgets";
3+
import { BetaAnalyticsDataClient } from "@google-analytics/data";
4+
import { MetricMap, PropertyMap } from "./utilities";
5+
6+
type ClientOptions = {
7+
endpoint: string;
8+
timeframe?: string;
9+
metrics?: Metrics[];
10+
property?: Properties;
11+
};
12+
13+
function client(provider: GoogleProvider, options: ClientOptions) {
14+
const analyticsDataClient = new BetaAnalyticsDataClient({
15+
keyFilename: provider.credentials,
16+
});
17+
18+
return {
19+
run: analyticsDataClient,
20+
};
21+
}
22+
23+
export default client;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { GoogleProvider } from "../../types/providers";
2+
import type { LiveDataOptions } from "..";
3+
import type { LiveData } from "../../types/data";
4+
import client from "./client";
5+
6+
async function getLiveData(provider: GoogleProvider, options: LiveDataOptions) {
7+
const googleClient = client(provider, {
8+
endpoint: "/stats/realtime/visitors",
9+
});
10+
11+
const request = {
12+
property: `properties/${provider.propertyId}`,
13+
dateRanges: [
14+
{
15+
startDate: "2023-03-05",
16+
endDate: "today",
17+
},
18+
],
19+
/* dimensions: [{ name: "country" }], */
20+
metrics: [{ name: "activeUsers" }],
21+
};
22+
23+
const data = await googleClient.run
24+
.runRealtimeReport(request)
25+
.then((data) => data[0].rows?.[0].metricValues?.[0]?.value);
26+
27+
console.log("data rows", data);
28+
29+
const processedData: LiveData = {
30+
visitors: data ? parseInt(data) : 0,
31+
};
32+
33+
return processedData;
34+
}
35+
36+
export default getLiveData;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { GoogleProvider } from "../../types/providers";
2+
import type { PageAggregateOptions } from "..";
3+
import type { AggregateData } from "../../types/data";
4+
import { getMetrics } from "./utilities";
5+
import client from "./client";
6+
7+
async function getPageAggregateData(
8+
provider: GoogleProvider,
9+
options: PageAggregateOptions
10+
) {
11+
const googleClient = client(provider, {
12+
endpoint: "/stats/realtime/visitors",
13+
});
14+
15+
const { metrics } = options;
16+
17+
const usedMetrics = getMetrics(metrics);
18+
19+
console.log("usedMetrics", usedMetrics);
20+
21+
const request = {
22+
property: `properties/${provider.propertyId}`,
23+
dateRanges: [
24+
{
25+
startDate: "2023-03-05",
26+
endDate: "today",
27+
},
28+
],
29+
dimensions: [{ name: "pagePath" }],
30+
metrics: usedMetrics.map((metric) => {
31+
return {
32+
name: metric,
33+
};
34+
}),
35+
};
36+
37+
const data = await googleClient.run.runReport(request).then((data) => data);
38+
39+
console.log("data rows", data);
40+
41+
const processedData: AggregateData = {
42+
visitors: 0,
43+
};
44+
45+
return processedData;
46+
}
47+
48+
export default getPageAggregateData;

src/providers/google/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { GoogleProvider } from "../../types/providers";
2+
/* import getGlobalAggregateData from "./getGlobalAggregateData";
3+
import getGlobalChartData from "./getGlobalChartData";
4+
import getPageChartData from "./getPageChartData"; */
5+
import getPageAggregateData from "./getPageAggregateData";
6+
import getLiveData from "./getLiveData";
7+
/* import getReportData from "./getReportData"; */
8+
import type {
9+
ApiProvider,
10+
GlobalAggregateOptions,
11+
GlobalChartOptions,
12+
PageChartOptions,
13+
PageAggregateOptions,
14+
LiveDataOptions,
15+
ReportDataOptions,
16+
} from "..";
17+
18+
import { MetricMap } from "./utilities";
19+
20+
const google = (provider: GoogleProvider): ApiProvider => {
21+
return {
22+
getGlobalAggregateData: async (options: GlobalAggregateOptions) =>
23+
await new Promise(() => "test"),
24+
/* await getGlobalAggregateData(provider, options), */
25+
getGlobalChartData: async (options: GlobalChartOptions) =>
26+
await new Promise(() => "test"),
27+
/* await getGlobalChartData(provider, options), */
28+
getPageChartData: async (options: PageChartOptions) =>
29+
await new Promise(() => "test"),
30+
/* await getPageChartData(provider, options), */
31+
getPageAggregateData: async (options: PageAggregateOptions) =>
32+
await getPageAggregateData(provider, options),
33+
/* await getPageAggregateData(provider, options), */
34+
getLiveData: async (options: LiveDataOptions) =>
35+
await getLiveData(provider, options),
36+
getReportData: async (options: ReportDataOptions) =>
37+
await new Promise(() => "test"),
38+
/* await getReportData(provider, options), */
39+
metricsMap: MetricMap,
40+
};
41+
};
42+
43+
export default google;

src/providers/google/utilities.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { MetricsMap, PropertiesMap } from "../../types/data";
2+
3+
export const MetricMap: MetricsMap = {
4+
views: {
5+
label: "Views",
6+
value: "screenPageViews",
7+
},
8+
visitors: { label: "Visitors", value: "activeUsers" },
9+
bounceRate: { label: "Bounce rate", value: "bounceRate" },
10+
sessionDuration: { label: "Avg. duration", value: "averageSessionDuration" },
11+
sessions: { label: "Sessions", value: "sessions" },
12+
};
13+
14+
export const PropertyMap: PropertiesMap = {
15+
page: {
16+
label: "Page",
17+
value: "pagePath",
18+
},
19+
country: {
20+
label: "Country",
21+
value: "country",
22+
},
23+
/* entryPoint: {
24+
label: "Pages",
25+
value: "landingPagePlusQueryString",
26+
},
27+
exitPoint: {
28+
label: "Pages",
29+
value: "event:page",
30+
}, */
31+
source: {
32+
label: "Source",
33+
value: "source",
34+
},
35+
};
36+
37+
export const getMetrics = (metrics: Array<keyof MetricsMap>) => {
38+
const myMetrics: string[] = [];
39+
const availableMetrics = Object.entries(MetricMap);
40+
41+
metrics?.forEach((metric) => {
42+
const foundMetric = availableMetrics.find((mappedMetric) => {
43+
return mappedMetric[0] === metric;
44+
});
45+
46+
if (foundMetric) myMetrics.push(foundMetric[1].value);
47+
});
48+
49+
return myMetrics;
50+
};

0 commit comments

Comments
 (0)