Phone Exchange C# Rest Code Snippet

using System.Web;

namespace geophone_plus_2_dot_net.REST
{
    /// <summary>
    /// Provides functionality to call the ServiceObjects GeoPhone Plus 2 REST API's GetPhoneInfo endpoint,
    /// retrieving reverse phone lookup information (e.g., provider, contacts, SMS/MMS addresses) with fallback to a backup endpoint
    /// for reliability in live mode.
    /// </summary>
    public class GetPhoneInfoClient
    {
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/gppl2/api.svc/";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/gppl2/api.svc/";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/gppl2/api.svc/";

        /// <summary>
        /// Synchronously calls the GetPhoneInfo REST endpoint to retrieve reverse phone lookup information,
        /// attempting the primary endpoint first and falling back to the backup if the response is invalid
        /// (Error.TypeCode == "3") in live mode.
        /// </summary>
        /// <param name="input">The input parameters including phone number, test type, and license key.</param>
        /// <returns>Deserialized <see cref="GPPL2Response"/>.</returns>
        public static GPPL2Response Invoke(GetPhoneInfoInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GPPL2Response response = Helper.HttpGet<GPPL2Response>(url, input.TimeoutSeconds);

            // Fallback on error in live mode
            if (input.IsLive && !IsValid(response))
            {
                string fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GPPL2Response fallbackResponse = Helper.HttpGet<GPPL2Response>(fallbackUrl, input.TimeoutSeconds);
                return fallbackResponse;
            }

            return response;
        }

        /// <summary>
        /// Asynchronously calls the GetPhoneInfo REST endpoint to retrieve reverse phone lookup information,
        /// attempting the primary endpoint first and falling back to the backup if the response is invalid
        /// (Error.TypeCode == "3") in live mode.
        /// </summary>
        /// <param name="input">The input parameters including phone number, test type, and license key.</param>
        /// <returns>Deserialized <see cref="GPPL2Response"/>.</returns>
        public static async Task<GPPL2Response> InvokeAsync(GetPhoneInfoInput input)
        {
            // Use query string parameters so missing/optional fields don't break
            // the URL as path parameters would.
            string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GPPL2Response response = await Helper.HttpGetAsync<GPPL2Response>(url, input.TimeoutSeconds).ConfigureAwait(false);
            if (input.IsLive && !IsValid(response))
            {
                string fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GPPL2Response fallbackResponse = await Helper.HttpGetAsync<GPPL2Response>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
                return fallbackResponse;
            }

            return response;
        }

        // Build the full request URL, including URL-encoded query string
        public static string BuildUrl(GetPhoneInfoInput input, string baseUrl)
        {
            string qs = $"GetPhoneInfoJson?" +
                     $"PhoneNumber={HttpUtility.UrlEncode(input.PhoneNumber)}" +
                     $"&TestType={HttpUtility.UrlEncode(input.TestType)}" +
                     $"&LicenseKey={HttpUtility.UrlEncode(input.LicenseKey)}";
            return baseUrl + qs;
        }

        private static bool IsValid(GPPL2Response response) => 
            response?.Error == null || response.Error.TypeCode != "3";

