Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/utils/__tests__/provisioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ jest.mock('../debug', () => ({ logToFile: jest.fn() }));
jest.mock('../analytics', () => ({
analytics: { captureException: jest.fn() },
}));
jest.mock('../../lib/constants', () => ({
...jest.requireActual('../../lib/constants'),
IS_DEV: false,
WIZARD_USER_AGENT: 'posthog-wizard/test',
}));

const mockedAxios = axios as jest.Mocked<typeof axios>;

Expand Down Expand Up @@ -88,7 +93,9 @@ describe('provisionNewAccount', () => {
expect(
(accountCall[1] as Record<string, unknown>).code_challenge,
).toBeTruthy();
expect((accountCall[1] as Record<string, unknown>).client_id).toBeTruthy();
expect((accountCall[1] as Record<string, unknown>).client_id).toBe(
'https://us.posthog.com/api/oauth/wizard/client-metadata',
);

// Verify token exchange includes code_verifier
const tokenCall = mockedAxios.post.mock.calls[1];
Expand Down Expand Up @@ -158,7 +165,7 @@ describe('provisionNewAccount', () => {
);
});

it('sends correct region parameter', async () => {
it('routes EU requests to eu.posthog.com', async () => {
mockedAxios.post
.mockResolvedValueOnce({
data: { id: 'req_5', type: 'oauth', oauth: { code: 'code_5' } },
Expand Down Expand Up @@ -188,10 +195,17 @@ describe('provisionNewAccount', () => {
const result = await provisionNewAccount('eu@example.com', '', 'EU');

const accountCall = mockedAxios.post.mock.calls[0];
expect(accountCall[0]).toContain('https://eu.posthog.com');
expect((accountCall[1] as Record<string, unknown>).configuration).toEqual({
region: 'EU',
});
expect(result.host).toBe('https://eu.posthog.com');

const tokenCall = mockedAxios.post.mock.calls[1];
expect(tokenCall[0]).toContain('https://eu.posthog.com');

const resourceCall = mockedAxios.post.mock.calls[2];
expect(resourceCall[0]).toContain('https://eu.posthog.com');
});

it('sends project name in resources configuration', async () => {
Expand Down
30 changes: 17 additions & 13 deletions src/utils/provisioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@
import * as crypto from 'node:crypto';
import axios from 'axios';
import { z } from 'zod';
import {
IS_DEV,
POSTHOG_DEV_CLIENT_ID,
POSTHOG_US_CLIENT_ID,
WIZARD_USER_AGENT,
} from '../lib/constants';
import { IS_DEV, WIZARD_USER_AGENT } from '../lib/constants';
import { logToFile } from './debug';
import { analytics } from './analytics';

const WIZARD_CLIENT_ID = IS_DEV ? POSTHOG_DEV_CLIENT_ID : POSTHOG_US_CLIENT_ID;
const WIZARD_CLIENT_ID =
'https://us.posthog.com/api/oauth/wizard/client-metadata';
const API_VERSION = '0.1d';

const PROVISIONING_BASE_URL = IS_DEV
? 'http://localhost:8010'
: 'https://us.posthog.com';
const REGION_URLS: Record<string, string> = {
US: 'https://us.posthog.com',
EU: 'https://eu.posthog.com',
};

function getBaseUrl(region: 'US' | 'EU'): string {
if (IS_DEV) return 'http://localhost:8010';
return REGION_URLS[region];
}

function generateCodeVerifier(): string {
return crypto.randomBytes(32).toString('base64url');
Expand Down Expand Up @@ -105,11 +107,13 @@ export async function provisionNewAccount(
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);

const baseUrl = getBaseUrl(region);

logToFile('[provisioning] starting account creation');

// Step 1: Create account
const accountRes = await axios.post(
`${PROVISIONING_BASE_URL}/api/agentic/provisioning/account_requests`,
`${baseUrl}/api/agentic/provisioning/account_requests`,
{
id: crypto.randomUUID(),
email,
Expand Down Expand Up @@ -158,7 +162,7 @@ export async function provisionNewAccount(

// Step 2: Exchange code for tokens
const tokenRes = await axios.post(
`${PROVISIONING_BASE_URL}/api/agentic/oauth/token`,
`${baseUrl}/api/agentic/oauth/token`,
new URLSearchParams({
grant_type: 'authorization_code',
code,
Expand All @@ -180,7 +184,7 @@ export async function provisionNewAccount(

// Step 3: Provision resources
const resourceRes = await axios.post(
`${PROVISIONING_BASE_URL}/api/agentic/provisioning/resources`,
`${baseUrl}/api/agentic/provisioning/resources`,
{
service_id: 'analytics',
...(opts?.projectName
Expand Down
Loading