using InABox.Core; using InABox.Core.Postable; using Microsoft.Web.WebView2.Wpf; using MYOB.AccountRight.SDK; using MYOB.AccountRight.SDK.Contracts; using MYOB.AccountRight.SDK.Services; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using System.Windows; using System.Windows.Controls; using System.Windows.Threading; namespace InABox.Poster.MYOB; public class MYOBConnectionData(ApiConfiguration configuration, CompanyFileService cfService, IOAuthKeyService authKey) { public ApiConfiguration Configuration { get; set; } = configuration; public CompanyFileService CompanyFileService { get; set; } = cfService; public CompanyFile? CompanyFile { get; set; } public CompanyFileCredentials? CompanyFileCredentials { get; set; } public CompanyFileWithResources? ActiveCompanyFile { get; set; } public IOAuthKeyService AuthKey { get; set; } = authKey; } public static partial class MYOBPosterEngine { internal static readonly string DEV_KEY = "f3b27f88-2ef9-4d8e-95c1-d0a045d0afee"; internal static readonly string SECRET_KEY = "ksm0e8yo6oumcPb63A8cduaN"; internal const string AUTH_URL = "https://secure.myob.com/oauth2/account/authorize"; internal const string AUTH_SCOPE = "CompanyFile"; internal const string REDIRECT_URL = "http://desktop"; private static MYOBConnectionData? _connectionData; public static bool GetAuthorisationCode(IApiConfiguration config, out string? code) { var url = $"{AUTH_URL}?client_id={config.ClientId}&redirect_uri={HttpUtility.UrlEncode(config.RedirectUrl)}&scope={AUTH_SCOPE}&response_type=code"; var window = new Window { Width = 800, Height = 600, Title = "Sign in to MYOB" }; string? resultCode = null; var view = new WebView2 { }; view.NavigationCompleted += (o, e) => { view.ExecuteScriptAsync("document.documentElement.innerHTML;").ContinueWith(task => { if (task.Result is null) return; var str = Serialization.Deserialize(task.Result); if (str is null) return; var match = CodeRegex().Match(str); if (!match.Success) return; resultCode = match.Groups[1].Value; window.Dispatcher.BeginInvoke(() => { if(window.DialogResult is null) { window.DialogResult = true; window.Close(); } }); }); }; view.Source = new Uri(url); window.Content = view; if(window.ShowDialog() == true) { code = resultCode; return true; } else { code = null; return false; } } public static MYOBConnectionData GetConnectionData() { if(_connectionData is MYOBConnectionData data) { return data; } var configuration = new ApiConfiguration(DEV_KEY, SECRET_KEY, REDIRECT_URL); var authService = new OAuthService(configuration); if(!GetAuthorisationCode(configuration, out var code)) { throw new PostCancelledException(); } if(code is null) { throw new PostFailedMessageException("No authorisation code was received."); } var keystore = new SimpleOAuthKeyService { OAuthResponse = authService.GetTokens(code) }; var cfService = new CompanyFileService(configuration, null, keystore); _connectionData = new MYOBConnectionData(configuration, cfService, keystore); return _connectionData; } [GeneratedRegex("code=(.*)<")] private static partial Regex CodeRegex(); } public abstract class MYOBPosterEngine : PosterEngine, TSettings>, IGlobalSettingsPosterEngine, MYOBGlobalPosterSettings> where TPostable : Entity, IPostable, IRemotable, IPersistent, new() where TSettings : MYOBPosterSettings, new() { private MYOBGlobalPosterSettings GetGlobalSettings() => (this as IGlobalSettingsPosterEngine, MYOBGlobalPosterSettings>).GetGlobalSettings(); public override bool BeforePost(IDataModel model) { return Poster.BeforePost(model); } protected override IPostResult DoProcess(IDataModel model) { var data = MYOBPosterEngine.GetConnectionData(); var globalSettings = GetGlobalSettings(); var companyFileID = globalSettings.GetCompanyFileID(); if(data.CompanyFile is null || data.CompanyFile.Id != companyFileID) { if(companyFileID == Guid.Empty) { throw new PostFailedMessageException("No CompanyFileID has been set."); } else if(globalSettings.CompanyFileUserID.IsNullOrWhiteSpace()) { throw new PostFailedMessageException("CompanyFile credentials have not been set."); } var companyFile = data.CompanyFileService.GetRange().FirstOrDefault(x => x.Id == companyFileID); var fileCredentials = new CompanyFileCredentials(globalSettings.CompanyFileUserID, globalSettings.CompanyFilePassword); data.CompanyFile = companyFile; data.CompanyFileCredentials = fileCredentials; data.ActiveCompanyFile = data.CompanyFileService.Get(companyFile, fileCredentials); } Poster.ConnectionData = data; return Poster.Process(model); } public override void AfterPost(IDataModel model, IPostResult result) { } }