        /// <summary>
        /// This is the basic operation for finding the reverse-lookup information.Given a phone number and test type, it will consult national directory-assistance databases to find the owner and address registered.The addresses returned are validated via third party address-validation technique. They are returned to you exactly as the phone carrier releases them.If you need these addresses to be validated, using Service Objects’ AddressValidation web services is highly recommended.Both the contact’s information and the phone company’s information are returned with this operation. Two valuable bits of information are also retrieved – whether the phone line is for business or residential purposes, and whether the phone line is landline or wireless.By examining the WSDL, you may see that multiple groups of contact/exchange information are possible. Although they are possible in the XML, you will only see one exchange per output, always.It is common, however, to see multiple contacts per phone number (as people change numbers, or there may be multiple businesses at the same phone number.) It is highly recommended that you handle each of these contacts, rather than just the first contact returned.
        /// </summary>
        /// <param name="PhoneNumber">10 digit phone number</param>
        /// <param name="TestType">“FULL”, “BASIC” or “NORMAL”</param>
        /// <param name="LicenseKey">Your license key to use the service</param>
        /// <param name="IsLive">Option to use live service or trial service</param>
        /// <param name="TimeoutSeconds">Timeout, in seconds, for the call to the service.  </param>
        public record GetPhoneInfoInput(
            string PhoneNumber = "",
            string TestType = "",
            string LicenseKey = "",
            bool IsLive = true,
            int TimeoutSeconds = 15
        );
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace geophone_plus_2_dot_net.REST
{
    public class GPPL2Response
    {
        public string PhoneNumber { get; set; }
        public string TestType { get; set; }
        public string LicenseKey { get; set; }
        public PhoneInfo PhoneInfo { get; set; }
        public Error Error { get; set; }
    }
    public class PhoneInfo
    {
        public Provider Provider { get; set; }
        public Contacts[] Contacts { get; set; }
        public string SMSAddress { get; set; }
        public string MMSAddress { get; set; }
        public string DateFirstSeen { get; set; }
        public string DateOfPorting { get; set; }
        public string NoteCodes { get; set; }
        public string NoteDescriptions { get; set; }
        public string TokensUsed { get; set; }

        public override string ToString()
        {
            string contactsString = Contacts != null ? string.Join(", ", Contacts.Select(contact => contact.ToString())) : "null";

            return $"PhoneInfo - Providers: {Provider}, Contacts: [{contactsString}], SMSAddress: {SMSAddress}, MMSAddress: {MMSAddress}, DateFirstSeen: {DateFirstSeen}, DateOfPorting: {DateOfPorting}, NoteCodes: {NoteCodes}, NoteDescriptions: {NoteDescriptions}, TokensUsed: {TokensUsed}";
        }
    }
    public class Provider
    {
        public string Name { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string LineType { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }

        public override string ToString()
        {
            return $"Provider - Name: {Name}, City: {City}, State: {State}, LineType: {LineType}, Latitude: {Latitude}, Longitude: {Longitude}";
        }
    }
    public class Contacts
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
        public string PhoneType { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string SICCode { get; set; }
        public string SICDesc { get; set; }
        public string QualityScore { get; set; }

        public override string ToString()
        {
            return $"Contacts - Name: {Name}, Address: {Address}, City: {City}, State: {State}, PostalCode: {PostalCode}, PhoneType: {PhoneType}, Latitude: {Latitude}, Longitude: {Longitude}, SICCode: {SICCode}, SICDesc: {SICDesc}, QualityScore: {QualityScore}";
        }
    }
    public class Error
    {
        public string Type { get; set; }
        public string TypeCode { get; set; }
        public string Desc { get; set; }
        public string DescCode { get; set; }
        public override string ToString()
        {
            return $"Type: {Type} " +
                $"TypeCode: {TypeCode} " +
                $"Desc: {Desc} " +
                $"DescCode: {DescCode} ";
        }
    }

}


using System.Text.Json;
using System.Web;

namespace geophone_plus_2_dot_net.REST
{
    public class Helper
    {
        public static T HttpGet<T>(string url, int timeoutSeconds)
        {
            using var httpClient = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(timeoutSeconds)
            };
            using var request = new HttpRequestMessage(HttpMethod.Get, url);
            using HttpResponseMessage response = httpClient
                .SendAsync(request)
                .GetAwaiter()
                .GetResult();
            response.EnsureSuccessStatusCode();
            using Stream responseStream = response.Content
                .ReadAsStreamAsync()
                .GetAwaiter()
                .GetResult();
            var options = new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            };
            object? obj = JsonSerializer.Deserialize(responseStream, typeof(T), options);
            T result = (T)obj!;
            return result;
        }

        // Asynchronous HTTP GET and JSON deserialize
        public static async Task<T> HttpGetAsync<T>(string url, int timeoutSeconds)
        {
            HttpClient HttpClient = new HttpClient();
            HttpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
            using var httpResponse = await HttpClient.GetAsync(url).ConfigureAwait(false);
            httpResponse.EnsureSuccessStatusCode();
            var stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
            return JsonSerializer.Deserialize<T>(stream)!;
        }

        public static string UrlEncode(string value) => HttpUtility.UrlEncode(value ?? string.Empty);
    }
}

Phone Exchange Python Rest Code Snippet

import requests
from gppl2_response import GPPL2Response, Provider, Contacts, PhoneInfo, Error

# Endpoint URLs for GeoPhone Plus 2 GetPhoneInfo REST API
primary_url = 'https://sws.serviceobjects.com/gppl2/api.svc/GetPhoneInfoJson?'
backup_url = 'https://swsbackup.serviceobjects.com/gppl2/api.svc/GetPhoneInfoJson?'
trial_url = 'https://trial.serviceobjects.com/gppl2/api.svc/GetPhoneInfoJson?'

def get_phone_info(
    phone_number: str,
    test_type: str,
    license_key: str,
    is_live: bool = True,
    timeout_seconds: int = 15
) -> GPPL2Response:
    """
    Call GeoPhone Plus 2 GetPhoneInfo API to retrieve reverse phone lookup information.

    Parameters:
        phone_number: The 10 digit phone number.
        test_type: The type of validation to perform ('FULL', 'BASIC', or 'NORMAL').
        license_key: Your license key to use the service
        is_live: Value to determine whether to use the live or trial servers (default: True).
        timeout_seconds: Timeout, in seconds, for the call to the service (default: 15).

    Returns:
        GPPL2Response: Parsed JSON response with phone information or error details.
    """
    params = {
        'PhoneNumber': phone_number,
        'TestType': test_type,
        'LicenseKey': license_key
    }

