client.js

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FixleClient = void 0;
const https = __importStar(require("https"));
const http = __importStar(require("http"));
/**
 * Client for interacting with the Fixle API
 *
 * @example
 * const client = new FixleClient({
 *   apiUrl: 'https://api.fixle.com',
 *   apiKey: 'your-api-key'
 * });
 *
 * // Create a property
 * const propertyId = await client.findOrCreateProperty('123 Main St, Portland, OR 97201');
 *
 * // Create an inspection
 * await client.createInspection(propertyId, 12345);
 *
 * // Add an appliance
 * await client.createAppliance(propertyId, {
 *   item_name: 'Water Heater',
 *   section_name: 'Basement',
 *   brand: 'Rheem',
 *   model: 'XE50M06ST45U1',
 *   serial_number: 'ABC123',
 *   manufacturer: 'Rheem Manufacturing',
 *   year: '2020'
 * });
 */
class FixleClient {
    /**
     * Creates a new Fixle API client
     * @param config - Configuration options including API URL and key
     */
    constructor(config) {
        this.apiUrl = config.apiUrl;
        this.apiKey = config.apiKey;
    }
    /**
     * Makes an HTTP request to the Fixle API
     * @private
     * @param method - HTTP method (GET, POST, etc.)
     * @param path - API endpoint path
     * @param data - Optional request body data
     * @returns Promise resolving to the API response
     * @throws Error if the request fails or returns an error status
     */
    makeApiRequest(method, path, data) {
        return new Promise((resolve, reject) => {
            const url = new URL(`${this.apiUrl}${path}`);
            const postData = data ? JSON.stringify(data) : '';
            const protocol = url.protocol === 'https:' ? https : http;
            const options = {
                hostname: url.hostname,
                port: url.port || (url.protocol === 'https:' ? 443 : 80),
                path: url.pathname,
                method,
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json',
                    'X-API-Key': this.apiKey,
                    ...(postData && { 'Content-Length': Buffer.byteLength(postData) }),
                },
            };
            const req = protocol.request(options, (res) => {
                let body = '';
                res.on('data', (chunk) => body += chunk);
                res.on('end', () => {
                    if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
                        try {
                            const parsed = body ? JSON.parse(body) : {};
                            if (parsed.data) {
                                resolve(parsed);
                            }
                            else {
                                resolve(parsed);
                            }
                        }
                        catch {
                            resolve(body);
                        }
                    }
                    else {
                        try {
                            const errorResponse = JSON.parse(body);
                            if (errorResponse.errors && errorResponse.errors.length > 0) {
                                const error = errorResponse.errors[0];
                                reject(new Error(`API error ${error.status}: ${error.title} - ${error.detail}`));
                            }
                            else {
                                reject(new Error(`API error: ${res.statusCode} - ${body}`));
                            }
                        }
                        catch {
                            reject(new Error(`API error: ${res.statusCode} - ${body}`));
                        }
                    }
                });
            });
            req.on('error', reject);
            if (postData)
                req.write(postData);
            req.end();
        });
    }
    /**
     * Finds an existing property or creates a new one based on the address
     *
     * The address is parsed into street address, city, state, and zip code components.
     * Currently always creates a new property; future versions may search for existing properties first.
     *
     * @param address - Full address string (e.g., "123 Main St, Portland, OR 97201")
     * @returns Promise resolving to the property ID
     * @throws Error if the API request fails
     *
     * @example
     * const propertyId = await client.findOrCreateProperty('123 Main St, Portland, OR 97201');
     * console.log(`Created property with ID: ${propertyId}`);
     */
    async findOrCreateProperty(address) {
        const parts = address.split(',').map(s => s.trim());
        const streetAddress = parts[0] || address;
        const cityStateZip = parts[1] || '';
        const [city, stateZip] = cityStateZip.split(/\s+(?=\w{2}\s+\d)/);
        const [state, zipCode] = (stateZip || '').split(/\s+/);
        const propertyData = {
            property: {
                street_address: streetAddress,
                city: city || 'Unknown',
                state: state || 'Unknown',
                zip_code: zipCode || '00000',
                country: 'US',
            },
        };
        const response = await this.makeApiRequest('POST', '/api/v1/properties', propertyData);
        return parseInt(response.data?.id || response.id);
    }
    /**
     * Creates an inspection record for a property
     *
     * @param propertyId - ID of the property to associate the inspection with
     * @param inspectionId - External inspection ID (from the source system)
     * @returns Promise that resolves when the inspection is created
     * @throws Error if the API request fails or the property doesn't exist
     *
     * @example
     * await client.createInspection(123, 45678);
     * console.log('Inspection created successfully');
     */
    async createInspection(propertyId, inspectionId) {
        const inspectionData = {
            inspection: {
                external_id: inspectionId.toString(),
            },
        };
        await this.makeApiRequest('POST', `/api/v1/properties/${propertyId}/inspections`, inspectionData);
    }
    /**
     * Creates an appliance record for a property
     *
     * The appliance is associated with a master appliance type (currently uses ID 1 as placeholder).
     * In production, you should implement logic to find or create the appropriate master appliance.
     *
     * @param propertyId - ID of the property to add the appliance to
     * @param appliance - Appliance information including brand, model, serial number, etc.
     * @returns Promise that resolves when the appliance is created
     * @throws Error if the API request fails or the property doesn't exist
     *
     * @example
     * await client.createAppliance(123, {
     *   item_name: 'Water Heater',
     *   section_name: 'Basement',
     *   brand: 'Rheem',
     *   model: 'XE50M06ST45U1',
     *   serial_number: 'ABC123456',
     *   manufacturer: 'Rheem Manufacturing',
     *   year: '2020'
     * });
     */
    async createAppliance(propertyId, appliance) {
        const applianceData = {
            property_appliance: {
                appliance_id: 1,
                serial: appliance.serial_number,
                model: appliance.model,
                year: appliance.year,
                notes: `${appliance.item_name} - Brand: ${appliance.brand || 'Unknown'}`,
                location: appliance.section_name,
            },
        };
        await this.makeApiRequest('POST', `/api/v1/properties/${propertyId}/appliances`, applianceData);
    }
}
exports.FixleClient = FixleClient;