WPE Usage Metrics

Display WP Engine usage metrics in Local

View on GitHub

WPE Usage Metrics Developer Guide

Architecture

src/
├── main/              # Main process (Node.js/Electron)
│   ├── index.ts       # Entry point, registers IPC handlers
│   └── ipc-handlers.ts # IPC handlers for WPE API calls
├── renderer/          # Renderer process (React)
│   ├── index.tsx      # Entry point, registers UI hooks
│   └── components/    # React components
│       ├── UsageMetricsPanel.tsx  # Main panel component
│       ├── MetricCard.tsx         # Individual metric display
│       ├── DateRangeSelector.tsx  # Date range dropdown
│       └── icons.tsx              # SVG icon components
└── common/            # Shared code
    ├── constants.ts   # IPC channels, API config
    ├── types.ts       # TypeScript interfaces
    ├── theme.ts       # Theme detection utilities
    └── formatters.ts  # Number/date formatters

Key Concepts

IPC Communication

The addon uses Electron IPC for main/renderer communication:

// Main process (ipc-handlers.ts)
ipcMain.handle('wpe-usage-metrics:get-metrics', async (event, data) => {
  // Fetch from WPE API
  return { success: true, data: metrics };
});

// Renderer process (UsageMetricsPanel.tsx)
const response = await electron.ipcRenderer.invoke('wpe-usage-metrics:get-metrics', {
  siteId: site.id,
  installId: wpeSiteId,
  dateRange: 30,
});

Authentication

The addon uses Basic Auth with WP Engine API credentials stored securely in the OS keychain:

// Get credentials from secure storage
const credentials = await secureStorage.getCredentials();
if (credentials) {
  const encoded = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
  return `Basic ${encoded}`;
}

Users configure their API credentials in Local Preferences → WP Engine Usage Metrics.

WP Engine API

The addon calls these WP Engine API endpoints:

Endpoint Purpose
GET /installs List installs to find install ID from site ID
GET /installs/{id}/usage Fetch usage metrics for an install

Important: The remoteSiteId from Local’s hostConnections is a Site ID, not an Install ID. We must look up the Install ID first.

Theme Support

Dark mode is detected via DOM observation:

export function isDarkMode(): boolean {
  return document.documentElement.classList.contains('Theme__Dark');
}

export function onThemeChange(callback: (isDark: boolean) => void): () => void {
  const observer = new MutationObserver(() => callback(isDarkMode()));
  observer.observe(document.documentElement, {
    attributes: true,
    attributeFilter: ['class']
  });
  return () => observer.disconnect();
}

React Components

Important: Use React class components, not hooks. Local’s React environment doesn’t support hooks properly.

// ✅ Correct
export class MyComponent extends React.Component<Props, State> {
  componentDidMount() { ... }
  render() { return <div>...</div>; }
}

// ❌ Wrong - hooks don't work
export function MyComponent() {
  const [state, setState] = useState();
  return <div>...</div>;
}

Adding New Features

Adding a New Metric

  1. Update types.ts with the metric type
  2. Add the metric to UsageMetricsPanel.tsx
  3. Add formatting in formatters.ts if needed
  4. Add an icon in icons.tsx

Adding a New IPC Handler

  1. Add the channel name to constants.ts:
    export const IPC_CHANNELS = {
      ...
      NEW_ACTION: `${ADDON_SLUG}:new-action`,
    };
    
  2. Add request/response types to types.ts

  3. Implement the handler in ipc-handlers.ts:
    ipcMain.handle(IPC_CHANNELS.NEW_ACTION, async (event, data) => {
      // Implementation
    });
    
  4. Call from renderer:
    await electron.ipcRenderer.invoke(IPC_CHANNELS.NEW_ACTION, data);
    

Testing

# Run all tests
npm test

# Run with coverage
npm run test:coverage

# Watch mode
npm run test:watch

Mocking Local APIs

Tests use mocks in tests/__mocks__/:

// tests/__mocks__/local-main.ts
export const getServiceContainer = jest.fn().mockReturnValue({
  cradle: {
    siteData: { getSite: jest.fn() },
  },
});

Debugging

Enable Console Logging

The addon logs to console with [WPE Usage] prefix:

console.log('[WPE Usage] Fetching metrics from:', url);

View logs in:

Common Issues

Issue Cause Solution
IPC handlers not called Wrong channel name Check IPC_CHANNELS constants match
404 from API Install ID vs Site ID Use findInstallBySiteId() lookup
UI not updating Missing state update Call setState() after async operations
Theme not detected DOM not ready Wait for componentDidMount()