Calling Restful Services Dynamically - biztalk

I have an existing orchestration which calls the services with the below configuration.
System.Diagnostics.EventLog.WriteEntry("ABC", Message_Datasheets(FILE.ReceivedFileName));
varNewSearchDataLoadURL = System.Configuration.ConfigurationManager.AppSettings["NewSearchDataLoadURL"];
varNewXmlMsg = new System.Xml.XmlDocument();
varNewXmlMsg.LoadXml(#"<path>" + Message_Datasheets(FILE.ReceivedFileName) + #"</path>");
Message_NewUnZip = varNewXmlMsg;
Message_NewUnZip(HTTP.RequestTimeout) = 3600;
Port_NewJaxMiceSearch_API(Microsoft.XLANGs.BaseTypes.Address) = varNewSearchDataLoadURL + "?path=" + Message_Datasheets(FILE.ReceivedFileName);
Port_NewJaxMiceSearch_API(Microsoft.XLANGs.BaseTypes.TransportType) = "HTTP"
Here NewSearchDataLoadURL holds the address of the webservice that needs to be called in the config file.And the path holds the received file name.So the called URI will be "http://new.abc.org/AbcSearchWebApi/api/search/loaddatafeed?path=\share01\BizTalk\data\out\20150723"
Now I have to change that to the Restful services which uses WebHttp Adapter. I am trying to follow the here
but I dont understand the BtsVariablePropertyMapping because I dont have schema that has the value to be promoted. How can I approach this.
Any help will be greatly appreciated.

So your goal is to call a Rest WebService using a dynamic port, in order to do this you need to
1 - Specifiy your type of operation
<BtsHttpUrlMapping>
<Operation Name=’MyRestGET’ Method=’GET’ Url=’/XXXX/{EmpId}’ />
</BtsHttpUrlMapping>
2 - Create and Map your variables :
"BtsVariablePropertyMapping" is a powerful BizTalk technique that enables you to define a custom variable in your url and map it to any context property using its name and namespace
So you must definitely have the property schema unless it's a BizTalk context property hence its schema is already present in BizTalk
3 - Init your dynamic port with the webservice url
In this step you assign your webservice url and transport type in your dynamic solicit response port
Microsoft.XLangs.BaseTypes.Adress = /XXXX/{EmpId}
Microsoft.XLangs.Basetypes.TransportType = "WCF-WebHTTP"
And you are good to go with this

The below code works, without using the BtsVariablePropertyMapping
System.Diagnostics.EventLog.WriteEntry("ABC", Message_Datasheets(FILE.ReceivedFileName));
varNewSearchDataLoadURL = System.Configuration.ConfigurationManager.AppSettings["NewSearchDataLoadURL"];
varNewXmlMsg = new System.Xml.XmlDocument();
varNewXmlMsg.LoadXml(#"<path>" + Message_Datasheets(FILE.ReceivedFileName) + #"</path>");
Message_NewUnZip = varNewXmlMsg;
Message_NewUnZip(WCF.HttpMethodAndUrl) = #"<BtsHttpUrlMapping><Operation Name = 'RESTGet' Method ='GET'/></BtsHttpUrlMapping>";
Port_NewSearch_API(Microsoft.XLANGs.BaseTypes.Address) = varNewSearchDataLoadURL + "?path=" + Message_Datasheets(FILE.ReceivedFileName);
Port_NewSearch_API(Microsoft.XLANGs.BaseTypes.TransportType) = "WCF-WebHttp";
Message_NewUnZip(WCF.SuppressMessageBodyForHttpVerbs) = "GET";

Related

Redirect all ASP.NET Core logging into a single NLog logger

I have an ASP.NET project that sends its logs to NLog.
However in this project, I have my own NLog logger and I would like to know how to route all the logs through it.
I guess I shouldn't add NLog as a logger, but I should find a way to register a method that will get called each time ASP tries to log anything.
How can this be accomplished?
This is the code that creates the logger:
// create the module name
var ProcessName = Process.GetCurrentProcess().ProcessName;
_ModuleName = ProcessName + " (\"" + Oracle.GuessMyName() + "\")";
// create the logger configuration
var Configuration = new LoggingConfiguration();
// create the file target
var FileTarget = new FileTarget ("file")
{
FileName = #"x:\Logs\${processname}.log",
ArchiveFileName = #"x:\Logs\${processname}.{#}.log",
Layout = #"${longdate}|${logger}|${level}|${message}${onexception:|Exception occurred:${exception:format=tostring}${newline}",
ArchiveEvery = FileArchivePeriod.Day,
ArchiveNumbering = ArchiveNumberingMode.Rolling,
MaxArchiveFiles = 7,
ConcurrentWrites = true
};
Configuration.AddTarget(FileTarget);
// create the viewer target
var ViewerTarget = new NLogViewerTarget ("viewer")
{
Layout = #"${message}${onexception:${newline} --> Exception occurred\:${exception:format=tostring}",
IncludeSourceInfo = true,
IncludeCallSite = true,
Address = #"udp://127.0.0.1:9999"
};
Configuration.AddTarget(ViewerTarget);
// set the rules
Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, FileTarget));
Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, ViewerTarget));
// set the configuration
LogManager.Configuration = Configuration;
// create a new logger
_Logger = LogManager.GetLogger(_ModuleName);
and this is also how ASP.net gets attached to nlog:
LoggerFactory.AddNLog();
Application.AddNLogWeb();
Now the current log layout looks like this for two process (the animal names are automatically changing every time the process is restarted)
so both process: shinobi and mouserun here have their own log output, but anything ASP related goes to ASP's nlog instance called Microsoft, regardless of the process.
the goal is to have the ASP output of shinobi to go in the shinobi logger and the mouserun ASP output to go in the mouserun logger.
Look at the code of NLog.Extensions.Logging, where it injects its own custom log-provider.
You can do the same and just wrap your global-logger object:
https://github.com/NLog/NLog.Extensions.Logging/blob/e48d6cc54d9abd70d976066265c7992117cbac5a/src/NLog.Extensions.Logging/NLogLoggerProvider.cs
https://github.com/NLog/NLog.Extensions.Logging/blob/1474ffe5b26d2ac95534ed01ef259133133bfb67/src/NLog.Extensions.Logging/NLogLoggerFactory.cs
https://github.com/NLog/NLog.Extensions.Logging/blob/2c05a4fbdda0fe026e60814d535e164e18786aef/src/NLog.Extensions.Logging/ConfigureExtensions.cs
public static ILoggerFactory AddNLog(this ILoggerFactory factory, NLogProviderOptions options)
{
ConfigureHiddenAssemblies();
using (var provider = new NLogLoggerProvider(options))
{
factory.AddProvider(provider);
}
return factory;
}
You could also create a custom-target, and redirect all non-global-logger messages to this target using NLog rules:
https://github.com/nlog/NLog/wiki/Configuration-file#rules
The custom target can then just forward the log-event to the global-logger:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
You should be careful with cyclic logging. Maybe have a filter in the custom-target to ignore messages from the global-logger.
But I think this is an ugly solution, and I fail to understand the restriction of only one logger-object. Especially when the reason is because it should be named after the application. Why not not a global variable for the name instead of abusing the logger-name?
Alternative you can create a custom target wrapper, that fixes the Logger on LogEventInfo's, so when forwarded to the wrapped target (UDP- / File-target), then it looks like they are all come from the same logger.
Similar to what this guy is trying to do:
https://github.com/NLog/NLog/issues/2352
Again really ugly solution, and should only be used when not able to figure out, how to avoid using the logger-name in the configuration of the wanted Nlog-targets (Ex. configure file-target-filename using something else).

