-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathEncContentSapMessageBuilder.cs
418 lines (362 loc) · 21.3 KB
/
EncContentSapMessageBuilder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
using System.Xml;
using Microsoft.Extensions.Options;
using UKHO.ERPFacade.Common.IO;
using UKHO.ERPFacade.Common.Logging;
using UKHO.ERPFacade.Common.Models;
using UKHO.ERPFacade.Common.Providers;
using UKHO.ERPFacade.Common.PermitDecryption;
using UKHO.ERPFacade.Common.Exceptions;
using UKHO.ERPFacade.Common.Constants;
namespace UKHO.ERPFacade.API.Helpers
{
public class EncContentSapMessageBuilder : IEncContentSapMessageBuilder
{
private readonly ILogger<EncContentSapMessageBuilder> _logger;
private readonly IXmlHelper _xmlHelper;
private readonly IFileSystemHelper _fileSystemHelper;
private readonly IOptions<SapActionConfiguration> _sapActionConfig;
private readonly IWeekDetailsProvider _weekDetailsProvider;
private readonly IPermitDecryption _permitDecryption;
public EncContentSapMessageBuilder(ILogger<EncContentSapMessageBuilder> logger,
IXmlHelper xmlHelper,
IFileSystemHelper fileSystemHelper,
IOptions<SapActionConfiguration> sapActionConfig,
IWeekDetailsProvider weekDetailsProvider,
IPermitDecryption permitDecryption
)
{
_logger = logger;
_xmlHelper = xmlHelper;
_fileSystemHelper = fileSystemHelper;
_sapActionConfig = sapActionConfig;
_weekDetailsProvider = weekDetailsProvider;
_permitDecryption = permitDecryption;
}
/// <summary>
/// Generate SAP message xml file.
/// </summary>
/// <param name="eventData"></param>
/// <returns>XmlDocument</returns>
public XmlDocument BuildSapMessageXml(EncEventPayload eventData)
{
string sapXmlTemplatePath = Path.Combine(Environment.CurrentDirectory, Constants.S57SapXmlTemplatePath);
// Check if SAP XML payload template exists
if (!_fileSystemHelper.IsFileExists(sapXmlTemplatePath))
{
throw new ERPFacadeException(EventIds.SapXmlTemplateNotFound.ToEventId(), "The SAP XML payload template does not exist.");
}
var soapXml = _xmlHelper.CreateXmlDocument(sapXmlTemplatePath);
var actionItemNode = soapXml.SelectSingleNode(Constants.XpathActionItems);
_logger.LogInformation(EventIds.GenerationOfSapXmlPayloadStarted.ToEventId(), "Generation of SAP XML payload started.");
// Build SAP actions for ENC Cell
BuildEncCellActions(eventData, soapXml, actionItemNode);
// Build SAP actions for Units
BuildUnitActions(eventData, soapXml, actionItemNode);
var xmlNode = SortXmlPayload(actionItemNode);
soapXml.SelectSingleNode(Constants.XpathCorrId).InnerText = eventData.Data.CorrelationId;
soapXml.SelectSingleNode(Constants.XpathNoOfActions).InnerText = xmlNode.ChildNodes.Count.ToString();
soapXml.SelectSingleNode(Constants.XpathRecDate).InnerText = DateTime.UtcNow.ToString(Constants.RecDateFormat);
soapXml.SelectSingleNode(Constants.XpathRecTime).InnerText = DateTime.UtcNow.ToString(Constants.RecTimeFormat);
var IM_MATINFONode = soapXml.SelectSingleNode(Constants.XpathImMatInfo);
IM_MATINFONode.AppendChild(xmlNode);
_logger.LogInformation(EventIds.GenerationOfSapXmlPayloadCompleted.ToEventId(), "Generation of SAP XML payload completed.");
return soapXml;
}
private void BuildEncCellActions(EncEventPayload eventData, XmlDocument soapXml, XmlNode actionItemNode)
{
_logger.LogInformation(EventIds.EncCellSapActionGenerationStarted.ToEventId(), "Building ENC cell SAP actions.");
foreach (var product in eventData.Data.Products)
{
foreach (var action in _sapActionConfig.Value.SapActions.Where(x => x.Product == Constants.EncCell))
{
var unitOfSale = GetUnitOfSale(action.ActionNumber, eventData.Data.UnitsOfSales, product);
if (!ValidateActionRules(action, product))
continue;
switch (action.ActionNumber)
{
case 1://CREATE ENC CELL
case 10://CANCEL ENC CELL
if (unitOfSale is null)
{
throw new ERPFacadeException(EventIds.UnitOfSaleNotFoundException.ToEventId(), $"Required unit not found in event payload to generate {action.Action} action for {product.ProductName}.");
}
BuildAndAppendActionNode(soapXml, product, unitOfSale, action, eventData, actionItemNode, product.ProductName);
break;
case 4://REPLACED WITH ENC CELL
if (product.ReplacedBy.Any() && unitOfSale is null)
{
throw new ERPFacadeException(EventIds.UnitOfSaleNotFoundException.ToEventId(), $"Required unit not found in event payload to generate {action.Action} action for {product.ProductName}.");
}
foreach (var replacedProduct in product.ReplacedBy)
{
BuildAndAppendActionNode(soapXml, product, unitOfSale, action, eventData, actionItemNode, product.ProductName, replacedProduct);
}
break;
case 5://ADDITIONAL COVERAGE ENC CELL
foreach (var additionalCoverageProduct in product.AdditionalCoverage)
{
BuildAndAppendActionNode(soapXml, product, null, action, eventData, actionItemNode, product.ProductName, additionalCoverageProduct);
}
break;
case 6://CHANGE ENC CELL
case 8://UPDATE ENC CELL EDITION UPDATE NUMBER
if (unitOfSale is not null)
BuildAndAppendActionNode(soapXml, product, unitOfSale, action, eventData, actionItemNode, product.ProductName);
break;
}
}
}
}
private void BuildUnitActions(EncEventPayload eventData, XmlDocument soapXml, XmlNode actionItemNode)
{
foreach (var unitOfSale in eventData.Data.UnitsOfSales)
{
foreach (var action in _sapActionConfig.Value.SapActions.Where(x => x.Product == Constants.AvcsUnit))
{
if (!ValidateActionRules(action, unitOfSale))
continue;
switch (action.ActionNumber)
{
case 2://CREATE AVCS UNIT OF SALE
case 7://CHANGE AVCS UNIT OF SALE
case 11://CANCEL AVCS UNIT OF SALE
BuildAndAppendActionNode(soapXml, null, unitOfSale, action, eventData, actionItemNode);
break;
case 3://ASSIGN CELL TO AVCS UNIT OF SALE
foreach (var addProduct in unitOfSale.CompositionChanges.AddProducts)
{
BuildAndAppendActionNode(soapXml, null, unitOfSale, action, eventData, actionItemNode, addProduct, null);
}
break;
case 9://REMOVE ENC CELL FROM AVCS UNIT OF SALE
foreach (var removeProduct in unitOfSale.CompositionChanges.RemoveProducts)
{
BuildAndAppendActionNode(soapXml, null, unitOfSale, action, eventData, actionItemNode, removeProduct, null);
}
break;
}
}
}
}
/// <summary>
/// Returns primary unit of sale for given product to get ProductName for ENC cell SAP actions.
/// </summary>
/// <param name="actionNumber"></param>
/// <param name="listOfUnitOfSales"></param>
/// <param name="product"></param>
/// <returns></returns>
private UnitOfSale? GetUnitOfSale(int actionNumber, List<UnitOfSale> listOfUnitOfSales, Product product)
{
return actionNumber switch
{
//Case 1 : CREATE ENC CELL
1 => listOfUnitOfSales.FirstOrDefault(x => x.UnitOfSaleType == Constants.UnitSaleType &&
x.Status == Constants.UnitOfSaleStatusForSale &&
x.CompositionChanges.AddProducts.Contains(product.ProductName)),
//Case 4 : REPLACED WITH ENC CELL
//Case 10 : CANCEL ENC CELL
4 or 10 => listOfUnitOfSales.FirstOrDefault(x => x.UnitOfSaleType == Constants.UnitSaleType &&
x.CompositionChanges.RemoveProducts.Contains(product.ProductName)),
//Case 6 : CHANGE ENC CELL
//Case 8 : UPDATE ENC CELL EDITION UPDATE NUMBER
6 or 8 => listOfUnitOfSales.FirstOrDefault(x => x.UnitOfSaleType == Constants.UnitSaleType &&
x.Status == Constants.UnitOfSaleStatusForSale &&
product.InUnitsOfSale.Contains(x.UnitName)),
_ => null,
};
}
/// <summary>
/// Returns true if given product/unit satisfies rules for given action.
/// </summary>
/// <param name="action"></param>
/// <param name="obj"></param>
/// <returns></returns>
private bool ValidateActionRules(SapAction action, object obj)
{
bool isConditionSatisfied = false;
//Return true if no rules for SAP action.
if (action.Rules == null!) return true;
foreach (var rules in action.Rules)
{
foreach (var conditions in rules.Conditions)
{
object jsonFieldValue = CommonHelper.ParseXmlNode(conditions.AttributeName, obj, obj.GetType());
if (jsonFieldValue != null! && jsonFieldValue.ToString() == conditions.AttributeValue)
{
isConditionSatisfied = true;
}
else
{
isConditionSatisfied = false;
break;
}
}
if (isConditionSatisfied) break;
}
return isConditionSatisfied;
}
private void BuildAndAppendActionNode(XmlDocument soapXml, Product product, UnitOfSale unitOfSale, SapAction action, EncEventPayload eventData, XmlNode actionItemNode, string childCell = null, string replacedBy = null)
{
_logger.LogInformation(EventIds.BuildingSapActionStarted.ToEventId(), "Building SAP action {ActionName}.", action.Action);
var actionNode = BuildAction(soapXml, product, unitOfSale, action, eventData.Data.UkhoWeekNumber, childCell, replacedBy);
actionItemNode.AppendChild(actionNode);
_logger.LogInformation(EventIds.SapActionCreated.ToEventId(), "SAP action {ActionName} created.", action.Action);
}
private XmlElement BuildAction(XmlDocument soapXml, Product product, UnitOfSale unitOfSale, SapAction action, UkhoWeekNumber ukhoWeekNumber, string childCell, string replacedBy = null)
{
DecryptedPermit decryptedPermit = null;
// Create main item node
var itemNode = soapXml.CreateElement(Constants.Item);
// Add basic action-related nodes
AppendChildNode(itemNode, soapXml, Constants.ActionNumber, action.ActionNumber.ToString());
AppendChildNode(itemNode, soapXml, Constants.Action, action.Action.ToString());
AppendChildNode(itemNode, soapXml, Constants.Product, action.Product.ToString());
AppendChildNode(itemNode, soapXml, Constants.ProdType, Constants.ProdTypeValue);
// Add child cell node
AppendChildNode(itemNode, soapXml, Constants.ChildCell, childCell);
List<(int sortingOrder, XmlElement node)> actionAttributes = new();
// Get permit keys for New cell and Updated cell
if (action.Action == Constants.CreateEncCell || action.Action == Constants.UpdateCell)
{
decryptedPermit = _permitDecryption.Decrypt(product.Permit);
}
// Process ProductSection attributes
ProcessAttributes(action.Action, action.Attributes.Where(x => x.Section == Constants.ProductSection), soapXml, product, actionAttributes, decryptedPermit, replacedBy);
// Process UnitOfSaleSection attributes
ProcessAttributes(action.Action, action.Attributes.Where(x => x.Section == Constants.UnitOfSaleSection), soapXml, unitOfSale, actionAttributes, null, null);
// Process UkhoWeekNumberSection attributes
ProcessUkhoWeekNumberAttributes(action.Action, action.Attributes.Where(x => x.Section == Constants.UkhoWeekNumberSection), soapXml, ukhoWeekNumber, actionAttributes);
// Sort and append attributes to SAP action
foreach (var (sortingOrder, node) in actionAttributes.OrderBy(x => x.sortingOrder))
{
itemNode.AppendChild(node);
}
return itemNode;
}
private void AppendChildNode(XmlElement parentNode, XmlDocument doc, string nodeName, string value)
{
var childNode = doc.CreateElement(nodeName);
childNode.InnerText = value;
parentNode.AppendChild(childNode);
}
private void ProcessAttributes(string action, IEnumerable<ActionItemAttribute> attributes, XmlDocument soapXml, object source, List<(int, XmlElement)> actionAttributes, DecryptedPermit decryptedPermit = null, string replacedBy = null)
{
foreach (var attribute in attributes)
{
try
{
var attributeNode = soapXml.CreateElement(attribute.XmlNodeName);
if (attribute.IsRequired)
{
switch (attribute.XmlNodeName)
{
case Constants.ReplacedBy:
if (!IsPropertyNullOrEmpty(attribute.JsonPropertyName, replacedBy)) attributeNode.InnerText = GetXmlNodeValue(replacedBy.ToString(), attribute.XmlNodeName);
break;
case Constants.ActiveKey:
if (!IsPropertyNullOrEmpty(attribute.JsonPropertyName, decryptedPermit.ActiveKey)) attributeNode.InnerText = GetXmlNodeValue(decryptedPermit.ActiveKey, attribute.XmlNodeName);
break;
case Constants.NextKey:
if (!IsPropertyNullOrEmpty(attribute.JsonPropertyName, decryptedPermit.NextKey)) attributeNode.InnerText = GetXmlNodeValue(decryptedPermit.NextKey, attribute.XmlNodeName);
break;
default:
var jsonFieldValue = CommonHelper.ParseXmlNode(attribute.JsonPropertyName, source, source.GetType()).ToString();
if (!IsPropertyNullOrEmpty(attribute.JsonPropertyName, jsonFieldValue))
{
attributeNode.InnerText = GetXmlNodeValue(jsonFieldValue.ToString(), attribute.XmlNodeName);
}
break;
}
}
else
{
attributeNode.InnerText = string.Empty;
}
actionAttributes.Add((attribute.SortingOrder, attributeNode));
}
catch (Exception ex)
{
throw new ERPFacadeException(EventIds.BuildingSapActionInformationException.ToEventId(), $"Error while generating SAP action information. | Action : {action} | XML Attribute : {attribute.XmlNodeName} | ErrorMessage : {ex.Message}");
}
}
}
private void ProcessUkhoWeekNumberAttributes(string action, IEnumerable<ActionItemAttribute> attributes, XmlDocument soapXml, UkhoWeekNumber ukhoWeekNumber, List<(int, XmlElement)> actionAttributes)
{
if (ukhoWeekNumber == null)
{
throw new ERPFacadeException(EventIds.RequiredSectionNotFoundException.ToEventId(), $"UkhoWeekNumber section not found in enccontentpublished event payload while creating {action} action.");
}
foreach (var attribute in attributes)
{
try
{
var attributeNode = soapXml.CreateElement(attribute.XmlNodeName);
if (attribute.IsRequired)
{
if (ukhoWeekNumber.Year.HasValue && ukhoWeekNumber.Week.HasValue && ukhoWeekNumber.CurrentWeekAlphaCorrection.HasValue)
{
switch (attribute.XmlNodeName)
{
case Constants.ValidFrom:
var validFrom = _weekDetailsProvider.GetDateOfWeek(ukhoWeekNumber.Year.Value, ukhoWeekNumber.Week.Value, ukhoWeekNumber.CurrentWeekAlphaCorrection.Value);
attributeNode.InnerText = GetXmlNodeValue(validFrom, attribute.XmlNodeName);
break;
case Constants.WeekNo:
var weekNo = string.Join(Constants.UkhoWeekNoFormatSeparator, ukhoWeekNumber.Year, ukhoWeekNumber.Week.Value.ToString(Constants.UkhoWeekNoFormat));
attributeNode.InnerText = GetXmlNodeValue(weekNo, attribute.XmlNodeName);
break;
case Constants.Correction:
attributeNode.InnerText = GetXmlNodeValue(ukhoWeekNumber.CurrentWeekAlphaCorrection.Value ? Constants.IsCorrectionTrue : Constants.IsCorrectionFalse, attribute.XmlNodeName);
break;
}
}
else
{
throw new ERPFacadeException(EventIds.EmptyEventJsonPropertyException.ToEventId(), $"Required details are missing in enccontentpublished event payload. | Property Name : {attribute.JsonPropertyName}");
}
}
actionAttributes.Add((attribute.SortingOrder, attributeNode));
}
catch (Exception ex)
{
throw new ERPFacadeException(EventIds.BuildingSapActionInformationException.ToEventId(), $"Error while generating SAP action information. | Action : {action} | XML Attribute : {attribute.XmlNodeName} | ErrorMessage : {ex.Message}");
}
}
}
private string GetXmlNodeValue(string fieldValue, string xmlNodeName = null)
{
// Return first 2 characters if the node is Agency, else limit other nodes to 250 characters
return xmlNodeName == Constants.Agency ? CommonHelper.ToSubstring(fieldValue, 0, Constants.MaxAgencyXmlNodeLength) : CommonHelper.ToSubstring(fieldValue, 0, Constants.MaxXmlNodeLength);
}
private XmlNode SortXmlPayload(XmlNode actionItemNode)
{
// Extract all action item nodes
var actionItems = actionItemNode.Cast<XmlNode>().ToList();
int sequenceNumber = 1;
// Sort based on the ActionNumber
var sortedActionItems = actionItems
.OrderBy(node => Convert.ToInt32(node.SelectSingleNode(Constants.ActionNumber)?.InnerText))
.ToList();
// Update the sequence number in the sorted list
foreach (XmlNode actionItem in sortedActionItems)
{
actionItem.SelectSingleNode(Constants.ActionNumber).InnerText = sequenceNumber.ToString();
sequenceNumber++;
}
//Append sorted action items
foreach (XmlNode actionItem in sortedActionItems)
{
actionItemNode.AppendChild(actionItem);
}
return actionItemNode;
}
private bool IsPropertyNullOrEmpty(string propertyName, string propertyValue)
{
if (string.IsNullOrEmpty(propertyValue))
{
throw new ERPFacadeException(EventIds.EmptyEventJsonPropertyException.ToEventId(), $"Required details are missing in enccontentpublished event payload. | Property Name : {propertyName}");
}
else return false;
}
}
}