Skip to content
This repository has been archived by the owner on Aug 3, 2024. It is now read-only.
/ ServerCommon Public archive

Add status classes and tests #170

Merged
merged 14 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions NuGet.Server.Common.sln
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Sql", "src\N
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Sql.Tests", "tests\NuGet.Services.Sql.Tests\NuGet.Services.Sql.Tests.csproj", "{FA2B3447-7242-495F-A8E1-D94181C0C9A5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Status", "src\NuGet.Services.Status\NuGet.Services.Status.csproj", "{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Status.Tests", "tests\NuGet.Services.Status.Tests\NuGet.Services.Status.Tests.csproj", "{EA54BC71-F9AD-4863-8DC6-8CC41776C881}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -168,6 +172,14 @@ Global
{FA2B3447-7242-495F-A8E1-D94181C0C9A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA2B3447-7242-495F-A8E1-D94181C0C9A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA2B3447-7242-495F-A8E1-D94181C0C9A5}.Release|Any CPU.Build.0 = Release|Any CPU
{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056}.Release|Any CPU.Build.0 = Release|Any CPU
{EA54BC71-F9AD-4863-8DC6-8CC41776C881}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA54BC71-F9AD-4863-8DC6-8CC41776C881}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA54BC71-F9AD-4863-8DC6-8CC41776C881}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA54BC71-F9AD-4863-8DC6-8CC41776C881}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -198,6 +210,8 @@ Global
{E29F54DF-DFB8-4E27-940D-21ECCB9B6FC1} = {7783A106-0F4C-4055-9AB4-413FB2C7B8F0}
{F5121B0A-669F-48BD-86DC-27C546D1A825} = {8415FED7-1BED-4227-8B4F-BB7C24E041CD}
{FA2B3447-7242-495F-A8E1-D94181C0C9A5} = {7783A106-0F4C-4055-9AB4-413FB2C7B8F0}
{D3AB8DBD-EF83-41A5-AF25-0A7E7FACC056} = {8415FED7-1BED-4227-8B4F-BB7C24E041CD}
{EA54BC71-F9AD-4863-8DC6-8CC41776C881} = {7783A106-0F4C-4055-9AB4-413FB2C7B8F0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AA413DB0-5475-4B5D-A3AF-6323DA8D538B}
Expand Down
6 changes: 4 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ Invoke-BuildStep 'Set version metadata in AssemblyInfo.cs' { `
"$PSScriptRoot\src\NuGet.Services.ServiceBus\Properties\AssemblyInfo.g.cs", `
"$PSScriptRoot\src\NuGet.Services.Validation\Properties\AssemblyInfo.g.cs", `
"$PSScriptRoot\src\NuGet.Services.Validation.Issues\Properties\AssemblyInfo.g.cs", `
"$PSScriptRoot\src\NuGet.Services.Sql\Properties\AssemblyInfo.g.cs"
"$PSScriptRoot\src\NuGet.Services.Sql\Properties\AssemblyInfo.g.cs", `
"$PSScriptRoot\src\NuGet.Services.Status\Properties\AssemblyInfo.g.cs"

$versionMetadata | ForEach-Object {
Set-VersionInfo -Path $_ -Version $SimpleVersion -Branch $Branch -Commit $CommitSHA
Expand Down Expand Up @@ -105,7 +106,8 @@ Invoke-BuildStep 'Creating artifacts' { `
"src\NuGet.Services.ServiceBus\NuGet.Services.ServiceBus.csproj", `
"src\NuGet.Services.Validation\NuGet.Services.Validation.csproj", `
"src\NuGet.Services.Validation.Issues\NuGet.Services.Validation.Issues.csproj", `
"src\NuGet.Services.Sql\NuGet.Services.Sql.csproj"
"src\NuGet.Services.Sql\NuGet.Services.Sql.csproj", `
"src\NuGet.Services.Status\NuGet.Services.Status.csproj"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

csproj [](start = 61, length = 6)

Shouldn't you update test.ps1 too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!


$projects | ForEach-Object {
New-Package (Join-Path $PSScriptRoot $_) -Configuration $Configuration -Symbols -IncludeReferencedProjects -MSBuildVersion "15"
Expand Down
41 changes: 41 additions & 0 deletions src/NuGet.Services.Status/Component.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;

namespace NuGet.Services.Status
{
/// <summary>
/// Default implementation of <see cref="IComponent"/>.
/// </summary>
public abstract class Component : IComponent
{
public string Name { get; }
public string Description { get; }
public abstract ComponentStatus Status { get; set; }
public IEnumerable<IComponent> SubComponents { get; }
IEnumerable<IReadOnlyComponent> IReadOnlyComponent.SubComponents => SubComponents;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IEnumerable [](start = 8, length = 11)

Should this guy be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the inheritance so that this property no longer needs to exist.

public string Path => Name;

public Component(
string name,
string description)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
Description = description ?? "";
SubComponents = Enumerable.Empty<IComponent>();
}

public Component(
string name,
string description,
IEnumerable<IComponent> subComponents)
: this(name, description)
{
SubComponents = subComponents?.Select(s => new SubComponent(s, this))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Select [](start = 43, length = 6)

You should ToList this. The IEnumerable could be some crazy LINQ query that people will have to enumerate every time they call SubComponents.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Also made this fix to several other component classes

?? throw new ArgumentNullException(nameof(subComponents));
}
}
}
26 changes: 26 additions & 0 deletions src/NuGet.Services.Status/ComponentStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGet.Services.Status
{
/// <summary>
/// Describes whether or not a <see cref="IReadOnlyComponent"/> is performing as expected.
/// </summary>
public enum ComponentStatus
{
/// <summary>
/// The <see cref="IReadOnlyComponent"/> is performing as expected.
/// </summary>
Up,

/// <summary>
/// Some portion of the <see cref="IReadOnlyComponent"/> is not performing as expected.
/// </summary>
Degraded,

/// <summary>
/// The <see cref="IReadOnlyComponent"/> is completely unfunctional.
/// </summary>
Down
}
}
69 changes: 69 additions & 0 deletions src/NuGet.Services.Status/ComponentUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq;

namespace NuGet.Services.Status
{
public static class ComponentUtility
{
public static string GetPath(params string[] componentNames)
{
return string.Join(Constants.ComponentPathDivider.ToString(), componentNames);
}

public static IReadOnlyComponent GetByPath(this IReadOnlyComponent root, string path)
{
var componentNames = path.Split(Constants.ComponentPathDivider);
return root.GetByPath(componentNames);
}

public static IReadOnlyComponent GetByPath(this IReadOnlyComponent root, params string[] componentNames)
{
if (componentNames.First() != root.Name)
{
return null;
}

IReadOnlyComponent component = root;
foreach (var componentName in componentNames.Skip(1))
{
component = component.SubComponents.FirstOrDefault(c => c.Name == componentName);

if (component == null)
{
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break [](start = 20, length = 5)

If I run GetByPath('root', 'this', 'doesnt', 'exist') and only the root component exists, this will return the root IReadOnlyComponent. Would it be better if this returned null instead? If so, I would rename the method to be GetByPathOrNull.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug. I added tests to make sure it doesn't break again.

}
}

return component;
}

public static IComponent GetByPath(this IComponent root, string path)
{
var componentNames = path.Split(Constants.ComponentPathDivider);
return root.GetByPath(componentNames);
}

public static IComponent GetByPath(this IComponent root, params string[] componentNames)
{
if (componentNames.First() != root.Name)
{
return null;
}

IComponent component = root;
foreach (var componentName in componentNames.Skip(1))
{
component = component.SubComponents.FirstOrDefault(c => c.Name == componentName);

if (component == null)
{
break;
}
}

return component;
}
}
}
17 changes: 17 additions & 0 deletions src/NuGet.Services.Status/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGet.Services.Status
{
public static class Constants
{
/// <summary>
/// Used to construct <see cref="IReadOnlyComponent.Path"/>.
/// </summary>
/// <example>
/// Suppose C is a subcomponent of B which is a subcomponent of A.
/// The path of C is <code>"A" + ComponentPathDivider + "B" + ComponentPathDivider + "C"</code>.
/// </example>
public static char ComponentPathDivider = '/';
}
}
48 changes: 48 additions & 0 deletions src/NuGet.Services.Status/Event.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace NuGet.Services.Status
{
public class Event : IEvent
{
public string AffectedComponentPath { get; }
public ComponentStatus AffectedComponentStatus { get; }
public DateTime StartTime { get; }
public DateTime? EndTime { get; }
public IEnumerable<IMessage> Messages { get; }

public Event(
string affectedComponentPath,
ComponentStatus affectedComponentStatus,
DateTime startTime,
DateTime? endTime,
IEnumerable<IMessage> messages)
{
AffectedComponentPath = affectedComponentPath;
AffectedComponentStatus = affectedComponentStatus;
StartTime = startTime;
EndTime = endTime;
Messages = messages;
}

[JsonConstructor]
public Event(
string affectedComponentPath,
ComponentStatus affectedComponentStatus,
DateTime startTime,
DateTime? endTime,
IEnumerable<Message> messages)
: this(
affectedComponentPath,
affectedComponentStatus,
startTime,
endTime,
(IEnumerable<IMessage>)messages)
{
}
}
}
16 changes: 16 additions & 0 deletions src/NuGet.Services.Status/IComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace NuGet.Services.Status
{
/// <summary>
/// A writable <see cref="IReadOnlyComponent"/> that allows setting its status.
/// </summary>
public interface IComponent : IReadOnlyComponent
{
new ComponentStatus Status { get; set; }
new IEnumerable<IComponent> SubComponents { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is new necessary? Why can't it reuse IReadOnlyComponent.SubComponentsorIComponentRoot.SubComponents`?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove this, IComponentRoot<IComponent>.SubComponents is sufficient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot that this needs to exist so that IComponent.SubComponents is not ambiguous between IEnumerable<IReadOnlyComponent> and IEnumerable<IComponent>.

}
}
35 changes: 35 additions & 0 deletions src/NuGet.Services.Status/IEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;

namespace NuGet.Services.Status
{
/// <summary>
/// Represents an event affecting a <see cref="IReadOnlyComponent"/>.
/// </summary>
public interface IEvent
{
/// <summary>
/// The <see cref="IReadOnlyComponent.Path"/> of the <see cref="IReadOnlyComponent"/> affected.
/// </summary>
string AffectedComponentPath { get; }
/// <summary>
/// The <see cref="IReadOnlyComponent.Status"/> of the <see cref="IReadOnlyComponent"/> affected.
/// </summary>
ComponentStatus AffectedComponentStatus { get; }
/// <summary>
/// When the event began.
/// </summary>
DateTime StartTime { get; }
/// <summary>
/// When the event ended, or <c>null</c> if the event is still active.
/// </summary>
DateTime? EndTime { get; }
/// <summary>
/// A set of <see cref="IMessage"/>s related to this event.
/// </summary>
IEnumerable<IMessage> Messages { get; }
}
}
22 changes: 22 additions & 0 deletions src/NuGet.Services.Status/IMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace NuGet.Services.Status
{
/// <summary>
/// A message associated with an <see cref="IEvent"/>.
/// </summary>
public interface IMessage
{
/// <summary>
/// The time the message was posted.
/// </summary>
DateTime Time { get; }
/// <summary>
/// The contents of the message.
/// </summary>
string Contents { get; }
}
}
35 changes: 35 additions & 0 deletions src/NuGet.Services.Status/IReadOnlyComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;

namespace NuGet.Services.Status
{
/// <summary>
/// Represents a part of the service that has a status.
/// </summary>
public interface IReadOnlyComponent
{
/// <summary>
/// The name of the part of the service.
/// </summary>
string Name { get; }
/// <summary>
/// A description of what the part of the service does.
/// </summary>
string Description { get; }
/// <summary>
/// The status of the part of the service.
/// </summary>
ComponentStatus Status { get; }
/// <summary>
/// A list of subcomponents that make up this part of the service.
/// </summary>
IEnumerable<IReadOnlyComponent> SubComponents { get; }
/// <summary>
/// A string path used to identify this part of the service when accessed by a root component.
/// For example, if "A" is a component with a subcomponent "B", the path of "B" is "A/B".
/// </summary>
string Path { get; }
}
}
20 changes: 20 additions & 0 deletions src/NuGet.Services.Status/LeafComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGet.Services.Status
{
/// <summary>
/// A <see cref="Component"/> that has no children.
/// </summary>
public class LeafComponent : Component
{
public override ComponentStatus Status { get; set; }

public LeafComponent(
string name,
string description)
: base(name, description)
{
}
}
}
Loading