are these cxf/ws-security properties set correctly? (getting TPE1122 error, "WS Security Header invalid")

We're trying to send a signed, partially encrypted SOAP message to the IRS using CXF. We think we're following all their instructions but there are ambiguities. Some of the Signature and Encryption properties could be set in several ways, but none of the permutations I've tried get us past the dreaded "TPE1122" error (WS Security Header invalid). If anyone has done this successfully, is there some property we're failing to set? I'm especially unsure about the encryption algorithm setting and whether the whole element should be encrypted or just the 3 header elements within it. Thanks.
BulkRequestTransmitterService ss = new BulkRequestTransmitterService(wsdlURL, SERVICE_NAME);
BulkRequestTransmitterPortType port = ss.getBulkRequestTransmitterPort();
org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port);
// set up MTOM
Binding binding = ((BindingProvider)port).getBinding();
((SOAPBinding)binding).setMTOMEnabled(true);
// set output properties
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put("action", "Timestamp Signature Encrypt");
outProps.put("passwordType", "PasswordDigest");
outProps.put("signatureUser", "[REDACTED]";
outProps.put(WSHandlerConstants.SIG_KEY_ID, "X509KeyIdentifier");
outProps.put("passwordCallbackClass", "UTPasswordCallback");
outProps.put("encryptionUser", "irs");
outProps.put("encryptionPropFile", "encryption.properties");
outProps.put("encryptionKeyIdentifier", "DirectReference");
outProps.put("encryptionKeyTransportAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
// ENC_SYM_ALGO: what is the default? what should correct value be? and are these two lines equivalent?
outProps.put(WSHandlerConstants.ENC_SYM_ALGO, WSConstants.TRIPLE_DES);
outProps.put("encryptionSymAlgorithm", "http://www.w3.org/2001/04/xmlenc#tripledes-cbc");
// do we encrypt each of the three signed headers, or entire Signature element? have tried it both ways
outProps.put("encryptionParts",
//"{Element}{" + WSU_NS + "}Timestamp;"
//+"{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACATransmitterManifestReqDtl;"
//+"{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACABusinessHeader;");
"{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;");
outProps.put("signaturePropFile", "signature.properties");
outProps.put("signatureAlgorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
outProps.put("signatureParts",
"{Element}{" + WSU_NS + "}Timestamp;"
+ "{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACATransmitterManifestReqDtl;"
+ "{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACABusinessHeader;");
outProps.put(WSHandlerConstants.SIG_C14N_ALGO, "http://www.w3.org/2001/10/xml-exc-c14n#WithComments");
// is "Direct Reference" preferable? have tried it both ways
outProps.put(WSHandlerConstants.ENC_KEY_ID, "X509KeyIdentifier");
outProps.put("timeToLive", "600"); // = 10 min
outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "false");
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
client.getOutInterceptors().add(wssOut);
// add gzip interceptor
GZIPOutInterceptor gz = new GZIPOutInterceptor();
gz.setForce(true);
client.getOutInterceptors().add(gz);
[ create & populate Manifest Detail here]
Header detailHeader = new Header(new QName("urn:us:gov:treasury:irs:ext:aca:air:7.0", "ACATransmitterManifestReqDtl"), aCATrnsmtManifestReqDtlType,
new JAXBDataBinding(ACATrnsmtManifestReqDtlType.class));
headers.add(detailHeader);
Header businessHeader = new Header(new QName("urn:us:gov:treasury:irs:ext:aca:air:7.0", "ACABusinessHeader"), aCABulkBusinessHeaderRequestType,
new JAXBDataBinding(ACABulkBusinessHeaderRequestType.class));
headers.add(businessHeader);
// add headers to Request
client.getRequestContext().put(Header.HEADER_LIST, headers);
// add namespaces:
Map<String, String> nsMap = new HashMap<>();
nsMap.put("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
nsMap.put("urn", "urn:us:gov:treasury:irs:ext:aca:air:7.0");
nsMap.put("urn1", "urn:us:gov:treasury:irs:common");
nsMap.put("urn2", "urn:us:gov:treasury:irs:msg:acabusinessheader");
nsMap.put("urn3", "urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter");
nsMap.put("urn4", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
client.getRequestContext().put("soap.env.ns.map", nsMap);
// then transmit, get TPE1122 error in Response
Bulit in CXF security will not work in IRS submission. You need to code your interceptors for matching IRS requirements.
I have a sample project ready, which can do submission and status requests. This is by no means a production code, but can be a starting point
https://github.com/sangramjadhav/irsclient
Please see application.yml file. You need to provide keystore and other configuration for submission to IRS
Thanks to both responders for your feedback. We have it working now and the main problem was that we were not supposed to use encryption. Everything in the sample I posted worked (including using CXF to sign certain parts of the header), but the encryption had to be removed.
It is indeed quite difficult to get everything right when transmitting to the IRS AIR system. The next hurdle was solved by changing a namespace in one of the wsdl-generated files, and then it went through, but their Linux system's value for the file size was 2 chars smaller than our Windows system's value - the culprit was the trailing CRLF.
I would be happy to answer specific questions if anyone is trying to do this as we did, using Java, Apache-CXF, and Windows.

When I call an AIF service from C# to create a SO, my XML doesn't look like I would expect

I'm experimenting with AIF to create a basic SO from C# following this general guide from Microsoft. I have two questions.
The only information I'm passing is this:
// Create instances of the entities that are used in the service and
// set the needed fields on those entities.
AxdEntity_SalesTable salesTable = new AxdEntity_SalesTable();
salesTable.CurrencyCode = "USD";
salesTable.CustAccount = "100003";
salesTable.DeliveryDate = Convert.ToDateTime("1/14/2016");
salesTable.Payment = "Net30";
salesTable.PurchOrderFormNum = "PO";
AxdEntity_SalesLine salesLine = new AxdEntity_SalesLine();
salesLine.ItemId = "44417";
salesLine.SalesQty = 3;
salesLine.SalesUnit = "ea";
Why is it that when I examine the XML, it looks like it's passing tons of extra fields:
What does this error mean? EInvoiceAccountCode appears to be a base field on SalesTable, and I tried Tools>Application Integration Framework>Update document service to update the SalesSalesOrder service.
Invalid document schema. The following error was returned: The element 'SalesTable' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder' has invalid child element 'EInvoiceAccountCode' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder'. List of possible elements expected: 'DlvTerm' in namespace 'http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder'.
1) Everything is flagged as Nillable by Ax (for several reasons). Nillable VS MinOccurs 0
2) You are passing an element "EInvoiceAccountCode" in your request. But it's expecting an "DlvTerm" element at that location.

Get ALL Mailboxes via EWS (Exchange WebServices) - not my own but also shared and group mailboxes

Can anyone provide me with a .NET (C# / VB) sample of how to get all mailboxes that I have access to ?
I have only been able to get my OWN mailbox via EWS - not ALL THE OTHER mailboxes that I also have access to through Outlook.
I don't have the names nor the id's of these mailboxes but isn't it possible to retrieve ALL mailboxes - including my own - that I am allowed to see - just as I can in Outlook ?
I am using Autodiscover to get my mailbox like this: service.AutodiscoverUrl("xxxx#ee.dd") - this will perhaps only get my own mailbox and not all the rest?
Please help !?
The way I got around this was to define the group mailbox in question as a "mailbox" object and then obtain the FolderID for the particular folder.
Define mailbox object
Mailbox gpmailbox = new Mailbox("mailbox#yourdomainname.com");
Get the FolderID (in this case, the Inbox)
FolderId gpInbox = new FolderId(WellKnownFolderName.Inbox, gpmailbox);
Use FolderID in your normal code (in this case I'm obtaining 100 messages)
ItemView view = new ItemView(100);
FindItemsResults<Item> results = hookToServer.FindItems(new FolderId(WellKnownFolderName.Inbox, gpmailbox), view);
The key is to grab the FolderID of the folder you need. Hope this helps.
Edit: I also failed to note that my object "hookToServer" is simply the ExchangeService object. Here's how I defined it:
ExchangeService hookToServer = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
hookToServer.UseDefaultCredentials = true;
hookToServer.Url = new Uri("TheExchangeServer")
I also used this as reference:
EWS 2007 Group Mailbox Guide
You can do this by Using Autodiscover to get user settings, this is a completely separate service to the one with the AutodiscoverUrl method.
The setting name you need is AlternateMailboxes, this will give a collection of all the 'other' mailboxes you have access to. You might then add the user's default mailbox to get a complete list.
In c#:
using Microsoft.Exchange.WebServices.Autodiscover; // from nuget package "Microsoft.Exchange.WebServices"
internal List<string> GetAccessibleMailboxes()
{
AutodiscoverService autodiscoverService = new AutodiscoverService("outlook.office365.com");
autodiscoverService.Credentials = networkCredential;
var userSmtpAddress = networkCredential.UserName;
GetUserSettingsResponse userresponse = autodiscoverService.GetUserSettings(
userSmtpAddress,
UserSettingName.AlternateMailboxes);
var alternateMailboxCollection = (AlternateMailboxCollection)userresponse.Settings[UserSettingName.AlternateMailboxes];
var smtpAddressList = alternateMailboxCollection.Entries.ToList().Select(a => a.SmtpAddress).ToList();
smtpAddressList.Add(userSmtpAddress);
return smtpAddressList;
}

Send parameters in order in HTTPService

I am trying to work with a simple HTTPService. The problem is that my webservice is conscious of the order of arguments it gets. I will tell the problem with an example:
var service:HTTPService = new HTTPService();
var params:Object = new Object();
params.rows = 0;
params.facet = "true";
service.send(params);
Note that in the above code I have mentioned the parameter rows before facet, but the url I recieve is facet=true&rows=0. So I recieve the argument rows before facet and hence my webservice does not work. I figured out that the contents of array is always sent in alphabetical order, which I dont want.
Is there any way I can achieve explict ordering of parameters sent?
Note that I am not in power of changing the logic of webservice(its basically a RPC service supporting both desktop and web client).
Thanks.
I am assuming you are using a get method. Instead of passing params to the HTTPService, build a url string. You can pass get params just by changing that string then calling the service.
service.url = "originalURL" + "?" + "rows=0" + "&" + "facet=true";
service.send();

Resources