    # Select the base URL: production vs trial
    url = primary_url if is_live else trial_url

    # Attempt primary (or trial) endpoint first
    try:
        response = requests.get(url, params=params, timeout=timeout_seconds)
        response.raise_for_status()
        data = response.json()

        # If API returned an error in JSON payload, trigger fallback
        error = getattr(response, 'Error', None)
        if not (error is None or getattr(error, 'TypeCode', None) != "3"):
            if is_live:
            # Try backup URL
              response = requests.get(backup_url, params=params, timeout=timeout_seconds)
              response.raise_for_status()
              data = response.json()

            # If still error, propagate exception
            if 'Error' in data:
                raise RuntimeError(f"GPPL2 service error: {data['Error']}")

            else:
              # Trial mode error is terminal
              raise RuntimeError(f"GPPL2 trial error: {data['Error']}")

        # Convert JSON response to GPPL2Response for structured access
        error = Error(**data.get('Error', {})) if data.get('Error') else None
        phone_info = None
        if data.get('PhoneInfo'):
            provider = Provider(**data['PhoneInfo'].get('Provider', {})) if data['PhoneInfo'].get('Provider') else None
            contacts = [Contacts(**contact) for contact in data['PhoneInfo'].get('Contacts', [])] if data['PhoneInfo'].get('Contacts') else None
            phone_info = PhoneInfo(
                Provider=provider,
                Contacts=contacts,
                SMSAddress=data['PhoneInfo'].get('SMSAddress'),
                MMSAddress=data['PhoneInfo'].get('MMSAddress'),
                DateFirstSeen=data['PhoneInfo'].get('DateFirstSeen'),
                DateOfPorting=data['PhoneInfo'].get('DateOfPorting'),
                NoteCodes=data['PhoneInfo'].get('NoteCodes'),
                NoteDescriptions=data['PhoneInfo'].get('NoteDescriptions'),
                TokensUsed=data['PhoneInfo'].get('TokensUsed')
            )

        return GPPL2Response(
            PhoneNumber=data.get('PhoneNumber'),
            TestType=data.get('TestType'),
            LicenseKey=data.get('LicenseKey'),
            PhoneInfo=phone_info,
            Error=error
        )

    except requests.RequestException as req_exc:
        # Network or HTTP-level error occurred
        if is_live:
            try:
                # Fallback to backup URL on network failure
                response = requests.get(backup_url, params=params, timeout=timeout_seconds)
                response.raise_for_status()
                data = response.json()
                if 'Error' in data:
                    raise RuntimeError(f"GPPL2 backup error: {data['Error']}") from req_exc

