public void AddLine_ThrowsException_IfLineIsNull() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); Assert.Throws<ArgumentNullException>("line", () => context.AddLine(null)); }
public NonEscapedString Render(HtmlHelper helper, IDictionary <string, string> stateData) { var builder = new StringBuilder(); builder.Append("<dl class=\"dl-horizontal\">"); string serverId = null; if (stateData.ContainsKey("ServerId")) { serverId = stateData["ServerId"]; } else if (stateData.ContainsKey("ServerName")) { serverId = stateData["ServerName"]; } if (serverId != null) { builder.Append("<dt>Server:</dt>"); builder.Append($"<dd>{helper.ServerId(serverId)}</dd>"); } if (stateData.ContainsKey("WorkerId")) { builder.Append("<dt>Worker:</dt>"); builder.Append($"<dd>{stateData["WorkerId"].Substring(0, 8)}</dd>"); } else if (stateData.ContainsKey("WorkerNumber")) { builder.Append("<dt>Worker:</dt>"); builder.Append($"<dd>#{stateData["WorkerNumber"]}</dd>"); } builder.Append("</dl>"); var page = helper.GetPage(); if (page.RequestPath.StartsWith("/jobs/details/")) { // We cannot cast page to an internal type JobDetailsPage to get jobId :( var jobId = page.RequestPath.Substring("/jobs/details/".Length); var startedAt = JobHelper.DeserializeDateTime(stateData["StartedAt"]); var consoleId = new ConsoleId(jobId, startedAt); builder.Append("<div class=\"console-area\">"); builder.AppendFormat("<div class=\"console\" data-id=\"{0}\">", consoleId); using (var storage = new ConsoleStorage(page.Storage.GetConnection())) { ConsoleRenderer.RenderLineBuffer(builder, storage, consoleId, 0); } builder.Append("</div>"); builder.Append("</div>"); } return(new NonEscapedString(builder.ToString())); }
public double?GetProgress(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } var progress = _connection.GetValueFromHash(consoleId.GetHashKey(), "progress"); if (string.IsNullOrEmpty(progress)) { // progress value is not set return(null); } try { return(double.Parse(progress, CultureInfo.InvariantCulture)); } catch (Exception) { // corrupted data? return(null); } }
public Task Dispatch(DashboardContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var consoleId = ConsoleId.Parse(context.UriMatch.Groups[1].Value); var startArg = context.Request.GetQuery("start"); // try to parse offset at which we should start returning requests int start; if (string.IsNullOrEmpty(startArg) || !int.TryParse(startArg, out start)) { // if not provided or invalid, fetch records from the very start start = 0; } var buffer = new StringBuilder(); using (var data = new ConsoleStorage(context.Storage.GetConnection())) { ConsoleRenderer.RenderLineBuffer(buffer, data, consoleId, start); } context.Response.ContentType = "text/html"; return(context.Response.WriteAsync(buffer.ToString())); }
public void IgnoresFractionalMilliseconds() { var x = new ConsoleId("123", UnixEpoch.AddMilliseconds(3.0)); var y = new ConsoleId("123", UnixEpoch.AddMilliseconds(3.0215)); Assert.Equal(x, y); }
public void DeserializesCorrectly() { var x = ConsoleId.Parse("00cdb7af151123"); Assert.Equal("123", x.JobId); Assert.Equal(new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc), x.DateValue); }
public void Ctor_InitializesConsole() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); _storage.Verify(x => x.InitConsole(consoleId)); }
public void SerializesCorrectly() { var x = new ConsoleId("123", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var s = x.ToString(); Assert.Equal("00cdb7af151123", s); }
public void WriteLine_ReallyAddsLine() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); context.AddLine(new ConsoleLine() { TimeOffset = 0, Message = "line" }); _storage.Verify(x => x.AddLine(It.IsAny<ConsoleId>(), It.IsAny<ConsoleLine>())); }
public void WriteLine_ReallyAddsLineWithColor() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); context.WriteLine("line", ConsoleTextColor.Red); _storage.Verify(x => x.AddLine(It.IsAny <ConsoleId>(), It.Is <ConsoleLine>(_ => _.Message == "line" && _.TextColor == ConsoleTextColor.Red))); }
public StateData GetState(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } return(_connection.GetStateData(consoleId.JobId)); }
public int GetLineCount(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } return((int)_connection.GetSetCount(consoleId.ToString())); }
public void Expire_ReallyExpiresLines() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); context.Expire(TimeSpan.FromHours(1)); _storage.Verify(x => x.Expire(It.IsAny<ConsoleId>(), It.IsAny<TimeSpan>())); }
public TimeSpan GetConsoleTtl(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } return(_connection.GetHashTtl(consoleId.GetHashKey())); }
public IList <LineDto> GetLines(string jobId, DateTime timestamp, LineType type = LineType.Any) { var consoleId = new ConsoleId(jobId, timestamp); var count = _storage.GetLineCount(consoleId); var result = new List <LineDto>(count); if (count > 0) { Dictionary <string, ProgressBarDto> progressBars = null; foreach (var entry in _storage.GetLines(consoleId, 0, count)) { if (entry.ProgressValue.HasValue) { if (type == LineType.Text) { continue; } // aggregate progress value updates into single record if (progressBars != null) { ProgressBarDto prev; if (progressBars.TryGetValue(entry.Message, out prev)) { prev.Progress = entry.ProgressValue.Value; prev.Color = entry.TextColor; continue; } } else { progressBars = new Dictionary <string, ProgressBarDto>(); } var line = new ProgressBarDto(entry, timestamp); progressBars.Add(entry.Message, line); result.Add(line); } else { if (type == LineType.ProgressBar) { continue; } result.Add(new TextLineDto(entry, timestamp)); } } } return(result); }
public void WriteProgressBar_WritesProgressBarColor_AndReturnsNonNull() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); var progressBar = context.WriteProgressBar(null, 0, ConsoleTextColor.Red); _storage.Verify(x => x.AddLine(It.IsAny <ConsoleId>(), It.Is <ConsoleLine>(_ => _.TextColor == ConsoleTextColor.Red))); Assert.NotNull(progressBar); }
public ConsoleContext(ConsoleId consoleId, IConsoleStorage storage) { _consoleId = consoleId ?? throw new ArgumentNullException(nameof(consoleId)); _storage = storage ?? throw new ArgumentNullException(nameof(storage)); _lastTimeOffset = 0; _nextProgressBarId = 0; _storage.InitConsole(_consoleId); }
public void WriteProgressBar_WritesDefaultValue_AndReturnsNonNull() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); var progressBar = context.WriteProgressBar(0, null); _storage.Verify(x => x.AddLine(It.IsAny<ConsoleId>(), It.IsAny<ConsoleLine>())); Assert.NotNull(progressBar); }
public ConsoleStorageFacts() { _consoleId = new ConsoleId("1", DateTime.UtcNow); _connection = new Mock <JobStorageConnection>(); _transaction = new Mock <JobStorageTransaction>(); _connection.Setup(x => x.CreateWriteTransaction()) .Returns(_transaction.Object); }
public void AddLine(ConsoleId consoleId, ConsoleLine line) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } if (line == null) { throw new ArgumentNullException(nameof(line)); } if (line.IsReference) { throw new ArgumentException("Cannot add reference directly", nameof(line)); } using (var tran = _connection.CreateWriteTransaction()) { // check if encoded message fits into Set's Value field string value; if (line.Message.Length > ValueFieldLimit - 36) { // pretty sure it won't fit // (36 is an upper bound for JSON formatting, TimeOffset and TextColor) value = null; } else { // try to encode and see if it fits value = JobHelper.ToJson(line); if (value.Length > ValueFieldLimit) { value = null; } } if (value == null) { var referenceKey = Guid.NewGuid().ToString("N"); tran.SetRangeInHash(consoleId.ToString(), new[] { new KeyValuePair <string, string>(referenceKey, line.Message) }); line.Message = referenceKey; line.IsReference = true; value = JobHelper.ToJson(line); } tran.AddToSet(consoleId.ToString(), value); tran.Commit(); } }
public void InitConsole(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } // We add an extra "jobId" record into Hash for console, // to correctly track TTL even if console contains no lines _connection.SetRangeInHash(consoleId.GetHashKey(), new[] { new KeyValuePair <string, string>("jobId", consoleId.JobId) }); }
public void FixExpiration_RequestsConsoleTtl_ExpiresIfPositive() { _storage.Setup(x => x.GetConsoleTtl(It.IsAny<ConsoleId>())) .Returns(TimeSpan.FromHours(1)); var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); context.FixExpiration(); _storage.Verify(x => x.GetConsoleTtl(It.IsAny<ConsoleId>())); _storage.Verify(x => x.Expire(It.IsAny<ConsoleId>(), It.IsAny<TimeSpan>())); }
public void AddLine_CorrectsTimeOffset() { var consoleId = new ConsoleId("1", new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc)); var context = new ConsoleContext(consoleId, _storage.Object); var line1 = new ConsoleLine() { TimeOffset = 0, Message = "line" }; var line2 = new ConsoleLine() { TimeOffset = 0, Message = "line" }; context.AddLine(line1); context.AddLine(line2); _storage.Verify(x => x.AddLine(It.IsAny<ConsoleId>(), It.IsAny<ConsoleLine>()), Times.Exactly(2)); Assert.NotEqual(line1.TimeOffset, line2.TimeOffset, 4); }
public void Expire(ConsoleId consoleId, TimeSpan expireIn) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } using (var tran = (JobStorageTransaction)_connection.CreateWriteTransaction()) { tran.ExpireSet(consoleId.ToString(), expireIn); tran.ExpireHash(consoleId.ToString(), expireIn); tran.Commit(); } }
public void Expire(ConsoleId consoleId, TimeSpan expireIn) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } using (var tran = (JobStorageTransaction)_connection.CreateWriteTransaction()) using (var expiration = new ConsoleExpirationTransaction(tran)) { expiration.Expire(consoleId, expireIn); tran.Commit(); } }
public IEnumerable <ConsoleLine> GetLines(ConsoleId consoleId, int start, int end) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } var useOldKeys = false; var items = _connection.GetRangeFromSet(consoleId.GetSetKey(), start, end); if (items == null || items.Count == 0) { // Read operations should be backwards compatible and use // old keys, if new one don't contain any data. items = _connection.GetRangeFromSet(consoleId.GetOldConsoleKey(), start, end); useOldKeys = true; } foreach (var item in items) { var line = JobHelper.FromJson <ConsoleLine>(item); if (line.IsReference) { if (useOldKeys) { try { line.Message = _connection.GetValueFromHash(consoleId.GetOldConsoleKey(), line.Message); } catch { // This may happen, when using Hangfire.Redis storage and having // background job, whose console session was stored using old key // format. } } else { line.Message = _connection.GetValueFromHash(consoleId.GetHashKey(), line.Message); } line.IsReference = false; } yield return(line); } }
public void Persist(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } _transaction.PersistSet(consoleId.GetSetKey()); _transaction.PersistHash(consoleId.GetHashKey()); // After upgrading to Hangfire.Console version with new keys, // there may be existing background jobs with console attached // to the previous keys. We should persist them also. _transaction.PersistSet(consoleId.GetOldConsoleKey()); _transaction.PersistHash(consoleId.GetOldConsoleKey()); }
public ConsoleContext(ConsoleId consoleId, IConsoleStorage storage) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } if (storage == null) { throw new ArgumentNullException(nameof(storage)); } _consoleId = consoleId; _storage = storage; _lastTimeOffset = 0; _nextProgressBarId = 0; }
public async Task Dispatch(DashboardContext context) { if (!"POST".Equals(context.Request.Method, StringComparison.OrdinalIgnoreCase)) { context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; return; } var result = new Dictionary <string, double>(); var jobIds = await context.Request.GetFormValuesAsync("jobs[]"); if (jobIds.Count > 0) { // there are some jobs to process using (var connection = context.Storage.GetConnection()) using (var storage = new ConsoleStorage(connection)) { foreach (var jobId in jobIds) { var state = connection.GetStateData(jobId); if (state != null && string.Equals(state.Name, ProcessingState.StateName, StringComparison.OrdinalIgnoreCase)) { var consoleId = new ConsoleId(jobId, JobHelper.DeserializeDateTime(state.Data["StartedAt"])); var progress = storage.GetProgress(consoleId); if (progress.HasValue) { result[jobId] = progress.Value; } } else { // return -1 to indicate the job is not in Processing state result[jobId] = -1; } } } } var serialized = JsonConvert.SerializeObject(result, JsonSettings); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(serialized); }
public int GetLineCount(ConsoleId consoleId) { if (consoleId == null) { throw new ArgumentNullException(nameof(consoleId)); } var result = (int)_connection.GetSetCount(consoleId.GetSetKey()); if (result == 0) { // Read operations should be backwards compatible and use // old keys, if new one don't contain any data. return((int)_connection.GetSetCount(consoleId.GetOldConsoleKey())); } return(result); }