#!/usr/bin/env node
import dotenv from 'dotenv';
import { UmbrellaAuth } from './auth.js';
import { UmbrellaApiClient } from './api-client.js';
import { getTestCredentialSets } from './auth-helper.js';
dotenv.config();
// Specific accounts discovered from the account discovery test
const DISCOVERED_ACCOUNTS = {
MSP: [
'mspprodtest_tenant_multicloud',
'mspprodtest_tenant_azure',
'AZURE--e39985',
'AzureS-da18a0',
'DDCT-G-faed5b',
'Dedica-a7a822',
'mspprodtest_tenant_gcp',
'GCP-SH-b35635'
],
DIRECT: [
'umbrellatestnet_tenant_multicloud',
'umbrellatestnet_tenant_aws',
'322609219825', // This looks like a real AWS account ID
'umbrellatestnet_tenant_azure',
'umbrellatestnet_tenant_gcp',
'Manual-b7a983',
'Umbrel-2e4e6f'
]
};
// Key endpoints that should work with proper account IDs
const KEY_ENDPOINTS = [
{
path: '/invoices/caui',
method: 'GET',
description: 'Cost and Usage Interface - Main cost analysis endpoint',
requiresAccount: true,
testParams: {
startDate: '2024-01-01',
endDate: '2024-01-31',
costType: ['unblended'],
groupBy: ['service'],
periodGranLevel: 'daily'
}
},
{
path: '/invoices/cost-and-usage',
method: 'GET',
description: 'Public cost and usage API',
requiresAccount: false,
testParams: {
startDate: '2024-01-01',
endDate: '2024-01-31'
}
},
{
path: '/budgets',
method: 'GET',
description: 'Budget information and alerts',
requiresAccount: true,
testParams: {}
},
{
path: '/usage/ec2/instances',
method: 'GET',
description: 'EC2 instance usage and costs',
requiresAccount: true,
testParams: {
startDate: '2024-01-01',
endDate: '2024-01-31'
}
}
];
class TargetedAccountTester {
private auth: UmbrellaAuth;
private apiClient: UmbrellaApiClient;
private baseURL: string;
constructor(baseURL: string) {
this.baseURL = baseURL;
this.auth = new UmbrellaAuth(baseURL);
this.apiClient = new UmbrellaApiClient(baseURL);
}
async authenticate(credentials: { username: string; password: string }): Promise<boolean> {
try {
await this.auth.authenticate(credentials);
const authHeaders = this.auth.getAuthHeaders();
this.apiClient.setAuthToken(authHeaders);
return true;
} catch (error: any) {
console.error(`β Authentication failed: ${error.message}`);
return false;
}
}
async testEndpointWithSpecificAccount(endpoint: any, accountId: string): Promise<any> {
const testParams = { ...endpoint.testParams };
if (endpoint.requiresAccount) {
testParams.accountId = accountId;
}
console.log(` π Testing ${endpoint.path} with account: ${accountId}`);
console.log(` π§ Params: ${Object.keys(testParams).join(', ')}`);
try {
const startTime = Date.now();
const response = await this.apiClient.makeRequest(endpoint.path, testParams);
const responseTime = Date.now() - startTime;
if (response.success) {
let dataInfo = 'Success';
if (response.data) {
if (Array.isArray(response.data)) {
dataInfo = `${response.data.length} array items`;
} else if (typeof response.data === 'object') {
dataInfo = `object with ${Object.keys(response.data).length} keys`;
}
}
console.log(` β
SUCCESS: ${dataInfo} (${responseTime}ms)`);
return {
success: true,
data: response.data,
responseTime,
accountId,
endpoint: endpoint.path
};
} else {
console.log(` β FAILED: ${response.error} (${responseTime}ms)`);
return {
success: false,
error: response.error,
responseTime,
accountId,
endpoint: endpoint.path
};
}
} catch (error: any) {
console.log(` π₯ ERROR: ${error.message}`);
return {
success: false,
error: error.message,
accountId,
endpoint: endpoint.path
};
}
}
async runTargetedAccountTest(
credentials: { username: string; password: string },
accountType: 'MSP' | 'DIRECT'
): Promise<void> {
console.log(`\n${'='.repeat(80)}`);
console.log(`π― TARGETED ACCOUNT TESTING: ${credentials.username} (${accountType})`);
console.log(`${'='.repeat(80)}`);
// Authenticate
const authSuccess = await this.authenticate(credentials);
if (!authSuccess) {
return;
}
console.log(`β
Authentication successful`);
const accounts = DISCOVERED_ACCOUNTS[accountType];
console.log(`\nπ Testing ${KEY_ENDPOINTS.length} key endpoints with ${accounts.length} discovered accounts`);
const results: any[] = [];
// Test each endpoint with each account
for (const endpoint of KEY_ENDPOINTS) {
console.log(`\nπ Testing endpoint: ${endpoint.path}`);
console.log(` π ${endpoint.description}`);
const endpointResults = [];
for (const accountId of accounts) {
const result = await this.testEndpointWithSpecificAccount(endpoint, accountId);
results.push(result);
endpointResults.push(result);
// Brief pause between requests
await new Promise(resolve => setTimeout(resolve, 300));
}
// Summary for this endpoint
const successful = endpointResults.filter(r => r.success).length;
const total = endpointResults.length;
console.log(` π Endpoint Summary: ${successful}/${total} accounts worked (${((successful/total)*100).toFixed(1)}%)`);
if (successful > 0) {
const workingAccounts = endpointResults.filter(r => r.success).map(r => r.accountId);
console.log(` β
Working accounts: ${workingAccounts.join(', ')}`);
}
}
// Final summary
console.log(`\n${'='.repeat(80)}`);
console.log(`π TARGETED TEST SUMMARY FOR ${accountType}`);
console.log(`${'='.repeat(80)}`);
const totalSuccessful = results.filter(r => r.success).length;
const totalTests = results.length;
const overallSuccessRate = (totalSuccessful / totalTests) * 100;
console.log(`\nπ― OVERALL RESULTS:`);
console.log(` β
Successful tests: ${totalSuccessful}/${totalTests} (${overallSuccessRate.toFixed(1)}%)`);
console.log(` π Endpoints tested: ${KEY_ENDPOINTS.length}`);
console.log(` π’ Accounts tested: ${accounts.length}`);
// Success by endpoint
console.log(`\nπ SUCCESS BY ENDPOINT:`);
KEY_ENDPOINTS.forEach(endpoint => {
const endpointResults = results.filter(r => r.endpoint === endpoint.path);
const successful = endpointResults.filter(r => r.success).length;
const rate = (successful / endpointResults.length) * 100;
const status = rate > 0 ? 'β
' : 'β';
console.log(` ${status} ${endpoint.path}: ${successful}/${endpointResults.length} (${rate.toFixed(1)}%)`);
});
// Success by account
console.log(`\nπ’ SUCCESS BY ACCOUNT:`);
accounts.forEach(accountId => {
const accountResults = results.filter(r => r.accountId === accountId);
const successful = accountResults.filter(r => r.success).length;
const rate = (successful / accountResults.length) * 100;
const status = rate > 0 ? 'β
' : 'β';
console.log(` ${status} ${accountId}: ${successful}/${accountResults.length} (${rate.toFixed(1)}%)`);
});
// Show working combinations
const workingCombinations = results.filter(r => r.success);
if (workingCombinations.length > 0) {
console.log(`\nπ WORKING ENDPOINT + ACCOUNT COMBINATIONS:`);
workingCombinations.forEach(result => {
console.log(` β
${result.endpoint} + ${result.accountId}`);
});
} else {
console.log(`\nβ οΈ NO WORKING COMBINATIONS FOUND`);
console.log(` This suggests the account IDs may need different formatting or additional parameters`);
}
}
}
async function main() {
console.log('π― TARGETED ACCOUNT TESTING FOR UMBRELLA MCP');
console.log('=============================================');
console.log(`π
Test Time: ${new Date().toISOString()}`);
console.log('\nπ― Strategy: Test key endpoints with all discovered account IDs');
const baseURL = process.env.UMBRELLA_API_BASE_URL || 'https://api.umbrellacost.io/api/v1';
const credentialSets = getTestCredentialSets();
if (!credentialSets) {
console.error('β No test credentials available');
process.exit(1);
}
const testConfigs = [
{ creds: credentialSets[0], type: 'MSP' as const, name: 'MSP Account' },
{ creds: credentialSets[1], type: 'DIRECT' as const, name: 'Direct Account' }
];
for (const config of testConfigs) {
try {
const tester = new TargetedAccountTester(baseURL);
await tester.runTargetedAccountTest(config.creds, config.type);
} catch (error: any) {
console.error(`β Test failed for ${config.name}: ${error.message}`);
}
// Pause between different credential types
await new Promise(resolve => setTimeout(resolve, 2000));
}
console.log(`\nπ TARGETED ACCOUNT TESTING COMPLETED!`);
console.log('\nπ‘ Next steps:');
console.log(' 1. If no combinations work, account IDs may need different formatting');
console.log(' 2. Try adding organizationId parameter alongside accountId');
console.log(' 3. Some endpoints may require specific date ranges or other context');
console.log(' 4. Consider testing with more recent date ranges (last 30 days)');
}
// Run main function if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error);
}