                # Convert JSON response to GPPL2Response for structured access
                error = Error(**data.get('Error', {})) if data.get('Error') else None
                phone_info = None
                if data.get('PhoneInfo'):
                    provider = Provider(**data['PhoneInfo'].get('Provider', {})) if data['PhoneInfo'].get('Provider') else None
                    contacts = [Contacts(**contact) for contact in data['PhoneInfo'].get('Contacts', [])] if data['PhoneInfo'].get('Contacts') else None
                    phone_info = PhoneInfo(
                        Provider=provider,
                        Contacts=contacts,
                        SMSAddress=data['PhoneInfo'].get('SMSAddress'),
                        MMSAddress=data['PhoneInfo'].get('MMSAddress'),
                        DateFirstSeen=data['PhoneInfo'].get('DateFirstSeen'),
                        DateOfPorting=data['PhoneInfo'].get('DateOfPorting'),
                        NoteCodes=data['PhoneInfo'].get('NoteCodes'),
                        NoteDescriptions=data['PhoneInfo'].get('NoteDescriptions'),
                        TokensUsed=data['PhoneInfo'].get('TokensUsed')
                    )

                return GPPL2Response(
                    PhoneNumber=data.get('PhoneNumber'),
                    TestType=data.get('TestType'),
                    LicenseKey=data.get('LicenseKey'),
                    PhoneInfo=phone_info,
                    Error=error
                )
            except Exception as backup_exc:
                raise RuntimeError("GPPL2 service unreachable on both endpoints") from backup_exc
        else:
            raise RuntimeError(f"GPPL2 trial error: {str(req_exc)}") from req_exc



from dataclasses import dataclass
from typing import Optional, List

@dataclass
class Provider:
    Name: Optional[str] = None
    City: Optional[str] = None
    State: Optional[str] = None
    LineType: Optional[str] = None
    Latitude: Optional[str] = None
    Longitude: Optional[str] = None

    def __str__(self) -> str:
        return (f"Provider: Name={self.Name}, City={self.City}, State={self.State}, "
                f"LineType={self.LineType}, Latitude={self.Latitude}, Longitude={self.Longitude}")

@dataclass
class Contacts:
    Name: Optional[str] = None
    Address: Optional[str] = None
    City: Optional[str] = None
    State: Optional[str] = None
    PostalCode: Optional[str] = None
    PhoneType: Optional[str] = None
    Latitude: Optional[str] = None
    Longitude: Optional[str] = None
    SICCode: Optional[str] = None
    SICDesc: Optional[str] = None
    QualityScore: Optional[str] = None

    def __str__(self) -> str:
        return (f"Contacts: Name={self.Name}, Address={self.Address}, City={self.City}, "
                f"State={self.State}, PostalCode={self.PostalCode}, PhoneType={self.PhoneType}, "
                f"Latitude={self.Latitude}, Longitude={self.Longitude}, SICCode={self.SICCode}, "
                f"SICDesc={self.SICDesc}, QualityScore={self.QualityScore}")

@dataclass
class PhoneInfo:
    Provider: Optional[Provider] = None
    Contacts: Optional[List[Contacts]] = None
    SMSAddress: Optional[str] = None
    MMSAddress: Optional[str] = None
    DateFirstSeen: Optional[str] = None
    DateOfPorting: Optional[str] = None
    NoteCodes: Optional[str] = None
    NoteDescriptions: Optional[str] = None
    TokensUsed: Optional[str] = None

    def __str__(self) -> str:
        contacts = ", ".join(str(c) for c in self.Contacts) if self.Contacts else "None"
        provider = str(self.Provider) if self.Provider else "None"
        return (f"PhoneInfo: Provider={provider}, Contacts=[{contacts}], SMSAddress={self.SMSAddress}, "
                f"MMSAddress={self.MMSAddress}, DateFirstSeen={self.DateFirstSeen}, "
                f"DateOfPorting={self.DateOfPorting}, NoteCodes={self.NoteCodes}, "
                f"NoteDescriptions={self.NoteDescriptions}, TokensUsed={self.TokensUsed}")

@dataclass
class Error:
    Type: Optional[str] = None
    TypeCode: Optional[str] = None
    Desc: Optional[str] = None
    DescCode: Optional[str] = None

    def __str__(self) -> str:
        return (f"Error: Type={self.Type}, TypeCode={self.TypeCode}, Desc={self.Desc}, "
                f"DescCode={self.DescCode}")

@dataclass
class GPPL2Response:
    PhoneNumber: Optional[str] = None
    TestType: Optional[str] = None
    LicenseKey: Optional[str] = None
    PhoneInfo: Optional[PhoneInfo] = None
    Error: Optional[Error] = None

    def __str__(self) -> str:
        phone_info = str(self.PhoneInfo) if self.PhoneInfo else "None"
        error = str(self.Error) if self.Error else "None"
        return (f"GPPL2Response: PhoneNumber={self.PhoneNumber}, TestType={self.TestType}, "
                f"LicenseKey={self.LicenseKey}, PhoneInfo={phone_info}")

Phone Exchange NodeJS Rest Code Snippet

import axios from 'axios';
import querystring from 'querystring';
import { GPPL2Response } from './gppl2_response.js';

/**
 * @constant
 * @type {string}
 * @description The base URL for the live ServiceObjects GeoPhone Plus 2 API service.
 */
const liveBaseUrl = 'https://sws.serviceobjects.com/gppl2/api.svc/';

/**
 * @constant
 * @type {string}
 * @description The base URL for the backup ServiceObjects GeoPhone Plus 2 API service.
 */
const backupBaseUrl = 'https://swsbackup.serviceobjects.com/gppl2/api.svc/';

/**
 * @constant
 * @type {string}
 * @description The base URL for the trial ServiceObjects GeoPhone Plus 2 API service.
 */
const trialBaseUrl = 'https://trial.serviceobjects.com/gppl2/api.svc/';

/**
 * <summary>
 * Checks if a response from the API is valid by verifying that it either has no Error object
 * or the Error.TypeCode is not equal to '1'.
 * </summary>
 * <param name="response" type="Object">The API response object to validate.</param>
 * <returns type="boolean">True if the response is valid, false otherwise.</returns>
 */
const isValid = (response) => !response?.Error || response.Error.TypeCode !== '3';

/**
 * <summary>
 * Constructs a full URL for the GetPhoneInfo API endpoint by combining the base URL
 * with query parameters derived from the input parameters.
 * </summary>
 * <param name="params" type="Object">An object containing all the input parameters.</param>
 * <param name="baseUrl" type="string">The base URL for the API service (live, backup, or trial).</param>
 * <returns type="string">The constructed URL with query parameters.</returns>
 */
const buildUrl = (params, baseUrl) =>
    `${baseUrl}GetPhoneInfoJson?${querystring.stringify(params)}`;

/**
 * <summary>
 * Performs an HTTP GET request to the specified URL with a given timeout.
 * </summary>
 * <param name="url" type="string">The URL to send the GET request to.</param>
 * <param name="timeoutSeconds" type="number">The timeout duration in seconds for the request.</param>
 * <returns type="Promise<GPPL2Response>">A promise that resolves to a GPPL2Response object containing the API response data.</returns>
 * <exception cref="Error">Thrown if the HTTP request fails, with a message detailing the error.</exception>
 */
const httpGet = async (url, timeoutSeconds) => {
    try {
        const response = await axios.get(url, { timeout: timeoutSeconds * 1000 });
        return new GPPL2Response(response.data);
    } catch (error) {
        throw new Error(`HTTP request failed: ${error.message}`);
    }
};

/**
 * <summary>
 * Provides functionality to call the ServiceObjects GeoPhone Plus 2 API's GetPhoneInfo endpoint,
 * retrieving reverse phone lookup information with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
const GetPhoneInfoClient = {
    /**
     * <summary>
     * Asynchronously invokes the GetPhoneInfo API endpoint, attempting the primary endpoint
     * first and falling back to the backup if the response is invalid (Error.TypeCode == '3') in live mode.
     * </summary>
     * @param {string} phoneNumber - 10 digit phone number.
     * @param {string} testType - The type of validation to perform ('FULL', 'BASIC', or 'NORMAL').
     * @param {string} licenseKey - Your license key to use the service
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service.
     * @returns {Promise<GPPL2Response>} - A promise that resolves to a GPPL2Response object.
     */
    async invokeAsync(phoneNumber, testType, licenseKey, isLive = true, timeoutSeconds = 15) {
        const params = {
            phoneNumber,
            testType,
            licenseKey
        };

        const url = buildUrl(params, isLive ? liveBaseUrl : trialBaseUrl);
        let response = await httpGet(url, timeoutSeconds);

        if (isLive && !isValid(response)) {
            const fallbackUrl = buildUrl(params, BackupBaseUrl);
            const fallbackResponse = await httpGet(fallbackUrl, timeoutSeconds);
            return isValid(fallbackResponse) ? fallbackResponse : response;
        }

        return response;
    },

