- C#
- Python
- NodeJS
GeoPhone C# Code Snippet
using System;
using GPService;
namespace geophone_dot_net.SOAP
{
/// <summary>
/// Provides functionality to call the ServiceObjects GeoPhone SOAP service's GetPhoneInfo_V2 operation,
/// retrieving phone-related information (e.g., provider and contact details) with fallback to a backup endpoint
/// for reliability in live mode.
/// </summary>
public class GetPhoneInfoValidation
{
private const string LiveBaseUrl = "https://sws.serviceobjects.com/GP/SOAP.svc/SOAP";
private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/GP/SOAP.svc/SOAP";
private const string TrailBaseUrl = "https://trial.serviceobjects.com/GP/SOAP.svc/SOAP";
private readonly string _primaryUrl;
private readonly string _backupUrl;
private readonly int _timeoutMs;
private readonly bool _isLive;
/// <summary>
/// Initializes URLs/timeout/IsLive.
/// </summary>
public GetPhoneInfoValidation(bool isLive)
{
// Read timeout (milliseconds) and IsLive flag
_timeoutMs = 10000;
_isLive = isLive;
if (_isLive)
{
_primaryUrl = LiveBaseUrl;
_backupUrl = BackupBaseUrl;
}
else
{
_primaryUrl = TrailBaseUrl;
_backupUrl = TrailBaseUrl;
}
if (string.IsNullOrWhiteSpace(_primaryUrl))
throw new InvalidOperationException("Primary URL not set.");
if (string.IsNullOrWhiteSpace(_backupUrl))
throw new InvalidOperationException("Backup URL not set.");
}
/// <summary>
/// Asynchronously calls the GetPhoneInfo_V2 SOAP endpoint to retrieve phone information,
/// attempting the primary endpoint first and falling back to the backup if the response is invalid
/// (Error.Number == "4") in live mode or if the primary call fails.
/// </summary>
/// <param name="phoneNumber">The phone number in question.</param>
/// <param name="licenseKey">Your ServiceObjects GeoPhone license key</param>
/// <returns>A <see cref="Task{TResult}"/> containing a <see cref="PhoneInfo_V2"/> object with provider and contact details or an error.</returns>
/// <exception cref="Exception">
/// Thrown if both primary and backup endpoints fail.
/// </exception>
public async Task<PhoneInfo_V2> InvokeAsync(string phoneNumber, string licenseKey)
{
SOAPClient clientPrimary = null;
SOAPClient clientBackup = null;
try
{
// Attempt Primary_PLL
clientPrimary = new SOAPClient();
clientPrimary.Endpoint.Address = new System.ServiceModel.EndpointAddress(_primaryUrl);
clientPrimary.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);
PhoneInfo_V2 response = await clientPrimary.GetPhoneInfo_V2Async(phoneNumber, licenseKey).ConfigureAwait(false);
if(response == null || (response.Error != null && response.Error.Number == "4"))
{
throw new InvalidOperationException("Primary endpoint returned null or a fatal Number=4 error for PhoneInfo_V2");
}
return response;
}
catch (Exception primaryEx)
{
// If primary fails, try Backup
try
{
clientBackup =new SOAPClient();
clientBackup.Endpoint.Address = new System.ServiceModel.EndpointAddress(_backupUrl);
clientBackup.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);
PhoneInfo_V2 response = await clientBackup.GetPhoneInfo_V2Async(phoneNumber, licenseKey).ConfigureAwait(false);
return response;
}
catch (Exception backupEx)
{
// If backup also fails, wrap both exceptions
throw new Exception(
$"Both primary and backup endpoints failed.\n" +
$"Primary error: {primaryEx.Message}\n" +
$"Backup error: {backupEx.Message}");
}
finally
{
clientBackup?.Close();
}
}
finally
{
clientPrimary?.Close();
}
}
}
}
GeoPhone Python Code Snippet
from suds.client import Client
from suds import WebFault
from suds.sudsobject import Object
class GetPhoneInfoValidation:
def __init__(self, license_key: str, is_live: bool, timeout_ms: int = 10000):
"""
license_key: Service Objects GeoPhone license key.
is_live: whether to use live or trial endpoints
timeout_ms: SOAP call timeout in milliseconds
"""
self._timeout_s = timeout_ms / 1000.0
self._is_live = is_live
self.license_key = license_key
# WSDL URLs
self._primary_wsdl = (
"https://sws.serviceobjects.com/gp/soap.svc?wsdl"
if is_live else
"https://trial.serviceobjects.com/gp/soap.svc?wsdl"
)
self._backup_wsdl = (
"https://swsbackup.serviceobjects.com/gp/soap.svc?wsdl"
if is_live else
"https://trial.serviceobjects.com/gp/soap.svc?wsdl"
)
def get_phone_info(self, phone_number: str) -> Object:
"""
Calls GetPhoneInfo_V2 on the primary endpoint; on None response,
WebFault, or Error.Number == '4' falls back to the backup endpoint.
returns: the suds response object
raises RuntimeError: if both endpoints fail
"""
# Common kwargs for both calls
call_kwargs = dict(
PhoneNumber=phone_number,
LicenseKey=self.license_key
)
# Attempt primary
try:
client = Client(self._primary_wsdl, timeout=self._timeout_s)
# Override endpoint URL if needed:
response = client.service.GetPhoneInfo_V2(**call_kwargs)
# If response is None or fatal error code, trigger fallback
if response is None or (hasattr(response, 'Error') and response.Error and response.Error.Number == '4'):
raise ValueError("Primary returned no result or fatal Error.Number=4")
return response
except (WebFault, ValueError, Exception) as primary_ex:
# Attempt backup
try:
client = Client(self._backup_wsdl, timeout=self._timeout_s)
response = client.service.GetPhoneInfo_V2(**call_kwargs)
if response is None:
raise ValueError("Backup returned no result")
return response
except (WebFault, Exception) as backup_ex:
msg = (
"Both primary and backup endpoints failed.\n"
f"Primary error: {str(primary_ex)}\n"
f"Backup error: {str(backup_ex)}"
)
raise RuntimeError(msg)
GeoPhone NodeJS Code Snippet
import { soap } from "strong-soap";
/**
* <summary>
* A class that provides functionality to call the ServiceObjects GeoPhone SOAP service's GetPhoneInfo_V2 endpoint,
* retrieving phone-related information with fallback to a backup endpoint for reliability in live mode.
* </summary>
*/
class GetPhoneInfoSoap {
/**
* <summary>
* Initializes a new instance of the GetPhoneInfoSoap class with the provided input parameters,
* setting up primary and backup WSDL URLs based on the live/trial mode.
* </summary>
* <param name="input" type="Object">The input object containing phoneNumber, licenseKey, isLive, and timeoutSeconds.</param>
* <exception cref="Error">Thrown if phoneNumber, licenseKey, primaryWsdl, or backupWsdl is empty or null.</exception>
*/
constructor(phoneNumber, licenseKey, isLive, timeoutSeconds) {
if (!phoneNumber) throw new Error("PhoneNumber cannot be empty or null.");
if (!licenseKey) throw new Error("LicenseKey cannot be empty or null.");
this.phoneNumber = phoneNumber;
this.licenseKey = licenseKey;
this.isLive = isLive;
this.timeoutSeconds = timeoutSeconds;
this.liveBaseUrl = "https://sws.serviceobjects.com/gp/soap.svc?wsdl";
this.backupBaseUrl = "https://swsbackup.serviceobjects.com/gp/soap.svc?wsdl";
this.trailBaseUrl = "https://trial.serviceobjects.com/gp/soap.svc?wsdl";
this._primaryWsdl = this.isLive ? this.liveBaseUrl : this.trailBaseUrl;
this._backupWsdl = this.isLive ? this.backupBaseUrl : this.trailBaseUrl;
if (!this._primaryWsdl) throw new Error("Primary WSDL URL is not set.");
if (!this._backupWsdl) throw new Error("Backup WSDL URL is not set.");
}
/**
* <summary>
* Asynchronously calls the GetPhoneInfo_V2 SOAP endpoint, attempting the primary endpoint
* first and falling back to the backup if the response is invalid (Error.Number == "4") in live mode
* or if the primary call fails.
* </summary>
* <returns type="Promise<GPResponse>">A promise that resolves to a GPResponse object with provider and contact details or an error.</returns>
* <exception cref="Error">Thrown if both primary and backup calls fail, with details of both errors.</exception>
*/
async getPhoneInfo() {
const args = {
PhoneNumber: this.phoneNumber,
LicenseKey: this.licenseKey,
};
try {
const primaryResult = await this._callSoap(this._primaryWsdl, args);
if (this._isLive && !this._isValid(primaryResult)) {
console.warn(
"Primary returned Error.Number == '4', falling back to backup..."
);
const backupResult = await this._callSoap(this._backupWsdl, args);
return backupResult;
}
return primaryResult;
} catch (primaryErr) {
try {
const backupResult = await this._callSoap(this._backupWsdl, args);
return backupResult;
} catch (backupErr) {
throw new Error(
`Both primary and backup calls failed:\nPrimary: ${primaryErr.message}\nBackup: ${backupErr.message}`
);
}
}
}
/**
* <summary>
* Performs a SOAP service call to the specified WSDL URL with the given arguments,
* creating a client and processing the response into a GPResponse object.
* </summary>
* <param name="wsdlUrl" type="string">The WSDL URL of the SOAP service endpoint (primary or backup).</param>
* <param name="args" type="Object">The arguments to pass to the GetPhoneInfo_V2 method (e.g., PhoneNumber, LicenseKey).</param>
* <returns type="Promise<GPResponse>">A promise that resolves to a GPResponse object containing the SOAP response data.</returns>
* <exception cref="Error">Thrown if the SOAP client creation fails, the service call fails, or the response cannot be parsed.</exception>
*/
_callSoap(wsdlUrl, args) {
return new Promise((resolve, reject) => {
soap.createClient(wsdlUrl, { timeout: this.timeoutSeconds * 1000 }, (err, client) => {
if (err) return reject(err);
client.GetPhoneInfo_V2(args, (err, result) => {
if (err) return reject(err);
const rawData = result.GetPhoneInfo_V2Result;
try {
if (!rawData) {
return reject(new Error("SOAP response is empty or undefined."));
}
resolve(rawData);
} catch (parseErr) {
reject(new Error(`Failed to parse SOAP response: ${parseErr.message}`));
}
});
});
});
}
/**
* <summary>
* Checks if a SOAP response is valid by verifying that it exists and either has no Error object
* or the Error.Number is not equal to '4'.
* </summary>
* <param name="response" type="GPResponse">The GPResponse object to validate.</param>
* <returns type="boolean">True if the response is valid, false otherwise.</returns>
*/
_isValid(response) {
return response && (!response.Error || response.Error.Number !== "4");
}
}
export { GetPhoneInfoSoap };