Example #1
0
        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()));
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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));
        }
Example #8
0
        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);
        }
Example #9
0
        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>()));
        }
Example #10
0
        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()));
        }
Example #13
0
        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()));
        }
Example #15
0
        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);
        }
Example #16
0
        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);
        }
Example #17
0
        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);
        }
Example #18
0
        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);
        }
Example #19
0
        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();
            }
        }
Example #21
0
        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) });
        }
Example #22
0
        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>()));
        }
Example #23
0
        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);
            }
        }
Example #27
0
        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);
        }