    /**
     * <summary>
     * Synchronously invokes the GetPhoneInfo API endpoint by wrapping the async call
     * and awaiting its result immediately.
     * </summary>
     * @returns {GPPL2Response} - A GPPL2Response object with reverse phone lookup details or an error.
     */
    invoke(phoneNumber, testType, licenseKey, isLive = true, timeoutSeconds = 15) {
        return (async () => await this.invokeAsync(
            phoneNumber, testType, licenseKey, isLive, timeoutSeconds
        ))();
    }
};

export { GetPhoneInfoClient, GPPL2Response };



export class Provider {
    constructor(data = {}) {
        this.Name = data.Name;
        this.City = data.City;
        this.State = data.State;
        this.LineType = data.LineType;
        this.Latitude = data.Latitude;
        this.Longitude = data.Longitude;
    }

    toString() {
        return `Provider: Name = ${this.Name}, City = ${this.City}, State = ${this.State}, LineType = ${this.LineType}, Latitude = ${this.Latitude}, Longitude = ${this.Longitude}`;
    }
}

export class Contacts {
    constructor(data = {}) {
        this.Name = data.Name;
        this.Address = data.Address;
        this.City = data.City;
        this.State = data.State;
        this.PostalCode = data.PostalCode;
        this.PhoneType = data.PhoneType;
        this.Latitude = data.Latitude;
        this.Longitude = data.Longitude;
        this.SICCode = data.SICCode;
        this.SICDesc = data.SICDesc;
        this.QualityScore = data.QualityScore;
    }

    toString() {
        return `Contacts: Name = ${this.Name}, Address = ${this.Address}, City = ${this.City}, State = ${this.State}, PostalCode = ${this.PostalCode}, PhoneType = ${this.PhoneType}, Latitude = ${this.Latitude}, Longitude = ${this.Longitude}, SICCode = ${this.SICCode}, SICDesc = ${this.SICDesc}, QualityScore = ${this.QualityScore}`;
    }
}

export class PhoneInfo {
    constructor(data = {}) {
        this.Provider = data.Provider ? new Provider(data.Provider) : null;
        this.Contacts = (data.Contacts || []).map(contact => new Contacts(contact));
        this.SMSAddress = data.SMSAddress;
        this.MMSAddress = data.MMSAddress;
        this.DateFirstSeen = data.DateFirstSeen;
        this.DateOfPorting = data.DateOfPorting;
        this.NoteCodes = data.NoteCodes;
        this.NoteDescriptions = data.NoteDescriptions;
        this.TokensUsed = data.TokensUsed;
    }

    toString() {
        const contactsString = this.Contacts.length
            ? this.Contacts.map(contact => contact.toString()).join(', ')
            : 'null';
        return `PhoneInfo: Provider = ${this.Provider ? this.Provider.toString() : 'null'}, Contacts = [${contactsString}], SMSAddress = ${this.SMSAddress}, MMSAddress = ${this.MMSAddress}, DateFirstSeen = ${this.DateFirstSeen}, DateOfPorting = ${this.DateOfPorting}, NoteCodes = ${this.NoteCodes}, NoteDescriptions = ${this.NoteDescriptions}, TokensUsed = ${this.TokensUsed}`;
    }
}

export class Error {
    constructor(data = {}) {
        this.Type = data.Type;
        this.TypeCode = data.TypeCode;
        this.Desc = data.Desc;
        this.DescCode = data.DescCode;
    }

    toString() {
        return `Error: Type = ${this.Type}, TypeCode = ${this.TypeCode}, Desc = ${this.Desc}, DescCode = ${this.DescCode}`;
    }
}

export class GPPL2Response {
    constructor(data = {}) {
        this.PhoneNumber = data.PhoneNumber;
        this.TestType = data.TestType;
        this.LicenseKey = data.LicenseKey;
        this.PhoneInfo = data.PhoneInfo ? new PhoneInfo(data.PhoneInfo) : null;
        this.Error = data.Error ? new Error(data.Error) : null;
    }

    toString() {
        return `GPPL2Response: PhoneNumber = ${this.PhoneNumber}, TestType = ${this.TestType}, LicenseKey = ${this.LicenseKey}, PhoneInfo = ${this.PhoneInfo ? this.PhoneInfo.toString() : 'null'}, Error = ${this.Error ? this.Error.toString() : 'null'}`;
    }
}

export default GPPL2Response;