Advertisement
CharlesLindsay

Untitled

Mar 12th, 2018
552
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.12 KB | None | 0 0
  1. using Amazon.CognitoIdentityProvider;
  2. using Amazon.CognitoIdentityProvider.Model;
  3. using Enumis.Apply.Api.ServiceInterface.Extensions;
  4. using Enumis.Apply.Api.ServiceModel;
  5. using Enumis.Constants;
  6. using Enumis.Constants.Extensions;
  7. using Enumis.DataModel.Extensions;
  8. using Enumis.DataModel.Indices.GSI.Application;
  9. using Enumis.DataModel.Indices.GSI.Customer;
  10. using Enumis.DataModel.Tables;
  11. using Enumis.DataModel.Validators;
  12. using Enumis.Interfaces.Applications;
  13. using Enumis.Interfaces.ChangeTracking;
  14. using Enumis.Interfaces.Cognito;
  15. using Enumis.Interfaces.DataModel.DataTypes;
  16. using Enumis.Interfaces.Enums;
  17. using Enumis.Interfaces.Extensions;
  18. using Enumis.Interfaces.Processors.Gps;
  19. using Enumis.Interfaces.Programs;
  20. using Enumis.Utilities.Api.Types;
  21. using Enumis.Utilities.Extensions;
  22. using id3global;
  23. using Serilog;
  24. using ServiceStack;
  25. using ServiceStack.Aws.DynamoDb;
  26. using ServiceStack.Configuration;
  27. using ServiceStack.Text;
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Globalization;
  31. using System.IO;
  32. using System.Linq;
  33. using Address = Enumis.Interfaces.Applications.Address;
  34.  
  35. namespace Enumis.Apply.Api.ServiceInterface
  36. {
  37. /// <summary>
  38. /// Defines the <see cref="ApplicationService" />
  39. /// </summary>
  40. public class ApplicationService : Service
  41. {
  42. #region Properties
  43.  
  44. /// <summary>
  45. /// Gets or sets the AppSettings
  46. /// </summary>
  47. public IAppSettings AppSettings { get; set; }
  48.  
  49. /// <summary>
  50. /// Gets or sets the CognitoClient
  51. /// </summary>
  52. public AmazonCognitoIdentityProviderClient CognitoClient { get; set; }
  53.  
  54. /// <summary>
  55. /// Gets or sets the CognitoIdentity
  56. /// </summary>
  57. public ICognitoIdentity CognitoIdentity { get; set; }
  58.  
  59. /// <summary>
  60. /// Gets or sets the DynamoDb
  61. /// </summary>
  62. public IPocoDynamo DynamoDb { get; set; }
  63.  
  64. /// <summary>
  65. /// Gets or sets the Log
  66. /// </summary>
  67. public ILogger Log { get; set; }
  68.  
  69. /// <summary>
  70. /// Gets or sets the S3Vault
  71. /// </summary>
  72. public S3Vault S3Vault { get; set; }
  73.  
  74. #endregion
  75.  
  76. #region Methods
  77.  
  78. /// <summary>
  79. /// The Get
  80. /// </summary>
  81. /// <param name="request">The <see cref="ApplicationsRequest" /></param>
  82. /// <returns>The <see cref="ApplicationsResponse" /></returns>
  83. public ApplicationsResponse Get(ApplicationsRequest request)
  84. {
  85. var applications = GetGpsApplications().AsApplicationsWeb();
  86. var applicationsResponse = new ApplicationsResponse
  87. {
  88. Applications = applications.Where(x => x.State == ApplicationState.Success).ToList(),
  89. Application = applications.FirstOrDefault(x => x.State < ApplicationState.Success)
  90. };
  91.  
  92. return applicationsResponse;
  93. }
  94.  
  95. /// <summary>
  96. /// The Post
  97. /// </summary>
  98. /// <param name="request">The <see cref="ApplicationKycRequest" /></param>
  99. /// <returns>The <see cref="ApplicationKycResponse" /></returns>
  100. public ApplicationKycResponse Post(ApplicationKycRequest request)
  101. {
  102. var authenticateClient =
  103. new GlobalAuthenticateClient(GlobalAuthenticateClient.EndpointConfiguration
  104. .wsHttpBinding_GlobalAuthenticate);
  105.  
  106. authenticateClient.ClientCredentials.UserName.UserName = "ryan.helms@enumis.co.uk";
  107. authenticateClient.ClientCredentials.UserName.Password = "40%4MKy4yZdaCKclx1RC";
  108.  
  109. var input = new GlobalInputData
  110. {
  111. Personal = new GlobalPersonal
  112. {
  113. PersonalDetails = request.GetPersonalDetails()
  114. },
  115. Addresses = new GlobalAddresses
  116. {
  117. CurrentAddress = request.GetCurrentAddress(),
  118. HistoricAddresses = request.GetHistoricAddresses()
  119. }
  120. };
  121.  
  122. var profile = new GlobalProfileIDVersion
  123. {
  124. ID = new Guid("7c2bcf5a-2fae-456b-995c-450630fb2621"),
  125. Version = 0
  126. };
  127.  
  128. // Log("||ApplicationKycRequest||", "Profile:", profile.ToJson());
  129. // Log("||ApplicationKycRequest||", "Input:", input.ToJson());
  130.  
  131. var result = authenticateClient.AuthenticateSPAsync(profile, "", input).ConfigureAwait(false).GetAwaiter()
  132. .GetResult();
  133.  
  134. // Log("||ApplicationKycRequest||", "Result:", result.ToJson());
  135.  
  136. var response = new ApplicationKycResponse
  137. {
  138. Decision = result.BandText,
  139. Score = result.Score.ToString(),
  140. AuthenticationId = result.AuthenticationID.ToString(),
  141. Timestamp = result.Timestamp.ToString(CultureInfo.InvariantCulture)
  142. };
  143.  
  144. // Log("||ApplicationKycRequest||", "Response:", response.ToJson());
  145.  
  146. return response;
  147. }
  148.  
  149. /// <summary>
  150. /// The Get
  151. /// </summary>
  152. /// <param name="request">The <see cref="ApplyRegistrationRequest" /></param>
  153. /// <returns>The <see cref="ApplyRegistrationResponse" /></returns>
  154. public ApplyRegistrationResponse Post(ApplyRegistrationRequest request)
  155. {
  156. try
  157. {
  158. var addUserToGroupResult = AddUserToGroup(CognitoIdentity.Username);
  159.  
  160. Log.Debug("||ApplicationKycRequest|| Add User To Group Result: {@AddUserToGroupResults}", addUserToGroupResult);
  161.  
  162. return new ApplyRegistrationResponse
  163. {
  164. AddUserToGroupResponse = addUserToGroupResult
  165. };
  166. }
  167. catch (Exception ex)
  168. {
  169. throw new InvalidOperationException(ex.FormatException(), ex);
  170. }
  171. }
  172.  
  173. /// <summary>
  174. /// The Post
  175. /// </summary>
  176. /// <param name="request">The <see cref="AssetUploadRequest" /></param>
  177. /// <returns>The <see cref="AssetUploadResponse" /></returns>
  178. public AssetUploadResponse Post(AssetUploadRequest request)
  179. {
  180. try
  181. {
  182. request.ERN.ThrowIfNullOrEmpty("Application ERN cannot be empty");
  183. request.Link.ThrowIfNullOrEmpty("Asset file name cannot be empty");
  184. request.Bytes.ThrowIfNull("Asset Bytes cannot be empty");
  185.  
  186. Log.Debug("File Received: {@AssetUploadRequest}", request);
  187.  
  188. var applicationIdx = DynamoDb.FromQueryIndex<ApplicationERNGlobalIndex>(x => x.ERN == request.ERN).Exec()?.FirstOrDefault();
  189. applicationIdx.ThrowIfNull();
  190.  
  191. var application = DynamoDb.GetItem<Application>(new DynamoId(applicationIdx.CustomerERN, applicationIdx.ERN));
  192. var originalApplication = application.AsIGpsApplication();
  193. originalApplication.ThrowIfNull();
  194.  
  195. var destinationName = $"{request.Type.ToString().ToLower()}{Path.GetExtension(request.Link)}";
  196. var destination = $"applications/{applicationIdx.CustomerERN}/{applicationIdx.ERN}/{destinationName}";
  197.  
  198. S3Vault.WriteFile(destination, new MemoryStream(request.Bytes));
  199.  
  200.  
  201. Log.Information("Image Saved: {AssetUploadDestination}", destination);
  202.  
  203. request.Bytes = null;
  204. request.Link = destinationName;
  205.  
  206. // Lets upate or add
  207. var applicationAsset = request.ConvertTo<ApplicationAsset>();
  208. var existingAsset = originalApplication.Form.Assets.FirstOrDefault(x => x.Type == request.Type && x.Domain == request.Domain);
  209.  
  210. if (existingAsset != null)
  211. {
  212. existingAsset.GetChanges(applicationAsset).AsModifications().SaveTo(originalApplication);
  213. originalApplication.Form.Assets.Remove(existingAsset);
  214. }
  215.  
  216. originalApplication.Form.Assets.Add(applicationAsset);
  217.  
  218. // Lets move the state of the application
  219. switch (originalApplication.State)
  220. {
  221. case ApplicationState.UploadIdentificationDocuments:
  222. if (originalApplication.Form.Assets.Any(x => x.Domain == ApplicationAssetDomain.Identification && (int)x.Type < 1000))
  223. originalApplication.State++;
  224. break;
  225. case ApplicationState.UploadAddressDocuments:
  226. var cutoff = 999;
  227. if (originalApplication.Form.Assets.Any(x => x.Domain == ApplicationAssetDomain.Identification && x.Type == ApplicationAssetType.DriversLicense)) cutoff = 1000;
  228. if (originalApplication.Form.Assets.Count(x => x.Domain == ApplicationAssetDomain.Address && (int)x.Type >= cutoff) > 1)
  229. originalApplication.State++;
  230. break;
  231. }
  232.  
  233. originalApplication.BumpModifyDate();
  234. DynamoDb.PutItem(originalApplication.ToDynamoDb());
  235.  
  236. return originalApplication.ConvertTo<AssetUploadResponse>();
  237. }
  238. catch (Exception ex)
  239. {
  240. Log.Fatal(ex, ex.ToString());
  241. throw new InvalidOperationException(ex.FormatException(), ex);
  242. }
  243. }
  244.  
  245. /// <summary>
  246. /// The Post
  247. /// </summary>
  248. /// <param name="request">The <see cref="ProcessApplicationRequest" /></param>
  249. /// <returns>The <see cref="ProcessApplicationResponse" /></returns>
  250. public ProcessApplicationResponse Post(ProcessApplicationRequest request)
  251. {
  252. try
  253. {
  254. var application = GetApplication(CognitoIdentity.Sub, CognitoIdentity.Email);
  255. Log.Debug("Returning Application {@application}", application);
  256. return application.ConvertTo<ProcessApplicationResponse>();
  257. }
  258. catch (Exception ex)
  259. {
  260. throw new InvalidOperationException(ex.FormatException(), ex);
  261. }
  262. }
  263.  
  264. /// <summary>
  265. /// The Put
  266. /// </summary>
  267. /// <param name="request">The <see cref="ProcessApplicationRequest" /></param>
  268. /// <returns>The <see cref="ProcessApplicationResponse" /></returns>
  269. public ProcessApplicationResponse Put(ProcessApplicationRequest request)
  270. {
  271. try
  272. {
  273. request.ThrowIfNull("Incomming request should not be null");
  274. Log.Debug("Incomming Request {@ProcessApplicationRequest}", request);
  275.  
  276. var application = GetCurrentApplication(request.ConvertTo<GpsApplication>());
  277. application.ThrowIfNull("Application should not be null");
  278. Log.Debug("Current Application: {@CurrentApplication}", application);
  279.  
  280.  
  281. var processedApplication = ProcessApplication(application);
  282.  
  283. return processedApplication.ConvertTo<ProcessApplicationResponse>();
  284. }
  285. catch (Exception ex)
  286. {
  287. throw new InvalidOperationException(ex.FormatException(), ex);
  288. }
  289. }
  290.  
  291. /// <summary>
  292. /// The AddUserToGroup
  293. /// </summary>
  294. /// <param name="username">The <see cref="string" /></param>
  295. /// <returns>The <see cref="AdminAddUserToGroupResponse" /></returns>
  296. private AdminAddUserToGroupResponse AddUserToGroup(string username)
  297. {
  298. return CognitoClient.AdminAddUserToGroupAsync(new AdminAddUserToGroupRequest
  299. {
  300. UserPoolId = AppSettings.GetString("COGNITO_USERPOOL_ID"),
  301. Username = username,
  302. GroupName = "system:apply"
  303. }).GetAwaiter().GetResult();
  304. }
  305.  
  306. /// <summary>
  307. /// The CompleteNewRegistration
  308. /// </summary>
  309. /// <param name="application">The <see cref="IApplicationComplete" /></param>
  310. private void CompleteNewRegistration(IApplicationComplete application)
  311. {
  312. if (!application.IsNewRegistration) return;
  313.  
  314. var registration = DynamoDb.GetRegistrationByCognitoUserId(CognitoIdentity.Sub);
  315. var originalRegistration = registration.Clone();
  316.  
  317. // TODO: Need to fill these in with valid data
  318. //AccountCode = "AccountCode".ToNewERN()
  319. if (!registration.BirthDate.HasValue) registration.BirthDate = DateTime.Parse($"{application.Form.DobYear}-{application.Form.DobMonth}-{application.Form.DobDay}").ToUnixTimeMs();
  320. if (string.IsNullOrWhiteSpace(registration.Title)) registration.Title = application.Form.Title;
  321. if (string.IsNullOrWhiteSpace(registration.FirstName)) registration.FirstName = application.Form.FirstName;
  322. if (string.IsNullOrWhiteSpace(registration.MiddleName)) registration.MiddleName = application.Form.MiddleName;
  323. if (string.IsNullOrWhiteSpace(registration.LastName)) registration.LastName = application.Form.LastName;
  324. if (!registration.OriginCountry.HasValue) registration.OriginCountry = Iso.Countries.FindBy(application.Form.Nationality, false)?.IsoCode?.ToInt() ?? 0;
  325.  
  326. if (originalRegistration.GetChanges(registration).Any())
  327. {
  328. registration.BumpModifyDate();
  329. DynamoDb.PutItem(registration);
  330. }
  331. }
  332.  
  333. /// <summary>
  334. /// The CreateApplication
  335. /// </summary>
  336. /// <param name="registration">The <see cref="Registration" /></param>
  337. /// <returns>The <see cref="IGpsApplication" /></returns>
  338. private IGpsApplication CreateApplication(Registration registration)
  339. {
  340. Log.Debug("Creating new Application for {@Registration}", registration);
  341. var application = new GpsApplication
  342. {
  343. ERN = "Application".ToNewERN(),
  344. CustomerERN = registration.ERN,
  345. CreatedDate = DateTime.UtcNow.ToUnixTimeMs(),
  346. IsNewRegistration = registration.AccountCode.Contains("Accountcode"),
  347. Form = new Form
  348. {
  349. Addresses = new List<Address>
  350. {
  351. new Address()
  352. }
  353. },
  354. Program = new Program
  355. {
  356. Code = "FLOW",
  357. Name = "Flow Card"
  358. }
  359. };
  360.  
  361. var dynamoDb = application.ToDynamoDb();
  362. DynamoDb.PutItem(dynamoDb);
  363.  
  364. Log.Information("Created New Application: {@Application}", application);
  365. return application;
  366. }
  367.  
  368. /// <summary>
  369. /// The GetApplication
  370. /// </summary>
  371. /// <param name="cognitoId">The <see cref="string" /></param>
  372. /// <param name="cognitoEmail">The <see cref="string" /></param>
  373. /// <returns>The <see cref="IGpsApplication" /></returns>
  374. private IGpsApplication GetApplication(string cognitoId, string cognitoEmail)
  375. {
  376. var registration = DynamoDb.GetRegistrationByCognitoUserId(cognitoId);
  377.  
  378. if (registration == null)
  379. {
  380. registration = new Registration
  381. {
  382. ERN = "Customer".ToNewERN(),
  383. Code = GetCustomerCode(),
  384. CognitoUserId = cognitoId,
  385. EmailAddress = cognitoEmail,
  386. CreatedDate = DateTime.UtcNow.ToUnixTimeMs(),
  387. SecurityQuestions = new List<SecurityQuestion>(),
  388. RegisteredDate = -1,
  389.  
  390. // TODO: Need to fill these in with valid data
  391. AccountCode = "AccountCode".ToNewERN()
  392. //BirthDate = DateTime.Parse(context.RawApplication.DOB).ToUnixTimeMs(),
  393. //Title = context.RawApplication.Title,
  394. //FirstName = context.RawApplication.FirstName,
  395. //LastName = context.RawApplication.LastName,
  396. //AccessCode = context.CreateCardRequest.AccCode,
  397. //OriginCountry = Countries.FindBy(context.RawApplication.Nationality, false)?.IsoCode?.ToInt() ?? 0
  398. };
  399.  
  400. DynamoDb.PutItem(registration);
  401. Log.Information("Created New Registration: {@NewRegistration}", registration);
  402. }
  403.  
  404. var applications = GetGpsApplications();
  405.  
  406. if (!applications.Any(x => x.State < ApplicationState.Success))
  407. return CreateApplication(registration);
  408.  
  409. var application = applications.FirstOrDefault(x => x.State < ApplicationState.Success);
  410. application.ThrowIfNull();
  411.  
  412. Log.Debug("Found Application: {@Application}", application);
  413. return application;
  414. }
  415.  
  416. /// <summary>
  417. /// The GetCurrentApplication
  418. /// </summary>
  419. /// <param name="application">The <see cref="IGpsApplication" /></param>
  420. /// <returns>The <see cref="IGpsApplication" /></returns>
  421. private IGpsApplication GetCurrentApplication(IGpsApplication application)
  422. {
  423. try
  424. {
  425. DynamoDb.ThrowIfNull("DynamoDb should not be null");
  426.  
  427. var registration = DynamoDb.GetRegistrationByCognitoUserId(CognitoIdentity.Sub);
  428. registration.ThrowIfNull($"No registration found => {CognitoIdentity.Sub}");
  429. Log.Debug("Found Registration: {@Registration}", registration);
  430.  
  431. var originalApplication = DynamoDb.GetItem<Application>(new DynamoId(registration.ERN, application.ERN)).AsIGpsApplication();
  432. originalApplication.ThrowIfNull();
  433. Log.Debug("Found Application: {@Application}", originalApplication);
  434.  
  435. // lets normailzie / humanize the data
  436. application.Form.ToTitleCase2All();
  437. application.Form.Addresses.ForEach(x => x.ToTitleCase2All());
  438.  
  439. application.Form.GetChanges(originalApplication.Form).AsModifications().SaveTo(originalApplication);
  440. // Lets merge in just the form
  441. originalApplication.Form.MergeWith(application.Form);
  442.  
  443. return originalApplication;
  444. }
  445. catch (Exception ex)
  446. {
  447. Log.Fatal(ex, nameof(GetCurrentApplication));
  448. throw;
  449. }
  450. }
  451.  
  452. /// <summary>
  453. /// The GetCustomerCode
  454. /// </summary>
  455. /// <returns>The <see cref="string" /></returns>
  456. private string GetCustomerCode()
  457. {
  458. string customerCode;
  459. bool exists;
  460.  
  461. do
  462. {
  463. Log.Verbose("Generating Customer Code");
  464.  
  465. customerCode = RandomExtensions.AlphaNumeric(10);
  466.  
  467. var code = customerCode;
  468.  
  469. exists = DynamoDb.Query(DynamoDb.FromQueryIndex<CodeGlobalIndex>(x => x.Code == code)).Any();
  470. } while (exists);
  471.  
  472. Log.Information("Generated Customer Code: {CustomerCode}", customerCode);
  473.  
  474. return customerCode;
  475. }
  476.  
  477. /// <summary>
  478. /// The GetGpsApplications
  479. /// </summary>
  480. /// <returns>The <see cref="List{IGpsApplication}" /></returns>
  481. private List<IGpsApplication> GetGpsApplications()
  482. {
  483. DynamoDb.ThrowIfNull("DynamoDb should not be null");
  484. var registration = DynamoDb.GetRegistrationByCognitoUserId(CognitoIdentity.Sub);
  485. if (registration == null) return new List<IGpsApplication>();
  486.  
  487. Log.Verbose("Looking for Existing Applications: {CustomerERN}", registration.ERN);
  488. var applications = DynamoDb.FromQuery<Application>(x => x.CustomerERN == registration.ERN)
  489. .Exec()
  490. .Select(x => x.AsIGpsApplication())
  491. .ToList();
  492.  
  493. return applications;
  494. }
  495.  
  496. /// <summary>
  497. /// The ProcessApplication
  498. /// </summary>
  499. /// <param name="application">The <see cref="IGpsApplication" /></param>
  500. /// <returns>The <see cref="IGpsApplication" /></returns>
  501. private IGpsApplication ProcessApplication(IGpsApplication application)
  502. {
  503. switch (application.State)
  504. {
  505. case ApplicationState.NewApplication:
  506. ValidateApplicationForm(application);
  507.  
  508. CompleteNewRegistration(application);
  509.  
  510. // TODO: This is a temp try/catch to ensure we continue while KYC is in progress.
  511. try
  512. {
  513. var results = Post(application.Form.ConvertTo<ApplicationKycRequest>());
  514. }
  515. catch (Exception e)
  516. {
  517. Log.Error(e, e.Message);
  518. throw;
  519. }
  520. //TODO: how de we interpert / process these results
  521.  
  522. application.State++;
  523. break;
  524. case ApplicationState.UploadIdentificationDocuments:
  525. application.State++;
  526. break;
  527. case ApplicationState.UploadAddressDocuments:
  528. application.State++;
  529. break;
  530. case ApplicationState.AcceptProductOffering:
  531. application.State++;
  532. break;
  533. case ApplicationState.Processing:
  534. application.State++;
  535. break;
  536. case ApplicationState.Pending:
  537. application.State++;
  538. break;
  539. case ApplicationState.Success:
  540. application.State = ApplicationState.NewApplication;
  541. break;
  542. case ApplicationState.Failure:
  543. application.State++;
  544. break;
  545. default:
  546. throw new ArgumentOutOfRangeException();
  547. }
  548.  
  549. application.BumpModifyDate();
  550. DynamoDb.PutItem(application.ToDynamoDb());
  551. return application;
  552. }
  553.  
  554. /// <summary>
  555. /// The ValidateApplicationForm
  556. /// </summary>
  557. /// <param name="application">The <see cref="IHasForm" /></param>
  558. private void ValidateApplicationForm(IHasForm application)
  559. {
  560. var newApplicationFormValidator = new NewApplicationFormValidator();
  561. var validationResults = newApplicationFormValidator.Validate(application.Form);
  562. if (!validationResults.IsValid) throw validationResults.ToException();
  563. }
  564.  
  565. #endregion
  566. }
  567. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement