Hi Martin,
Thank you for your response.
I wrote the below logic and it is failing at the authentication(webResponse = httpWebRequest.GetResponse();)
using Microsoft.Dynamics.ApplicationSuite.FinancialManagement.Currency.Framework;
using Microsoft.Dynamics.Currency.Instrumentation;
using System.Collections;
using System.ComponentModel.Composition;
/// <summary>
/// The <c>ExchangeRateProviderOanda</c> class is an exchange rate provider for OANDA.
/// </summary>
[ExportMetadataAttribute(enumStr(ExchangeRateProvider), ExchangeRateProvider::XE), ExportAttribute('Microsoft.Dynamics.ApplicationSuite.FinancialManagement.Currency.Framework.IExchangeRateProvider')]
class FIS_ExchangeRateProviderXE implements IExchangeRateProvider
{
private const str ServiceURL = 'xecdapi.xe.com/.../
private const ExchangeRateProviderId ProviderId = '443fe016-8542-43f9-87a7-3aa4de76c683';
private const str XEDateFormat = 'yyyy-MM-dd';
private const str HttpWebRequestMethod = 'GET';
private const str HttpWebRequestContentType = 'application/xml';
private const str HttpHeaderAuthorization = 'Authorization';
private const str KeyTokenPrefix = 'Bearer ';
private const str XPathQuote = '//response/quotes/quote';
private const str XPathAverageBid = '//bid';
private const str XPathAverageAsk = '//ask';
private const str XPathLowBid = '//low_bid';
private const str XPathLowAsk = '//low_ask';
private const str XPathMidpoint = '//midpoint';
private const str XPathDate = '//quote/date';
private const str XPathHighBid = '//high_bid';
private const str XPathHighAsk = '//high_ask';
private const str QuoteParameterAverages = 'averages';
private const str QuoteParameterLows = 'lows';
private const str QuoteParameterMidPoint = 'midpoint';
private const str QuoteParameterHighs = 'highs';
IExchangeRateProviderFrameworkFactory factory;
public ExchangeRateProviderName get_Name()
{
return "XE rates";
}
public ExchangeRateProviderId get_Id()
{
return ProviderId;
}
public void set_Factory(IExchangeRateProviderFrameworkFactory _factory)
{
factory = _factory;
}
public IExchangeRateProviderSupportedOptions GetSupportedOptions()
{
IExchangeRateProviderSupportedOptions options = factory.CreateExchangeRateProviderSupportedOptions();
options.set_doesSupportSpecificCurrencyPairs(true);
options.set_doesSupportSpecificDates(false);
options.set_fixedBaseIsoCurrency('');
options.set_singleRateForDateRange(true);
options.set_doesSupportPreventImportOnNationalHoliday(false);
options.set_DoesSupportExchangeRateFromPreviousDay(false);
return options;
}
public IExchangeRateProviderConfigDefaults GetConfigurationDefaults()
{
IExchangeRateProviderConfigDefaults configurationDefaults = factory.CreateExchangeRateProviderConfigDefaults();
configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_ServiceTimeout", '5000');
configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_OandaAPIKey", '');
configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_DecimalPlaces", '5');
configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_QuoteType", '1');
return configurationDefaults;
}
public boolean ValidateConfigurationDetail(ExchangeRateProviderPropertyKey _key, ExchangeRateProviderPropertyValue _value)
{
boolean result = true;
switch (_key)
{
case "@CurrencyExchange:Currency_ConfigField_DecimalPlaces":
int decimals = str2Int(_value);
if ((decimals > 12) || (decimals < 1))
{
CurrencyEventSource eventSource = CurrencyEventSource::Log;
eventSource.ImportExchangeRateMark("@CurrencyExchange:Currency_ConfigMessage_DecimalPlacesInvalid");
error("@CurrencyExchange:Currency_ConfigMessage_DecimalPlacesInvalid");
result = false;
}
break;
case "@CurrencyExchange:Currency_ConfigField_OandaAPIKey":
if (_value == '')
{
CurrencyEventSource eventSource = CurrencyEventSource::Log;
eventSource.ImportExchangeRateMark("A valid Key from XE Rates is required for this provider to import exchange rates.");
warning("A valid Key from XE Rates is required for this provider to import exchange rates.");
}
break;
}
return result;
}
public str EnumNameForLookup(ExchangeRateProviderPropertyKey _key)
{
if (_key == "@CurrencyExchange:Currency_ConfigField_QuoteType")
{
return enumStr(ExchangeRateProviderOANDAQuoteType);
}
return '';
}
public IExchangeRateResponse GetExchangeRates(IExchangeRateRequest _request, IExchangeRateProviderConfig _config)
{
System.Exception exception;
// cache configuration and request properties locally for performance
ExchangeRateProviderPropertyValue xeKey = _config.GetPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_OandaAPIKey");
if (xeKey == '')
{
CurrencyEventSource eventSource = CurrencyEventSource::Log;
eventSource.ImportRatesException("A valid Key from XE Rates is required for this provider to import exchange rates.", "");
throw error("A valid Key from XE Rates is required for this provider to import exchange rates.");
}
int decimalPlaces = str2Int(_config.GetPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_DecimalPlaces"));
int serviceTimeout = str2int(_config.getPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_ServiceTimeout"));
boolean singleRateForDateRange = _request.get_SingleRateForDateRange();
ExchangeRateProviderOANDAQuoteType quoteType = str2Int(_config.getPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_QuoteTypeLocked"));
str quoteTypeParameterForXE = this.getQuoteTypeParameterForURL(quoteType);
List rates = new List(Types::Real);
List dates = new List(Types::Date);
System.TimeZone localTimeZone = System.TimeZone::get_CurrentTimeZone();
System.DateTime toUTCDate = localTimeZone.ToUniversalTime(_request.get_ToDate());
str toDateForRequest = toUTCDate.ToString(XEDateFormat);
IExchangeRateResponse response = factory.CreateExchangeRateResponse();
// Iterate over the requested currency pairs. This is only required for providers
// that support specific currency pairs.
IEnumerator currencyPairsEnumerator = _request.GetEnumerator();
while(currencyPairsEnumerator.MoveNext())
{
URL xeUrl = ServiceURL;
// This loop will either execute once if singleRateForDateRange is true; otherwise, it will
// execute once for each day. If we make a single request for multiple dates
// then XE will return an average rate for the date range.
System.DateTime fromDate = _request.get_FromDate();
int compareResult = fromDate.CompareTo(_request.get_ToDate());
while (compareResult <= 0)
{
IExchangeRateRequestCurrencyPair currencyPairRequest = currencyPairsEnumerator.Current;
IExchangeRateResponseCurrencyPair currencyPairResponse = factory.CreateExchangeRateResponseCurrencyPair();
currencyPairResponse.set_FromCurrency(currencyPairRequest.get_FromCurrency());
currencyPairResponse.set_ToCurrency(currencyPairRequest.get_ToCurrency());
// All rates are requested with a display factor of 1 for this provider. If the rates
// internally are represented using a different exchange rate display factor, the
// framework will make the necessary adjustments when saving the exchange rates.
currencyPairResponse.set_ExchangeRateDisplayFactor(ExchangeRateDisplayFactor::One);
// convert to UTC which is required by XE
System.DateTime fromUTCDate = localTimeZone.ToUniversalTime(fromDate);
str fromDateForRequest = fromUTCDate.ToString(XEDateFormat);
// Build the request URL.
str xeRequestString;
if (singleRateForDateRange)
{
// getting an average rate for the date range so we invoke the service
// only once per currency pair using the from and to date
xeRequestString = strFmt(xeUrl,
currencyPairRequest.get_FromCurrency(),
currencyPairRequest.get_ToCurrency(),
fromDateForRequest,
toDateForRequest,
quoteTypeParameterForXE,
decimalPlaces);
}
else
{
// invoke the service once for each day.
xeRequestString = strFmt(xeUrl,
currencyPairRequest.get_FromCurrency(),
fromDateForRequest,
currencyPairRequest.get_ToCurrency());
}
// Configure the request for XE.
System.Net.HttpWebRequest httpWebRequest = System.Net.WebRequest::CreateHttp(xeRequestString);
httpWebRequest.set_Method(HttpWebRequestMethod);
httpWebRequest.set_ContentType(HttpWebRequestContentType);
httpWebRequest.set_Timeout(serviceTimeout);
// Authentication
System.Net.WebHeaderCollection webCollection = httpWebRequest.get_Headers();
webCollection.Add(HttpHeaderAuthorization, KeyTokenPrefix + xeKey);
try
{
// Invoke the service
System.Net.WebResponse webResponse;
webResponse = httpWebRequest.GetResponse();
// Retrieve the XML response.
System.IO.Stream stream = webResponse.GetResponseStream();
System.IO.StreamReader streamReader = new System.IO.StreamReader(stream);
str XMLOut = streamReader.ReadToEnd();
// Parse the XML to retrieve the rate and date.
this.processResult(quoteType, singleRateForDateRange, _request.get_FromDate(), XMLOut, rates, dates);
ListEnumerator rateEnumerator = rates.getEnumerator();
ListEnumerator dateEnumerator = dates.getEnumerator();
// Create the Exchange Rate Provider Response.
rateEnumerator.moveNext();
dateEnumerator.moveNext();
CurrencyExchangeRate exchangeRate = rateEnumerator.current();
date currentDate = dateEnumerator.current();
if (currentDate != dateNull() && exchangeRate)
{
IExchangeRateResponseExchangeRate exchangeRateResponse = factory.CreateExchangeRateResponseExchangeRate();
exchangeRateResponse.set_ValidFrom(currentDate);
exchangeRateResponse.set_ExchangeRate(exchangeRate);
currencyPairResponse.addExchangeRate(exchangeRateResponse);
}
}
catch(exception)
{
CurrencyEventSource eventSource = CurrencyEventSource::Log;
eventSource.ImportRatesException(exception.Message, Exception.StackTrace);
}
response.addOrUpdateCurrencyPair(currencyPairResponse);
rates = new List(Types::Real);
dates = new List(Types::Date);
fromDate = fromDate.AddDays(1);
if (singleRateForDateRange)
{
// getting an average rate across the date range so we invoke the service
// only once per currency pair
compareResult = 1;
}
else
{
compareResult = fromDate.CompareTo(_request.get_ToDate());
}
}
}
return response;
}
private str getQuoteTypeParameterForURL(ExchangeRateProviderOANDAQuoteType _quoteType)
{
str quoteTypeParameter;
switch (_quoteType)
{
case ExchangeRateProviderOANDAQuoteType::AverageAsk:
case ExchangeRateProviderOANDAQuoteType::AverageBid:
quoteTypeParameter = QuoteParameterAverages;
break;
case ExchangeRateProviderOANDAQuoteType::LowAsk:
case ExchangeRateProviderOANDAQuoteType::LowBid:
quoteTypeParameter = QuoteParameterLows;
break;
case ExchangeRateProviderOANDAQuoteType::MidPoint:
quoteTypeParameter = QuoteParameterMidPoint;
break;
case ExchangeRateProviderOANDAQuoteType::HighAsk:
case ExchangeRateProviderOANDAQuoteType::HighBid:
quoteTypeParameter = QuoteParameterHighs;
break;
}
return quoteTypeParameter;
}
private void readRate(ExchangeRateProviderOANDAQuoteType _quoteType, System.Xml.XmlNode _xmlQuoteNode, List _rates)
{
System.Xml.XmlNode xmlRateNode;
CurrencyExchangeRate exchangeRate;
str value;
// Find the exchange rate
switch (_quoteType)
{
case ExchangeRateProviderOANDAQuoteType::AverageBid:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathAverageBid);
break;
case ExchangeRateProviderOANDAQuoteType::AverageAsk:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathAverageAsk);
break;
case ExchangeRateProviderOANDAQuoteType::LowBid:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathLowBid);
break;
case ExchangeRateProviderOANDAQuoteType::LowAsk:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathLowAsk);
break;
case ExchangeRateProviderOANDAQuoteType::MidPoint:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathMidpoint);
break;
case ExchangeRateProviderOANDAQuoteType::HighBid:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathHighBid);
break;
case ExchangeRateProviderOANDAQuoteType::HighAsk:
xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathHighAsk);
break;
}
if (xmlRateNode)
{
value = xmlRateNode.get_InnerText();
exchangeRate = str2num(value);
if (exchangeRate)
{
_rates.addEnd(exchangeRate);
}
}
}
private void processResult(ExchangeRateProviderOANDAQuoteType _quoteType, boolean _singleRateForDateRange, System.DateTime _defaultDate,
str _xmlString, List _rates, List _dates)
{
System.Xml.XmlDocument xmlDom = new System.Xml.XmlDocument();
System.Xml.XmlNode xmlQuoteNode, xmlDateNode;
ValidFromDate exchangeDate;
str value;
xmlDom.LoadXml(_xmlString);
// Find the Quote
xmlQuoteNode = xmlDom.SelectSingleNode(XPathQuote);
if (xmlQuoteNode)
{
this.readRate(_quoteType, xmlQuoteNode, _rates);
// Find the date of the exchange rate.
xmlDateNode = xmlQuoteNode.SelectSingleNode(XPathDate);
if (xmlDateNode || _singleRateForDateRange)
{
if (xmlDateNode)
{
value = xmlDateNode.get_InnerText();
}
if (value)
{
// convert the date from UTC to local timezone.
exchangeDate = System.DateTime::Parse(value, System.Globalization.CultureInfo::get_CurrentUICulture(),
System.Globalization.DateTimeStyles::AssumeUniversal);
if (exchangeDate)
{
_dates.addEnd(exchangeDate);
}
}
else if (!value && _singleRateForDateRange)
{
exchangeDate = _defaultDate;
_dates.addEnd(exchangeDate);
}
}
}
}
}