-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #82 from InRule/issue/CAD-327-rest-operation-overr…
…ides CAD-327: Allow overriding the headers and body on a REST operation
- Loading branch information
Showing
16 changed files
with
923 additions
and
40 deletions.
There are no files selected for viewing
39 changes: 0 additions & 39 deletions
39
Developer Samples/Runtime Overrides/Runtime Overrides CI CD example Script.md
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
# Runtime Overrides | ||
|
||
Runtime overrides may override End Point and Data Element settings that differ from what was originally authored in a Rule Application. | ||
This can be useful when deploying an application to different environments. | ||
|
||
Overrides may be specified either using irSDK from the `RuleSession.Overrides` property, or via .config file `<appSettings/>`. | ||
|
||
The following End Points may be overridden: | ||
- Database Connection | ||
- Rest Service | ||
- Web Service | ||
- SendMail Service | ||
- XML Schema | ||
|
||
The following Data Elements may be overridden: | ||
- Inline Value List | ||
- Inline Table | ||
- Inline XML Document | ||
- SQL Query | ||
- XPath Query | ||
- REST Operation | ||
|
||
--- | ||
## irSDK Override Mechanism | ||
When creating a RuleSession for executing rules, overrides may be specified prior to execution. | ||
|
||
For example: | ||
|
||
``` | ||
using (RuleSession session = new RuleSession(ruleApp)) | ||
{ | ||
session.Overrides.OverrideRestOperationUriTemplate("operation1", "api/v2/invoice"); | ||
session.CreateEntity("Invoice"); | ||
session.ApplyRules(); | ||
} | ||
``` | ||
|
||
--- | ||
## Configuration Override Mechanism | ||
Using the `<appSettings/>` in the application's .config file, overrides may be applied declaratively instead of adding irSDK code. | ||
|
||
These `<appSettings/>` may be extended using [Configuration Builders](https://github.com/aspnet/MicrosoftConfigurationBuilders) to read configuration from external sources such as Environment Variables, JSON files, or Azure KeyVault. | ||
|
||
The `<appSettings/>` follow a key/value pattern. The key is the identifier of the override, and the value is the new value for the Runtime to use. | ||
|
||
For example: | ||
|
||
``` | ||
<configuration> | ||
<appSettings> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:uritemplate" value="api/v2/test" /> | ||
</appSettings> | ||
</configuration> | ||
``` | ||
|
||
This illustrates the equivalent of the irSDK override example listed above. | ||
|
||
### Key Format | ||
The appSetting key follows the following format: | ||
|
||
`inrule:runtime:overrides:<name>:<type>:<property>` | ||
|
||
- `<name>` is the name of the End Point or Data Element | ||
- `<type>` is the type of End Point or Data Element (see list below) | ||
- `<property>` is the property of the type being overridden (see list below) | ||
|
||
**Note: These overrides will apply to End Points and Data Elements with the same name across different Rule Applications.** | ||
|
||
The `<name>` component is **case-sensitive**. | ||
|
||
The `<type>` and `<property>` components are **not case-sensitive**. The combination of type/property may be selected from the following: | ||
|
||
### End Points | ||
| Type | Property | | ||
| ---- | -------- | | ||
| DatabaseConnection | ConnectionString | | ||
| SendMailServer | ServerAddress | | ||
| XmlDocument | XmlPath | | ||
| XmlSchema | XsdPath | | ||
| | EnableXsdValidation | | ||
| WebService | WsdlUrl | | ||
| | ServiceUriOverride | | ||
| | MaxReceivedMessageSize | | ||
| RestService | RootUrl | | ||
| | AllowUntrustedCertificates | | ||
| | AuthenticationType | | ||
| | Username | | ||
| | Password | | ||
| | Domain | | ||
| | X509CertificatePath | | ||
| | X509CertificatePassword | | ||
|
||
### Data Elements | ||
|
||
| Name | Property | | ||
| ---- | -------- | | ||
| ValueList | ValueListItems* | | ||
| Table | TableSettings* | | ||
| XPathDocument | InlineXml | | ||
| SqlQuery | Query | | ||
| RestOperation | UriTemplate | | ||
| | Body | | ||
| | Headers* | | ||
|
||
_*see special handling below_ | ||
|
||
**Note: All appSetting values must be HTML-safe, so any values containing characters such as '&', '<', '>', '\\"' should first be passed through an HTML encoder.** | ||
|
||
For example: | ||
|
||
``` | ||
const string unencodedValue = @"<InlineXml><Value1>This is a \"test\" & should be encoded</Value1></InlineXml>"; | ||
Console.WriteLine("Unencoded Value: " + unencodedValue); | ||
Console.WriteLine("Encoded Value: " + System.Net.WebUtility.HtmlEncode(unencodedValue)); | ||
``` | ||
|
||
The above should output the following: | ||
|
||
``` | ||
Unencoded Value: <InlineXml><Value1>This is a "test" & should be encoded</Value1></InlineXml> | ||
Encoded Value: <InlineXml><Value1>This is a "test" & should be encoded</Value1></InlineXml> | ||
``` | ||
|
||
#### ValueListItems Serialization | ||
The value for ValueListItems uses a custom XML format, which must be HTML encoded for use as the value. | ||
|
||
For example: | ||
|
||
``` | ||
const string valueListItems = | ||
@"<ValueListItems> | ||
<ValueListItem> | ||
<Value>Value1</Value> | ||
<DisplayText>Value One</DisplayText> | ||
</ValueListItem> | ||
<ValueListItem> | ||
<Value>Value2</Value> | ||
</ValueListItem> | ||
<ValueListItem> | ||
<Value>Value3</Value> | ||
</ValueListItem> | ||
</ValueListItems>"; | ||
var value = System.Net.WebUtility.HtmlEncode(valueListItems); | ||
``` | ||
|
||
#### TableSettings Serialization | ||
The value of TableSettings configuration must use irSDK to XML Serialize an instance of `InRule.Repository.TableSettings`. This must then be HTML encoded for use as the value. | ||
|
||
For example: | ||
|
||
``` | ||
var tableSettings = ruleAppDef.DataElements["table1"].TableSettings; | ||
var xs = new XmlSerializer(typeof("InRule.Repository.TableSettings)); | ||
var sb = new StringBuilder(); | ||
using (var writer = new StringWriter(sb)) | ||
{ | ||
xs.Serialize(writer, tableSettings); | ||
} | ||
var value = System.Net.WebUtility.HtmlEncode(sb.ToString()); | ||
``` | ||
|
||
#### REST Operation Headers | ||
The Headers are a collection of name/value pairs. Any number of headers may be applied. To simulate a collection, the appSetting key is extended to include the header name. | ||
|
||
For example: | ||
``` | ||
<configuration> | ||
<appSettings> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:Headers:Header1" value="value1" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:Headers:Header2" value="value2" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:Headers:Header3" value="value3" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:Headers:Connection" value="keep-alive" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:Headers:Accept-Language" value="en-US" /> | ||
</appSettings> | ||
</configuration> | ||
``` | ||
|
||
--- | ||
## Samples | ||
The 2 samples illustrate how the REST operation may be overridden by both irSDK and .config `<appSettings/>`: | ||
- [RuntimeOverridesViaSdk](RuntimeOverridesViaSdk/) | ||
- [RuntimeOverridesViaConfig](RuntimeOverridesViaConfig/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.0.32126.317 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuntimeOverridesViaSdk", "RuntimeOverridesViaSdk\RuntimeOverridesViaSdk.csproj", "{5E9273F5-548A-4813-9499-5201B2160060}" | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuntimeOverridesViaConfig", "RuntimeOverridesViaConfig\RuntimeOverridesViaConfig.csproj", "{AD7153D5-AD79-4928-AF2E-41CCAD584EE5}" | ||
EndProject | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{55B8A9D2-B845-469F-B26D-EAAB2866E33F}" | ||
ProjectSection(SolutionItems) = preProject | ||
README.md = README.md | ||
EndProjectSection | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{5E9273F5-548A-4813-9499-5201B2160060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{5E9273F5-548A-4813-9499-5201B2160060}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{5E9273F5-548A-4813-9499-5201B2160060}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{5E9273F5-548A-4813-9499-5201B2160060}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{AD7153D5-AD79-4928-AF2E-41CCAD584EE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{AD7153D5-AD79-4928-AF2E-41CCAD584EE5}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{AD7153D5-AD79-4928-AF2E-41CCAD584EE5}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{AD7153D5-AD79-4928-AF2E-41CCAD584EE5}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {FCBB9BA7-F634-45F4-9797-72E3AA9A6F7A} | ||
EndGlobalSection | ||
EndGlobal |
27 changes: 27 additions & 0 deletions
27
Developer Samples/RuntimeOverrides/RuntimeOverridesViaConfig/App.config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<configuration> | ||
<startup> | ||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> | ||
</startup> | ||
|
||
<appSettings> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:uritemplate" value="api/vConfig/test" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:body" value="{ 'Value': 'configBody' }" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:headers:Header1" value="configHeader1" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:headers:Header2" value="configHeader2" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:headers:Header3" value="configHeader3" /> | ||
<add key="inrule:runtime:overrides:operation1:RestOperation:headers:User-Agent" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0" /> | ||
</appSettings> | ||
<runtime> | ||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | ||
<dependentAssembly> | ||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> | ||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" /> | ||
</dependentAssembly> | ||
<dependentAssembly> | ||
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" /> | ||
<bindingRedirect oldVersion="0.0.0.0-1.0.115.5" newVersion="1.0.115.5" /> | ||
</dependentAssembly> | ||
</assemblyBinding> | ||
</runtime> | ||
</configuration> |
86 changes: 86 additions & 0 deletions
86
Developer Samples/RuntimeOverrides/RuntimeOverridesViaConfig/InProcessWebServer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System; | ||
using System.Net; | ||
|
||
namespace RuntimeOverridesViaConfig | ||
{ | ||
public sealed class InProcessWebServer : IDisposable | ||
{ | ||
private const int HttpPort = 9595; | ||
|
||
private readonly Action<HttpListenerContext> _processRequest; | ||
private readonly HttpListener _httpListener; | ||
|
||
public InProcessWebServer(Action<HttpListenerContext> processRequest) | ||
{ | ||
_processRequest = processRequest; | ||
_httpListener = new HttpListener { AuthenticationSchemes = AuthenticationSchemes.Anonymous }; | ||
_httpListener.Prefixes.Add(RootUrl); | ||
_httpListener.Start(); | ||
Result = _httpListener.BeginGetContext(WebRequestCallback, _httpListener); | ||
} | ||
|
||
public static string RootUrl => $"http://localhost:{HttpPort}/"; | ||
|
||
public IAsyncResult Result { get; private set; } | ||
|
||
public HttpListenerRequest Request { get; private set; } | ||
|
||
public void Dispose() | ||
{ | ||
if (_httpListener == null) return; | ||
|
||
lock (_httpListener) | ||
{ | ||
try | ||
{ | ||
((IDisposable)_httpListener).Dispose(); | ||
} | ||
catch | ||
{ | ||
} | ||
} | ||
} | ||
|
||
private void WebRequestCallback(IAsyncResult result) | ||
{ | ||
// Avoid accessing HttpListener while/after it has been disposed | ||
lock (_httpListener) | ||
{ | ||
if (!_httpListener.IsListening) return; | ||
|
||
try | ||
{ | ||
HttpListenerContext context = _httpListener.EndGetContext(result); | ||
|
||
Request = context.Request; | ||
|
||
try | ||
{ | ||
if (_processRequest == null) | ||
{ | ||
context.Response.StatusCode = (int)HttpStatusCode.OK; | ||
context.Response.Headers.Add(HttpResponseHeader.ContentType, "text/plain"); | ||
} | ||
else | ||
{ | ||
_processRequest(context); | ||
} | ||
} | ||
finally | ||
{ | ||
context.Response.OutputStream.Close(); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine($"[Server] - Error: {ex.Message}"); | ||
} | ||
finally | ||
{ | ||
// Set up the next context | ||
Result = _httpListener.BeginGetContext(WebRequestCallback, _httpListener); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.