424 lines
20 KiB
C#
424 lines
20 KiB
C#
using System.Threading.Tasks;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Sockeye.Util;
|
|
using Sockeye.Api.ControllerHelpers;
|
|
using Sockeye.Models;
|
|
using System.Collections.Generic;
|
|
using System;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
|
|
using System.Linq;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
//JSON KEY
|
|
using Org.BouncyCastle.Security;
|
|
using Org.BouncyCastle.OpenSsl;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Net.Http.Headers;
|
|
|
|
namespace Sockeye.Biz
|
|
{
|
|
|
|
internal class GlobalBizSettingsBiz : BizObject
|
|
{
|
|
|
|
internal GlobalBizSettingsBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
|
{
|
|
ct = dbcontext;
|
|
UserId = currentUserId;
|
|
UserTranslationId = userTranslationId;
|
|
CurrentUserRoles = UserRoles;
|
|
BizType = SockType.Global;
|
|
}
|
|
|
|
internal static GlobalBizSettingsBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
|
{
|
|
if (httpContext != null)
|
|
return new GlobalBizSettingsBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
|
|
else
|
|
return new GlobalBizSettingsBiz(ct, 1, ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin);
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// GET
|
|
|
|
//Get one
|
|
internal async Task<GlobalBizSettings> GetAsync(bool logTheGetEvent = true)
|
|
{
|
|
//first try to fetch from db
|
|
var ret = await ct.GlobalBizSettings.AsNoTracking().SingleOrDefaultAsync(m => m.Id == 1);
|
|
if (logTheGetEvent && ret != null)
|
|
{
|
|
//Log
|
|
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 1, BizType, SockEvent.Retrieved), ct);
|
|
}
|
|
|
|
//not in db then get the default
|
|
if (ret == null)
|
|
{
|
|
throw new System.Exception("GlobalBizSettingsBiz::GetAsync -> Global settings object not found in database!!");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//UPDATE
|
|
//
|
|
internal async Task<GlobalBizSettings> PutAsync(GlobalBizSettings putObject)
|
|
{
|
|
var dbObject = await GetAsync(false);
|
|
if (dbObject == null)
|
|
throw new System.Exception("GlobalBizSettingsBiz::PutAsync -> Global settings object not found in database. Contact support immediately!");
|
|
|
|
if (dbObject.Concurrency != putObject.Concurrency)
|
|
{
|
|
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
|
|
return null;
|
|
}
|
|
Validate(putObject, dbObject);
|
|
if (HasErrors) return null;
|
|
|
|
List<string> originalTags = dbObject.AllTags();
|
|
List<string> newTags = putObject.AllTags();
|
|
|
|
ct.Replace(dbObject, putObject);
|
|
|
|
|
|
try
|
|
{
|
|
await ct.SaveChangesAsync();
|
|
}
|
|
catch (DbUpdateConcurrencyException)
|
|
{
|
|
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
|
|
return null;
|
|
}
|
|
//Update cache
|
|
ServerGlobalBizSettings.Initialize(putObject, null);
|
|
|
|
//Log modification and save context
|
|
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 1, BizType, SockEvent.Modified), ct);
|
|
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newTags, originalTags);
|
|
|
|
return putObject;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//VALIDATION
|
|
//
|
|
|
|
//Can save or update?
|
|
private void Validate(GlobalBizSettings proposedObj, GlobalBizSettings currentObj)
|
|
{
|
|
//currently nothing to validate
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//IMPORT FROM ROCKFISH
|
|
|
|
public async Task ImportRockfish(AyContext ct, ILogger log)
|
|
{
|
|
log.LogInformation("Start import from rockfish, authenticating");
|
|
//Authenticate to rockfish
|
|
//string sUrl = $"{LICENSE_SERVER_URL_ROCKFISH}rvr";
|
|
string URL_ROCKFISH = "https://rockfish.ayanova.com/";
|
|
|
|
try
|
|
{
|
|
//var content = new StringContent("login=john&password=b43698c255365ee739c05ba0d42855e96c2365c76bb2f9b9eb149cec7b52174c");
|
|
var content = new StringContent(JsonConvert.SerializeObject(new
|
|
{
|
|
login = "john",
|
|
password = "b43698c255365ee739c05ba0d42855e96c2365c76bb2f9b9eb149cec7b52174c"
|
|
}), Encoding.UTF8, "application/json");
|
|
|
|
|
|
|
|
var client = ServiceProviderProvider.HttpClientFactory.CreateClient();
|
|
|
|
|
|
//AUTHENTICATE
|
|
var res = await client.PostAsync($"{URL_ROCKFISH}authenticate?login=john&password=b43698c255365ee739c05ba0d42855e96c2365c76bb2f9b9eb149cec7b52174c", content);
|
|
var responseText = await res.Content.ReadAsStringAsync();
|
|
|
|
if (res.IsSuccessStatusCode)
|
|
{
|
|
log.LogInformation(responseText);
|
|
}
|
|
else log.LogError($"E1020 - Error authenticating to rockfish: {res.ReasonPhrase}");
|
|
|
|
var responseJson = JObject.Parse(responseText);
|
|
|
|
var authToken = responseJson["token"].Value<string>();
|
|
var dlKey = responseJson["dlkey"].Value<string>();
|
|
var userId = responseJson["id"].Value<long>();
|
|
//client.DefaultRequestHeaders.Add("Bearer", authToken);
|
|
|
|
var contentType = new MediaTypeWithQualityHeaderValue("application/json");
|
|
client.DefaultRequestHeaders.Accept.Add(contentType);
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/customer/list");
|
|
responseText = await res.Content.ReadAsStringAsync();
|
|
var jaCustomerList = JArray.Parse(responseText);
|
|
log.LogInformation("RFImport customers");
|
|
foreach (JObject jCustomerListItem in jaCustomerList)
|
|
{
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/customer/{jCustomerListItem["id"].Value<long>()}");
|
|
|
|
var jCustomer = JObject.Parse(await res.Content.ReadAsStringAsync());
|
|
|
|
//SITES
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/customer/{jCustomerListItem["id"].Value<long>()}/sites");
|
|
var jaSiteList = JArray.Parse(await res.Content.ReadAsStringAsync());
|
|
|
|
bool multiSite = jaSiteList.Count() > 1;
|
|
|
|
foreach (JObject jSite in jaSiteList)
|
|
{
|
|
var CustomerName = jCustomer["name"].Value<string>();
|
|
if (multiSite)
|
|
CustomerName += " - " + jSite["name"].Value<string>();
|
|
long CurrentCustomerId = 0;
|
|
|
|
//Create customer if we don't have one already
|
|
if (await ct.Customer.AnyAsync(z => z.Name == CustomerName))
|
|
continue;//already have this one so no need to process it again
|
|
{
|
|
//CREATE CUSTOMER
|
|
Customer c = new Customer();
|
|
c.Name = CustomerName;
|
|
c.Country = jSite["country"].Value<string>();
|
|
c.Region = jSite["stateProvince"].Value<string>();
|
|
c.DoNotContact = jCustomer["doNotContact"].Value<bool>();
|
|
c.Notes = jCustomer["notes"].Value<string>();
|
|
c.DbId = jSite["dbId"].Value<string>();
|
|
if (c.DbId == "v7_no_dbid")
|
|
{
|
|
c.DbId = null;
|
|
c.Tags.Add("v7");
|
|
}
|
|
else
|
|
c.Tags.Add("raven");
|
|
|
|
if (jSite["hosted"].Value<bool>() == true)
|
|
c.Tags.Add("hosted");
|
|
|
|
var adminEmail = jCustomer["adminEmail"].Value<string>();
|
|
if (!string.IsNullOrWhiteSpace(adminEmail))
|
|
c.Notes += "\nAdmin Email: " + adminEmail;
|
|
c.EmailAddress = jCustomer["adminEmail"].Value<string>();
|
|
CustomerBiz biz = CustomerBiz.GetBiz(ct);
|
|
var NewObject = await biz.CreateAsync(c);
|
|
CurrentCustomerId = NewObject.Id;
|
|
}//customer creation
|
|
|
|
|
|
//SITE PURCHASES
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/site/{jSite["id"].Value<long>()}/purchases");
|
|
var jaPurchaseList = JArray.Parse(await res.Content.ReadAsStringAsync());
|
|
|
|
foreach (JObject jPurchase in jaPurchaseList)
|
|
{
|
|
//create product if not exist then import
|
|
//Get product id if exists
|
|
var ProductName = jPurchase["name"].Value<string>();
|
|
ProductName = ProductName.Replace("- Renewal", "").Replace(" Renewal", "").Replace(" RENEWAL", "").Replace("CANCELLED ", "").Replace("CANCELED ", "");
|
|
var p = await ct.Product.AsNoTracking().FirstOrDefaultAsync(z => z.VendorCode == jPurchase["productCode"].Value<string>());
|
|
|
|
|
|
if (p == null)
|
|
{
|
|
|
|
//Create it here
|
|
p = new Product();
|
|
p.Name = ProductName;
|
|
p.Active = true;//not entirely true but we can always deactivate there aren't many products
|
|
p.VendorId = 1;
|
|
p.OurCode = p.VendorCode = jPurchase["productCode"].Value<string>();
|
|
|
|
if (p.VendorCode == "301028468")//subscription yearly
|
|
{
|
|
p.LicenseInterval = new TimeSpan(365, 0, 0, 0); //actual length, in license is where I pad to cover them
|
|
}
|
|
|
|
if (p.VendorCode == "301028467")//subscription monthly
|
|
{
|
|
p.LicenseInterval = new TimeSpan(31, 0, 0, 0);//there is no concept beyond days for timespan so a month is 31 days max
|
|
p.MaintInterval = new TimeSpan(31, 0, 0, 0);//the only product less than one year of maintenance save the ones with no maintenance which can be edited manually later as there are so few of them
|
|
}
|
|
else
|
|
p.MaintInterval = new TimeSpan(365, 0, 0, 0);
|
|
|
|
ProductBiz biz = ProductBiz.GetBiz(ct);
|
|
p = await biz.CreateAsync(p);
|
|
}
|
|
|
|
//Now we can add the sale that we have the product
|
|
if (!await ct.Purchase.AnyAsync(z => z.SalesOrderNumber == jPurchase["salesOrderNumber"].Value<string>()))
|
|
{
|
|
var s = new Purchase();
|
|
s.VendorId = 1;
|
|
s.ProductId = p.Id;
|
|
|
|
s.CustomerId = CurrentCustomerId;
|
|
s.CancelDate = DateUtil.EpochToDateNullIsNull(jPurchase["cancelDate"].Value<long?>());
|
|
s.CouponCode = jPurchase["couponCode"].Value<string>();
|
|
s.ExpireDate = DateUtil.EpochToDateNullIsNull(jPurchase["expireDate"].Value<long?>());
|
|
s.Name = p.Name;
|
|
s.ProcessedDate = s.PurchaseDate = DateUtil.EpochToDateNullIsMin(jPurchase["purchaseDate"].Value<long?>());
|
|
s.Quantity = jPurchase["quantity"].Value<int>();
|
|
s.RenewNoticeSent = jPurchase["renewNoticeSent"].Value<bool>();
|
|
s.SalesOrderNumber = jPurchase["salesOrderNumber"].Value<string>();
|
|
s.VendorData = jPurchase["notes"].Value<string>();
|
|
PurchaseBiz biz = PurchaseBiz.GetBiz(ct);
|
|
await biz.CreateAsync(s);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}//site loop
|
|
|
|
}//end of all customers iteration
|
|
|
|
|
|
|
|
#region CASES
|
|
log.LogInformation("RFImport Cases");
|
|
//case projects to be tags
|
|
List<NameIdItem> CaseProjectList = new List<NameIdItem>();
|
|
{
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/rfcaseproject");
|
|
responseText = await res.Content.ReadAsStringAsync();
|
|
|
|
var jaRFCaseProjectList = JArray.Parse(responseText);
|
|
foreach (JObject jRFCaseProject in jaRFCaseProjectList)
|
|
{
|
|
CaseProjectList.Add(new NameIdItem() { Name = jRFCaseProject["name"].Value<string>(), Id = jRFCaseProject["id"].Value<long>() });
|
|
|
|
}
|
|
|
|
}
|
|
{
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/rfcase/list");
|
|
responseText = await res.Content.ReadAsStringAsync();
|
|
var jaRFCaseList = JArray.Parse(responseText);
|
|
//some cases are missing the start date so substitute a close other case date (not critical but sb at least in the ballpark for list viewing purposes)
|
|
DateTime dtTempCreated = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
foreach (JObject jRFCase in jaRFCaseList)
|
|
{
|
|
|
|
var g = new GZCase();
|
|
g.CaseId = jRFCase["id"].Value<long>();
|
|
g.Closed = DateUtil.EpochToDateNullIsNull(jRFCase["dtClosed"].Value<long?>());
|
|
//NOTE: closed in rockfish was the date at midnight in GMT
|
|
//so to be in the same day as here need to add a few hours, let's say 8 am for each so add 8 hours
|
|
if(g.Closed!=null){
|
|
g.Closed=((DateTime)g.Closed).AddHours(8);
|
|
}
|
|
|
|
//fuckery to try to insert a at least semi close date when created date is missing
|
|
DateTime? dtTemp = DateUtil.EpochToDateNullIsNull(jRFCase["dtCreated"].Value<long?>());
|
|
if (dtTemp == null)
|
|
{
|
|
dtTemp = dtTempCreated;
|
|
}
|
|
else
|
|
{
|
|
dtTempCreated = (DateTime)dtTemp;
|
|
}
|
|
g.Created = (DateTime)dtTemp;
|
|
g.Name = jRFCase["title"].Value<string>();
|
|
g.Notes = jRFCase["notes"].Value<string>();
|
|
|
|
var ver = jRFCase["releaseVersion"].Value<string>();
|
|
if (!string.IsNullOrWhiteSpace(ver))
|
|
g.Notes += $"\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\nReleased on version:{ver}";
|
|
|
|
var releaseNotes = jRFCase["releaseNotes"].Value<string>();
|
|
if (!string.IsNullOrWhiteSpace(releaseNotes))
|
|
g.Notes += $"\nRelease notes:{releaseNotes}";
|
|
|
|
//Project name to tags
|
|
g.Tags.Add(CaseProjectList.FirstOrDefault(z => z.Id == jRFCase["rfCaseProjectId"].Value<long>()).Name.Replace("z_", "legacy-"));
|
|
|
|
//priority to tags
|
|
g.Tags.Add($"{jRFCase["priority"].Value<long>()}-priority");
|
|
|
|
//check for attachments and just add as a note, don't bother with actual transfer of attachment, there aren't a lot and most are way in the past and not required for anything
|
|
//if needed in future can manually xfer it over from the rockfish.sqlite db directly using DB BRowser for sqlite which allows opening the blob from the rfcaseblob table and saving it
|
|
res = await client.GetAsync($"{URL_ROCKFISH}api/rfcase/{g.CaseId}/attachments");
|
|
responseText = await res.Content.ReadAsStringAsync();
|
|
var jAttachments = JObject.Parse(responseText);
|
|
if (jAttachments["attach"].Count() > 0)
|
|
{
|
|
g.Notes += "\n********\nRockfish attachments\n";
|
|
foreach (JObject jAttachmentRecord in jAttachments["attach"])
|
|
{
|
|
g.Notes += $"File: \"{jAttachmentRecord["name"].Value<string>()}\", rfcaseblob table id: {jAttachmentRecord["id"].Value<long>()}\n";
|
|
|
|
}
|
|
g.Notes += "\n********\n";
|
|
}
|
|
|
|
GZCaseBiz biz = GZCaseBiz.GetBiz(ct);
|
|
await biz.CreateAsync(g);
|
|
|
|
//attachments example 86400000
|
|
// /api/rfcase/4360/attachments
|
|
//{"dlkey":"ZFkAUpo1L0Gi3Q9aO5szkA","attach":[{"id":259,"name":"desired weight calcs.txt"}]}
|
|
//{"dlkey":"iR6ncD70CkzkozyT0otA","attach":[]}
|
|
|
|
|
|
|
|
|
|
}// all cases loop
|
|
|
|
//Start next case with a new sequence caseid of 4444
|
|
using (var command = ct.Database.GetDbConnection().CreateCommand())
|
|
{
|
|
command.CommandText = $"ALTER SEQUENCE agzcase_caseid_seq RESTART WITH 4443;";
|
|
await ct.Database.OpenConnectionAsync();
|
|
await command.ExecuteNonQueryAsync();
|
|
await ct.Database.CloseConnectionAsync();
|
|
}
|
|
}
|
|
#endregion cases
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
var msg = "Error importing rockfish";
|
|
log.LogError(ex, msg);
|
|
}
|
|
|
|
|
|
log.LogInformation("FINISHED IMPORTING");
|
|
//in the correct order retrieve every object and if it's not already present in sockeye, import it
|
|
//this should be callable any time and it will just update so it can be test live in sync / parallel until ready to switch over
|
|
//await Task.CompletedTask;
|
|
|
|
}
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
}//eoc
|
|
|
|
|
|
}//eons
|
|
|