Skip to content

Commit

Permalink
feat(sdk-analytics) fix comments
Browse files Browse the repository at this point in the history
  • Loading branch information
oidacra committed Dec 12, 2024
1 parent 21796a0 commit 19e9927
Show file tree
Hide file tree
Showing 21 changed files with 126 additions and 143 deletions.
6 changes: 6 additions & 0 deletions core-web/libs/sdk/analytics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ Or include the script in your HTML page:

### Provider Setup

First, import the provider:

```tsx
import { DotContentAnalyticsProvider } from '@dotcms/analytics';
```

Wrap your application with the `DotContentAnalyticsProvider`:

```tsx
Expand Down
3 changes: 1 addition & 2 deletions core-web/libs/sdk/analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
"homepage": "https://github.com/dotCMS/core/tree/main/core-web/libs/sdk/analytics/README.md",
"dependencies": {
"analytics": "^0.8.14",
"react": "^18.2.0",
"@dotcms/react": "0.0.1-alpha.38"
"react": "^18.2.0"
},
"peerDependencies": {
"vite": "^5.0.0"
Expand Down
8 changes: 1 addition & 7 deletions core-web/libs/sdk/analytics/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
// HOC for wrapping a component with content analytics capabilities
export * from './lib/react/components/withContentAnalytics';

// Hook for tracking analytics events
export * from './lib/react/hook/useAnalyticTracker';

// Provider for the DotContentAnalytics instance
export * from './lib/react/components/DotContentAnalyticsProvider';
export * from './lib/react/hook/useContentAnalytics';
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,29 @@ export class DotContentAnalytics {

private constructor(config: DotContentAnalyticsConfig) {
this.#config = config;
this.logger = new DotLogger(this.#config.debug, 'DotContentAnalytics');
this.logger = new DotLogger(config.debug, 'DotContentAnalytics');

if (!config.apiKey) {
this.#initialized = false;
}
}

/**
* Returns the singleton instance of DotContentAnalytics
*/
static getInstance(config: DotContentAnalyticsConfig): DotContentAnalytics {
if (!config.apiKey) {
console.error(
`DotContentAnalytics: Missing "apiKey" in configuration - Events will not be sent to Content Analytics`
);
}

if (!config.server) {
console.error(
`DotContentAnalytics: Missing "server" in configuration - Events will not be sent to Content Analytics`
);
}

if (!DotContentAnalytics.instance) {
DotContentAnalytics.instance = new DotContentAnalytics(config);
}
Expand All @@ -52,10 +68,12 @@ export class DotContentAnalytics {
this.logger.group('Initialization');
this.logger.time('Init');

const plugins = this.#getPlugins();

this.#analytics = Analytics({
app: 'dotAnalytics',
debug: this.#config.debug,
plugins: [dotAnalyticsEnricherPlugin, dotAnalytics(this.#config)]
plugins
});

this.#initialized = true;
Expand All @@ -70,6 +88,19 @@ export class DotContentAnalytics {
}
}

/**
* Returns the plugins to be used in the analytics instance
*/
#getPlugins() {
const hasRequiredConfig = this.#config.apiKey && this.#config.server;

if (!hasRequiredConfig) {
return [];
}

return [dotAnalyticsEnricherPlugin, dotAnalytics(this.#config)];
}

/**
* Sends a page view event to the analytics instance.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sendAnalyticsEventToServer } from '../shared/dot-content-analytics.http';
import {
DotAnalyticsPayload,
DotAnalyticsParams,
DotContentAnalyticsConfig
} from '../shared/dot-content-analytics.model';

Expand All @@ -23,10 +23,7 @@ export const dotAnalytics = (config: DotContentAnalyticsConfig) => {
/**
* Initialize the plugin
*/
initialize: (params: {
config: DotContentAnalyticsConfig;
payload: DotAnalyticsPayload;
}) => {
initialize: (params: DotAnalyticsParams) => {
const { config, payload } = params;
if (config.debug) {
console.warn('DotAnalytics: Initialized with config', config);
Expand All @@ -50,7 +47,7 @@ export const dotAnalytics = (config: DotContentAnalyticsConfig) => {
/**
* Track a page view event
*/
page: (params: { config: DotContentAnalyticsConfig; payload: DotAnalyticsPayload }) => {
page: (params: DotAnalyticsParams) => {
const { config, payload } = params;

if (!isInitialized) {
Expand All @@ -68,7 +65,7 @@ export const dotAnalytics = (config: DotContentAnalyticsConfig) => {
/**
* Track a custom event
*/
track: (params: { config: DotContentAnalyticsConfig; payload: DotAnalyticsPayload }) => {
track: (params: DotAnalyticsParams) => {
const { config, payload } = params;

if (!isInitialized) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ export const sendAnalyticsEventToServer = async (
}

try {
return await fetch(`${options.server}${ANALYTICS_ENDPOINT}`, {
const response = await fetch(`${options.server}${ANALYTICS_ENDPOINT}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(serverEvent)
});

