Skip to main content
Glama

Boilerplate MCP Server

by devkindhq
swell.customers.service.ts•16.9 kB
import { z } from 'zod'; import { Logger } from '../utils/logger.util.js'; import { config } from '../utils/config.util.js'; import { swellClient } from '../utils/swell-client.util.js'; import { createApiError, createUnexpectedError, McpError, } from '../utils/error.util.js'; import { SwellCustomer, SwellCustomerSchema, SwellCustomersList, SwellCustomersListSchema, CustomerListOptions, CustomerSearchOptions, CustomerGetOptions, CustomerOrderHistoryOptions, CustomerAnalyticsOptions, CustomerUpdateOptions, CustomerUpdateOptionsSchema, } from './swell.customers.types.js'; import { SwellOrdersList } from './swell.orders.types.js'; import swellOrdersService from './swell.orders.service.js'; // Create a contextualized logger for this file const serviceLogger = Logger.forContext('services/swell.customers.service.ts'); // Log service initialization serviceLogger.debug('Swell Customers service initialized'); /** * @namespace SwellCustomersService * @description Service layer for interacting with Swell Customers API. * Handles customer listing, search, profile retrieval, and analytics. */ /** * @function list * @description Fetches a paginated list of customers from Swell with optional filtering. * @memberof SwellCustomersService * @param {CustomerListOptions} [options={}] - Optional filtering and pagination options * @returns {Promise<SwellCustomersList>} A promise that resolves to the customers list with pagination info * @throws {McpError} Throws an `McpError` if the API call fails or response validation fails * @example * // Get first 10 customers * const customers = await list({ limit: 10 }); * // Get customers in a specific group * const groupCustomers = await list({ group_id: 'vip-customers', page: 1 }); * // Get customers with high order value * const highValueCustomers = await list({ order_value: { $gte: 1000 } }); */ async function list( options: CustomerListOptions = {}, ): Promise<SwellCustomersList> { const methodLogger = serviceLogger.forMethod('list'); methodLogger.debug('Fetching customers list', options); try { // Ensure client is initialized if (!swellClient.isClientInitialized()) { swellClient.initWithAutoConfig(); } const client = swellClient.getClient(); // Build query parameters const queryParams: Record<string, unknown> = {}; if (options.page !== undefined) { queryParams.page = options.page; } if (options.limit !== undefined) { queryParams.limit = options.limit; } if (options.email) { queryParams.email = options.email; } if (options.first_name) { queryParams.first_name = options.first_name; } if (options.last_name) { queryParams.last_name = options.last_name; } if (options.phone) { queryParams.phone = options.phone; } if (options.group_id) { queryParams.group_id = options.group_id; } if (options.tags && options.tags.length > 0) { queryParams.tags = options.tags.join(','); } if (options.date_created) { queryParams.date_created = options.date_created; } if (options.date_updated) { queryParams.date_updated = options.date_updated; } if (options.order_count) { queryParams.order_count = options.order_count; } if (options.order_value) { queryParams.order_value = options.order_value; } if (options.search) { queryParams.search = options.search; } if (options.sort) { queryParams.sort = options.sort; } if (options.where) { queryParams.where = options.where; } if (options.expand && options.expand.length > 0) { queryParams.expand = options.expand.join(','); } // Make the API call const rawData = await client.get<unknown>('/accounts', queryParams); // Validate response with Zod schema const validatedData = SwellCustomersListSchema.parse(rawData); methodLogger.debug( `Successfully fetched ${validatedData.results.length} customers`, { count: validatedData.count, page: validatedData.page, pages: validatedData.pages, }, ); return validatedData; } catch (error) { methodLogger.error('Service error fetching customers list', error); // Handle Zod validation errors if (error instanceof z.ZodError) { throw createApiError( `Customers list response validation failed: ${error.issues .map((e: z.ZodIssue) => `${e.path.join('.')}: ${e.message}`) .join(', ')}`, 500, error, ); } // Rethrow other McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( 'Unexpected service error while fetching customers list', error, ); } } /** * @function get * @description Fetches detailed information for a specific customer by ID. * @memberof SwellCustomersService * @param {string} customerId - The ID of the customer to retrieve * @param {CustomerGetOptions} [options={}] - Optional retrieval options * @returns {Promise<SwellCustomer>} A promise that resolves to the customer details * @throws {McpError} Throws an `McpError` if the customer is not found or API call fails * @example * // Get basic customer details * const customer = await get('customer-id-123'); * // Get customer with expanded relationships * const customerWithOrders = await get('customer-id-123', { expand: ['orders', 'addresses'] }); */ async function get( customerId: string, options: CustomerGetOptions = {}, ): Promise<SwellCustomer> { const methodLogger = serviceLogger.forMethod('get'); methodLogger.debug( `Fetching customer details for ID: ${customerId}`, options, ); if (!customerId || customerId.trim().length === 0) { throw createApiError('Customer ID is required', 400); } try { // Ensure client is initialized if (!swellClient.isClientInitialized()) { swellClient.initWithAutoConfig(); } const client = swellClient.getClient(); // Build query parameters const queryParams: Record<string, unknown> = {}; if (options.expand && options.expand.length > 0) { queryParams.expand = options.expand.join(','); } // Make the API call const rawData = await client.get<unknown>( `/accounts/${customerId}`, queryParams, ); // Handle null response (customer not found) if (!rawData) { throw createApiError(`Customer not found: ${customerId}`, 404); } // Validate response with Zod schema const validatedData = SwellCustomerSchema.parse(rawData); methodLogger.debug( `Successfully fetched customer: ${validatedData.email || customerId}`, ); return validatedData; } catch (error) { methodLogger.error( `Service error fetching customer ${customerId}`, error, ); // Handle Zod validation errors if (error instanceof z.ZodError) { throw createApiError( `Customer response validation failed: ${error.issues .map((e: z.ZodIssue) => `${e.path.join('.')}: ${e.message}`) .join(', ')}`, 500, error, ); } // Rethrow other McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( `Unexpected service error while fetching customer ${customerId}`, error, ); } } /** * @function search * @description Searches for customers using various criteria. * @memberof SwellCustomersService * @param {CustomerSearchOptions} options - Search options including query and filters * @returns {Promise<SwellCustomersList>} A promise that resolves to the search results * @throws {McpError} Throws an `McpError` if the search fails or response validation fails * @example * // Search for customers by email * const results = await search({ query: 'john@example.com', limit: 20 }); * // Search with additional filters * const filteredResults = await search({ * query: 'smith', * group_id: 'vip-customers', * sort: 'order_value_desc' * }); */ async function search( options: CustomerSearchOptions, ): Promise<SwellCustomersList> { const methodLogger = serviceLogger.forMethod('search'); methodLogger.debug('Searching customers', options); if (!options.query || options.query.trim().length === 0) { throw createApiError('Search query is required', 400); } try { // Use the list function with search parameter const searchOptions: CustomerListOptions = { search: options.query, page: options.page, limit: options.limit, group_id: options.group_id, tags: options.tags, sort: options.sort, expand: options.expand, }; const results = await list(searchOptions); methodLogger.debug( `Search completed: found ${results.count} customers matching "${options.query}"`, ); return results; } catch (error) { methodLogger.error( `Service error searching customers with query "${options.query}"`, error, ); // Rethrow McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( `Unexpected service error while searching customers with query "${options.query}"`, error, ); } } /** * @function getOrderHistory * @description Retrieves order history for a specific customer. * @memberof SwellCustomersService * @param {CustomerOrderHistoryOptions} options - Order history options * @returns {Promise<SwellOrdersList>} A promise that resolves to the customer's order history * @throws {McpError} Throws an `McpError` if the customer is not found or API call fails * @example * // Get all orders for a customer * const orderHistory = await getOrderHistory({ customer_id: 'customer-id-123' }); * // Get recent orders with status filter * const recentOrders = await getOrderHistory({ * customer_id: 'customer-id-123', * status: 'complete', * limit: 10, * sort: 'date_created_desc' * }); */ async function getOrderHistory( options: CustomerOrderHistoryOptions, ): Promise<SwellOrdersList> { const methodLogger = serviceLogger.forMethod('getOrderHistory'); methodLogger.debug( `Fetching order history for customer: ${options.customer_id}`, options, ); if (!options.customer_id || options.customer_id.trim().length === 0) { throw createApiError('Customer ID is required', 400); } try { // Build order list options for the customer const orderListOptions: Record<string, unknown> = { account_id: options.customer_id, page: options.page, limit: options.limit, sort: options.sort || 'date_created_desc', expand: ['items'], }; // Add status filter if provided if (options.status) { orderListOptions.status = options.status; } // Add date filters if provided if (options.date_from || options.date_to) { const dateFilter: Record<string, string> = {}; if (options.date_from) { dateFilter.$gte = options.date_from; } if (options.date_to) { dateFilter.$lte = options.date_to; } orderListOptions.date_created = dateFilter; } // Get orders using the orders service const orderHistory = await swellOrdersService.list(orderListOptions); methodLogger.debug( `Successfully fetched order history: ${orderHistory.count} orders for customer ${options.customer_id}`, ); return orderHistory; } catch (error) { methodLogger.error( `Service error fetching order history for customer ${options.customer_id}`, error, ); // Rethrow McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( `Unexpected service error while fetching order history for customer ${options.customer_id}`, error, ); } } /** * @function getAnalytics * @description Retrieves customer analytics and behavior insights. * @memberof SwellCustomersService * @param {CustomerAnalyticsOptions} [options={}] - Analytics options * @returns {Promise<SwellCustomersList>} A promise that resolves to customers data for analytics * @throws {McpError} Throws an `McpError` if the API call fails * @example * // Get analytics for all customers * const analytics = await getAnalytics({ * date_from: '2023-01-01', * date_to: '2023-12-31', * metrics: ['order_count', 'order_value', 'lifetime_value'] * }); * // Get analytics for a specific customer * const customerAnalytics = await getAnalytics({ * customer_id: 'customer-id-123', * metrics: ['order_count', 'average_order_value'] * }); */ async function getAnalytics( options: CustomerAnalyticsOptions = {}, ): Promise<SwellCustomersList> { const methodLogger = serviceLogger.forMethod('getAnalytics'); methodLogger.debug('Fetching customer analytics', options); try { // Build list options for analytics const listOptions: CustomerListOptions = { limit: 1000, // Get more data for analytics expand: ['orders'], // Include order data for analytics }; if (options.customer_id) { // Get specific customer analytics const customer = await get(options.customer_id, { expand: ['orders'], }); return { count: 1, results: [customer], page: 1, pages: 1, }; } if (options.group_id) { listOptions.group_id = options.group_id; } if (options.date_from || options.date_to) { listOptions.date_created = {}; if (options.date_from) { listOptions.date_created.$gte = options.date_from; } if (options.date_to) { listOptions.date_created.$lte = options.date_to; } } // Sort by order value for analytics listOptions.sort = 'order_value_desc'; // Get customers data const customersData = await list(listOptions); methodLogger.debug( `Successfully fetched analytics data: ${customersData.count} customers`, { customer_id: options.customer_id, group_id: options.group_id, date_from: options.date_from, date_to: options.date_to, }, ); return customersData; } catch (error) { methodLogger.error('Service error fetching customer analytics', error); // Rethrow McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( 'Unexpected service error while fetching customer analytics', error, ); } } /** * @function update * @description Updates customer information in Swell. * @memberof SwellCustomersService * @param {string} customerId - The ID of the customer to update * @param {CustomerUpdateOptions} updateData - The customer data to update * @returns {Promise<SwellCustomer>} A promise that resolves to the updated customer * @throws {McpError} Throws an `McpError` if the customer is not found or API call fails * @example * // Update customer name * const updatedCustomer = await update('customer-id-123', { * first_name: 'Babar', * last_name: 'Ali' * }); * // Update customer email and phone * const updatedCustomer = await update('customer-id-123', { * email: 'babar.ali@example.com', * phone: '+1234567890' * }); */ async function update( customerId: string, updateData: CustomerUpdateOptions, ): Promise<SwellCustomer> { const methodLogger = serviceLogger.forMethod('update'); methodLogger.debug(`Updating customer ${customerId}`, { customerId, updateData, }); if (!customerId || customerId.trim().length === 0) { throw createApiError('Customer ID is required', 400); } try { // Ensure client is initialized if (!swellClient.isClientInitialized()) { swellClient.initWithAutoConfig(); } const client = swellClient.getClient(); // Validate update data with Zod schema const validatedData = CustomerUpdateOptionsSchema.parse(updateData); // Make the API call const rawData = await client.put<unknown>( `/accounts/${customerId}`, validatedData, ); // Handle null response (customer not found) if (!rawData) { throw createApiError(`Customer not found: ${customerId}`, 404); } // Check if debug mode is enabled const isDebugMode = config.getBoolean('DEBUG', false); if (isDebugMode) { methodLogger.debug( 'Debug mode enabled - returning raw data without validation', ); return rawData as SwellCustomer; } // Validate response with Zod schema const validatedCustomer = SwellCustomerSchema.parse(rawData); methodLogger.debug( `Successfully updated customer: ${validatedCustomer.first_name} ${validatedCustomer.last_name}`, ); return validatedCustomer; } catch (error) { methodLogger.error( `Service error updating customer ${customerId}`, error, ); // Handle Zod validation errors if (error instanceof z.ZodError) { throw createApiError( `Customer update validation failed: ${error.issues .map((e: z.ZodIssue) => `${e.path.join('.')}: ${e.message}`) .join(', ')}`, 400, error, ); } // Rethrow other McpErrors if (error instanceof McpError) { throw error; } // Wrap any other unexpected errors throw createUnexpectedError( `Unexpected service error while updating customer ${customerId}`, error, ); } } export default { list, get, search, getOrderHistory, getAnalytics, update, };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/devkindhq/swell-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server