/// <summary> /// Attach a callback that performs new asyncronous work when the original promise fails /// </summary> public static IPromise <T> FailOver <T>(this IPromise <T> promise, Func <IPromise <T> > callback) { var result = new Promise <T>(); result.CancelTarget( promise .Progress((p, m) => result.Notify(p / 50, m)) .Done(t => result.Resolve(t)) .Fail(exT => { try { result.CancelTarget( callback() .Progress((p, m) => result.Notify(50 + p / 50, m)) .Done(s => result.Resolve(s)) .Fail(exS => result.Reject(exS))); } catch (Exception ex) { result.Reject(ex); } })); return(result); }
/// <summary> /// Get a single item from the database using the specified query asynchronously. If the result is not a single item, an exception will be thrown /// </summary> /// <param name="conn">Server connection</param> /// <param name="request">Query/command which should return a single item</param> /// <param name="async">Whether to perform this request asynchronously</param> /// <returns>A promise to return a single readonly item</returns> public static IPromise <IReadOnlyItem> ItemByQuery(this IConnection conn, Command request, bool async) { var result = new Promise <IReadOnlyItem>(); result.CancelTarget(conn.ProcessAsync(request, async) .Progress((p, m) => result.Notify(p, m)) .Done(r => { if (string.IsNullOrEmpty(conn.UserId)) { result.Reject(new LoggedOutException()); } else { var res = conn.AmlContext.FromXml(r, request, conn); var ex = res.Exception; if (ex == null) { try { result.Resolve(res.AssertItem()); } catch (Exception exc) { result.Reject(exc); } } else { result.Reject(ex); } } }).Fail(ex => result.Reject(ex))); return(result); }
/// <summary> /// Convert a .Net 4.0 Task to a promise /// </summary> public static IPromise <T> ToPromise <T>(this Task <T> task, CancellationTokenSource cts = null) { var result = new Promise <T>(); if (cts != null) { result.CancelTarget(new CancelTarget() { Source = cts }); } task.ContinueWith(t => { if (t.IsFaulted) { Exception ex = t.Exception; if (ex != null && ex.InnerException != null) { ex = ex.InnerException; } result.Reject(ex); } else if (!t.IsCanceled) { result.Resolve(t.Result); } }); return(result); }
/// <summary> /// Convert a promise by modifying the done/fail logic in addition to transforming the type. /// </summary> public static IPromise <S> Convert <T, S>(this IPromise <T> promise, Action <T, Promise <S> > doneCallback, Action <Exception, Promise <S> > failCallback) { var result = new Promise <S>(); result.CancelTarget( promise .Progress((p, m) => result.Notify(p, m)) .Done(d => { doneCallback.Invoke(d, result); }).Fail(ex => failCallback.Invoke(ex, result))); return(result); }
/// <summary> /// Convert a promise using a simple transformation to go from one type to another /// </summary> public static IPromise <S> Convert <T, S>(this IPromise <T> promise, Func <T, S> callback) { var result = new Promise <S>(); result.CancelTarget( promise .Progress((p, m) => result.Notify(p, m)) .Done(d => { try { result.Resolve(callback.Invoke(d)); } catch (Exception ex) { result.Reject(ex); } }).Fail(ex => result.Reject(ex))); return(result); }
private static IPromise <T> ToHttpPromise <T>(this Task <T> task, TimeoutSource timeout, LogData trace) { var result = new Promise <T>(); result.CancelTarget(timeout); task .ContinueWith(t => { if (t.IsFaulted) { Exception ex = t.Exception; while (ex != null && ex.InnerException != null && ex.GetType().Name == "AggregateException") { ex = ex.InnerException; } if (ex != null) { foreach (var kvp in trace) { ex.Data[kvp.Key] = kvp.Value; } trace.Add("exception", ex); } result.Reject(ex); } else if (t.IsCanceled) { result.Reject(new HttpTimeoutException(string.Format("A response was not received after waiting for {0:m' minutes, 's' seconds'}", TimeSpan.FromMilliseconds(timeout.TimeoutDelay)))); } else { result.Resolve(t.Result); } }); return(result); }
private static IPromise <IReadOnlyResult> ApplyAsyncInt(this IAsyncConnection conn, Command query, CancellationToken ct, params object[] parameters) { var result = new Promise <IReadOnlyResult>(); query.WithAml(query.Aml, parameters); ct.Register(() => result.Cancel()); result.CancelTarget( conn.Process(query, true) .Progress((p, m) => result.Notify(p, m)) .Done(r => { try { if (query.Action == CommandAction.ApplySQL) { var res = ElementFactory.Utc.FromXml(r, query, conn); result.Resolve(res); } else { var res = conn.AmlContext.FromXml(r, query, conn); result.Resolve(res); } } catch (Exception ex) { result.Reject(ex); } }).Fail(ex => { result.Reject(ex); })); return(result); }
/// <summary> /// Asynchronously gets an HTTP connection to an innovator instance (or proxy) at the given URL /// </summary> /// <param name="preferences">Object containing preferences for the connection</param> /// <param name="async">Whether or not to return the connection asynchronously. This is important /// as an HTTP request must be issued to determine the type of connection to create</param> /// <returns>A promise to return a connection object</returns> public static IPromise <IRemoteConnection> GetConnection(ConnectionPreferences preferences, bool async) { preferences = preferences ?? new ConnectionPreferences(); var url = preferences.Url; url = (url ?? "").TrimEnd('/'); if (url.EndsWith("Server/InnovatorServer.aspx", StringComparison.OrdinalIgnoreCase)) { url = url.Substring(0, url.Length - 21); } if (!url.EndsWith("/server", StringComparison.OrdinalIgnoreCase)) { url += "/Server"; } var configUrl = url + "/mapping.xml"; var masterService = preferences.HttpService ?? DefaultService.Invoke(); var arasSerice = preferences.HttpService ?? DefaultService.Invoke(); Func <ServerMapping, IRemoteConnection> connFactory = m => { var uri = (m.Url ?? "").TrimEnd('/'); if (!uri.EndsWith("/server", StringComparison.OrdinalIgnoreCase)) { url += "/Server"; } switch (m.Type) { case ServerType.Proxy: throw new NotSupportedException(); default: return(ArasConn(arasSerice, uri, preferences)); } }; var result = new Promise <IRemoteConnection>(); var req = new HttpRequest(); req.UserAgent = preferences.Headers.UserAgent; req.SetHeader("Accept", "text/xml"); foreach (var header in preferences.Headers.NonUserAgentHeaders()) { req.SetHeader(header.Key, header.Value); } var trace = new LogData(4, "Innovator: Try to download mapping file", Factory.LogListener) { { "url", configUrl }, }; result.CancelTarget(masterService.GetPromise(new Uri(configUrl), async, trace, req) .Progress((p, m) => result.Notify(p, m)) .Done(r => { var data = r.AsString(); if (string.IsNullOrEmpty(data)) { result.Resolve(ArasConn(arasSerice, url, preferences)); } else { try { var servers = ServerMapping.FromXml(data).ToArray(); if (servers.Length < 1) { result.Resolve(ArasConn(arasSerice, url, preferences)); } else if (servers.Length == 1) { result.Resolve(connFactory(servers.Single())); } else { foreach (var server in servers) { server.Factory = connFactory; } result.Resolve(new MappedConnection(servers, preferences.AuthCallback)); } } catch (XmlException) { result.Resolve(ArasConn(arasSerice, url, preferences)); } } }).Fail(ex => { result.Resolve(ArasConn(arasSerice, url, preferences)); })).Always(trace.Dispose); if (preferences.Credentials != null) { IRemoteConnection conn = null; return(result .Continue(c => { conn = c; return c.Login(preferences.Credentials, async); }) .Convert(u => conn)); } return(result); }