if (response.ok) {
return response;
} else {
throw new Error(`${response.status}`);
}
} catch (error) {
console.error('DotAnalytics: Error sending event:', error);
throw error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ export interface ServerEvent extends Record<string, unknown> {
/**
* Interface for the AnalyticsTracker.
*/
export interface AnalyticsTracker {
export interface DotContentAnalyticsCustomHook {
track: (eventName: string, payload?: Record<string, unknown>) => void;
}

/**
* Params for the DotAnalytics plugin
*/
export interface DotAnalyticsParams {
config: DotContentAnalyticsConfig;
payload: DotAnalyticsPayload;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Logger for the dotCMS SDK
*/
export class DotLogger {
// TODO: Create a logger that can be used in the SDK
private readonly isDebug: boolean;
private readonly packageName: string;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ReactElement, ReactNode, useEffect, useState } from 'react';
import { ReactElement, ReactNode, useEffect, useMemo } from 'react';

import { DotContentAnalytics } from '../../dot-content-analytics';
import { DotContentAnalyticsConfig } from '../../shared/dot-content-analytics.model';
import { DotContentAnalytics } from '../../dotAnalytics/dot-content-analytics';
import { DotContentAnalyticsConfig } from '../../dotAnalytics/shared/dot-content-analytics.model';
import DotContentAnalyticsContext from '../contexts/DotContentAnalyticsContext';
import { useContentAnalytics } from '../hook/useContentAnalytics';
import { useRouteTracker } from '../hook/useRouterTracker';

interface DotContentAnalyticsProviderProps {
children?: ReactNode;
Expand All @@ -25,17 +25,15 @@ export const DotContentAnalyticsProvider = ({
children,
config
}: DotContentAnalyticsProviderProps): ReactElement => {
const [instance, setInstance] = useState<DotContentAnalytics | null>(null);

useContentAnalytics(instance);
const instance = useMemo(() => DotContentAnalytics.getInstance(config), [config]);

useEffect(() => {
const dotContentAnalyticsInstance = DotContentAnalytics.getInstance(config);

dotContentAnalyticsInstance.ready().then(() => {
setInstance(dotContentAnalyticsInstance);
instance.ready().catch((err) => {
console.error('Error initializing analytics:', err);
});
}, []);
}, [instance]);

useRouteTracker(instance);

return (
<DotContentAnalyticsContext.Provider value={instance}>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { jest } from '@jest/globals';
import React, { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react-hooks';
import { ReactNode, useContext } from 'react';

import DotContentAnalyticsContext from './DotContentAnalyticsContext';

import { DotContentAnalytics } from '../../dot-content-analytics';
import { DotContentAnalytics } from '../../dotAnalytics/dot-content-analytics';

jest.mock('../dot-content-analytics', () => {
return jest.fn().mockImplementation(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext } from 'react';

import { DotContentAnalytics } from '../../dot-content-analytics';
import { DotContentAnalytics } from '../../dotAnalytics/dot-content-analytics';

/**
* `DotContentAnalyticsContext` is a React context that is designed to provide an instance of
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import { useEffect } from 'react';
import { useContext } from 'react';

import { DotContentAnalytics } from '../../dot-content-analytics';
import { isInsideEditor } from '../../shared/dot-content-analytics.utils';
import { DotContentAnalyticsCustomHook } from '../../dotAnalytics/shared/dot-content-analytics.model';
import { isInsideEditor } from '../../dotAnalytics/shared/dot-content-analytics.utils';
import DotContentAnalyticsContext from '../contexts/DotContentAnalyticsContext';

/**
* Custom hook that handles analytics page view tracking.
*
* @param {DotContentAnalytics | null} instance - The analytics instance used to track page views
* @returns {void}
* @returns {DotContentAnalyticsCustomHook} - The analytics instance used to track page views
*
*/
export const useContentAnalytics = (instance: DotContentAnalytics | null): void => {
/**
* Tracks page view when component mounts, but only if:
* - We have a valid analytics instance
* - We're in a browser environment
* - We're not inside the editor
*/
useEffect(() => {
if (!instance || typeof window === 'undefined') {
return;
}

const insideEditor = isInsideEditor();
export const useContentAnalytics = (): DotContentAnalyticsCustomHook => {
const instance = useContext(DotContentAnalyticsContext);
const insideEditor = isInsideEditor();

if (!insideEditor) {
instance.pageView({ source: 'headless' });
return {
/**
* Track an event with the analytics instance.
*
* @param {string} eventName - The name of the event to track
* @param {object} payload - Additional data to include with the event
*/
track: (eventName: string, payload: Record<string, unknown> = {}) => {
if (instance?.track && !insideEditor) {
instance.track(eventName, {
...payload,
timestamp: new Date().toISOString()
});
}
}
}, [instance]);
};
};
Loading

0 comments on commit 19e9927

Please sign in to comment.