All files / src/services/api partnerApi.ts

90.9% Statements 30/33
100% Branches 12/12
100% Functions 6/6
90.9% Lines 30/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185                                                      12x                                                                             12x         12x             12x   12x 2x     12x 2x 10x 1x     12x 4x       12x   12x                     12x 2x                             12x       8x 8x 3x   8x                           12x 4x 1x                   12x       3x       1x                   12x     3x     2x    
/**
 * Partner API Client (GREEN Phase - Story 2.8.1, Task 1b)
 *
 * Provides API integration for Partner Coordination Service
 * - List partners with filtering, sorting, pagination
 * - HTTP enrichment via ?include=company,contacts (ADR-004)
 * - Statistics endpoint for partner overview metrics
 *
 * CRITICAL: Path is '/partners' WITHOUT /api/v1 prefix (baseURL already includes it)
 */
 
import apiClient from '@/services/api/apiClient';
import type { components } from '@/types/generated/partner-api.types';
 
// Type aliases for cleaner code
export type PartnerResponse = components['schemas']['PartnerResponse'];
export type PartnerListResponse = components['schemas']['PartnerListResponse'];
export type PartnershipLevel = components['schemas']['PartnershipLevel'];
export type CreatePartnerRequest = components['schemas']['CreatePartnerRequest'];
export type UpdatePartnerRequest = components['schemas']['UpdatePartnerRequest'];
export type PartnerContactResponse = components['schemas']['PartnerContactResponse'];
export type PartnerStatistics = components['schemas']['PartnerStatistics'];
 
// API base path for partner endpoints
// CRITICAL: Use path WITHOUT /api/v1 prefix (already in baseURL)
// Note: apiClient baseURL is set from runtime config to 'http://localhost:8080/api/v1'
// so we only need '/partners' (the /v1 prefix is already in the baseURL)
const PARTNER_API_PATH = '/partners';
 
// ============================================================================
// Supporting types for Story 2.8.1 (Partner Directory)
// ============================================================================
 
/**
 * Filter parameters for partner list
 */
export interface PartnerFilters {
  tier: PartnershipLevel | 'all';
  status: 'all' | 'active' | 'inactive';
}
 
/**
 * Pagination parameters
 */
export interface PartnerPagination {
  page: number; // Zero-based indexing
  size: number;
}
 
/**
 * Sort parameters
 */
export interface PartnerSort {
  sortBy: 'engagement' | 'name' | 'tier' | 'lastEvent';
  sortOrder: 'asc' | 'desc';
}
 
/**
 * List partners with filters, sorting, and pagination
 * Includes HTTP enrichment via ?include=company,contacts (ADR-004)
 *
 * @param filters - Tier and status filters
 * @param sort - Sort configuration
 * @param pagination - Page and size parameters
 * @returns Promise<PartnerListResponse>
 */
export const listPartners = async (
  filters: PartnerFilters,
  sort: PartnerSort,
  pagination: PartnerPagination
): Promise<PartnerListResponse> => {
  const params: Record<string, string | number> = {
    page: pagination.page,
    size: pagination.size,
    include: 'company,contacts', // ADR-004 HTTP enrichment
  };
 
  // Build filter query parameter
  const filterParts: string[] = [];
 
  if (filters.tier && filters.tier !== 'all') {
    filterParts.push(`partnershipLevel:${filters.tier}`);
  }
 
  if (filters.status === 'active') {
    filterParts.push('isActive:true');
  } else if (filters.status === 'inactive') {
    filterParts.push('isActive:false');
  }
 
  if (filterParts.length > 0) {
    params.filter = filterParts.join(',');
  }
 
  // Build sort query parameter
  params.sort = `${sort.sortBy}:${sort.sortOrder}`;
 
  const response = await apiClient.get<PartnerListResponse>(PARTNER_API_PATH, { params });
  // ✅ Resolves to: http://localhost:8080/api/v1/partners?page=0&size=20&include=company,contacts&filter=...&sort=...
  return response.data;
};
 
/**
 * Get partner statistics for overview dashboard
 * Epic 8 feature - currently returns mock data
 *
 * @returns Promise<PartnerStatistics>
 */
export const getPartnerStatistics = async (): Promise<PartnerStatistics> => {
  const response = await apiClient.get<PartnerStatistics>(`${PARTNER_API_PATH}/statistics`);
  return response.data;
};
 
// ============================================================================
// Story 2.8.2: Partner Detail View APIs
// ============================================================================
 
/**
 * Get partner detail by companyName with optional includes
 * Story 2.8.2
 * @param companyName - Company name (meaningful ID)
 * @param include - Optional comma-separated includes (company,contacts,votes,meetings,activity)
 * @returns PartnerResponse with enriched data
 */
export const getPartnerDetail = async (
  companyName: string,
  include?: string
): Promise<PartnerResponse> => {
  const params: Record<string, string> = {};
  if (include) {
    params.include = include;
  }
  const response = await apiClient.get(`${PARTNER_API_PATH}/${companyName}`, { params });
  return response.data;
};
 
// ============================================================================
// Story 2.8.3: Partner Create/Edit Modal APIs
// ============================================================================
 
/**
 * Create a new partnership
 * Story 2.8.3
 * @param request - CreatePartnerRequest payload
 * @returns Created PartnerResponse
 */
export const createPartner = async (request: CreatePartnerRequest): Promise<PartnerResponse> => {
  const response = await apiClient.post<PartnerResponse>(PARTNER_API_PATH, request);
  return response.data;
};
 
/**
 * Update an existing partnership
 * Story 2.8.3
 * @param companyName - Company name (meaningful ID)
 * @param request - UpdatePartnerRequest payload
 * @returns Updated PartnerResponse
 */
export const updatePartner = async (
  companyName: string,
  request: UpdatePartnerRequest
): Promise<PartnerResponse> => {
  const response = await apiClient.patch<PartnerResponse>(
    `${PARTNER_API_PATH}/${companyName}`,
    request
  );
  return response.data;
};
 
/**
 * Get partner contacts for a company.
 * Returns all users with PARTNER role and matching companyId from User Service.
 *
 * @param companyName - Company name (meaningful ID per ADR-003)
 * @returns Array of PartnerContactResponse
 */
export const getPartnerContacts = async (
  companyName: string
): Promise<PartnerContactResponse[]> => {
  const response = await apiClient.get<PartnerContactResponse[]>(
    `${PARTNER_API_PATH}/${companyName}/contacts`
  );
  return response.data;
};