Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RepoRegistry: refactor format and access directly from verbs #216

Merged
merged 16 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 2 additions & 0 deletions Scalar.Build/GenerateVersionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ public override bool Execute()
this.AssemblyVersion,
string.Format(
@"using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyVersion(""{0}"")]
[assembly: AssemblyFileVersion(""{0}"")]
[assembly: InternalsVisibleTo(""Scalar.UnitTests"")]
",
this.Version));

Expand Down
5 changes: 5 additions & 0 deletions Scalar.Common/FileSystem/PhysicalFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ public virtual IEnumerable<string> EnumerateDirectories(string path)
return Directory.EnumerateDirectories(path);
}

public virtual IEnumerable<string> EnumerateFiles(string path, string searchPattern)
{
return Directory.EnumerateFiles(path, searchPattern);
}

public virtual FileProperties GetFileProperties(string path)
{
FileInfo entry = new FileInfo(path);
Expand Down
76 changes: 0 additions & 76 deletions Scalar.Common/NamedPipes/NamedPipeMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,82 +116,6 @@ public override string ToString()
}
}

public class UnregisterRepoRequest
{
public const string Header = nameof(UnregisterRepoRequest);

public string EnlistmentRoot { get; set; }

public static UnregisterRepoRequest FromMessage(Message message)
{
return JsonConvert.DeserializeObject<UnregisterRepoRequest>(message.Body);
}

public Message ToMessage()
{
return new Message(Header, JsonConvert.SerializeObject(this));
}

public class Response : BaseResponse<UnregisterRepoRequest>
{
public static Response FromMessage(Message message)
{
return JsonConvert.DeserializeObject<Response>(message.Body);
}
}
}

public class RegisterRepoRequest
{
public const string Header = nameof(RegisterRepoRequest);

public string EnlistmentRoot { get; set; }
public string OwnerSID { get; set; }

public static RegisterRepoRequest FromMessage(Message message)
{
return JsonConvert.DeserializeObject<RegisterRepoRequest>(message.Body);
}

public Message ToMessage()
{
return new Message(Header, JsonConvert.SerializeObject(this));
}

public class Response : BaseResponse<RegisterRepoRequest>
{
public static Response FromMessage(Message message)
{
return JsonConvert.DeserializeObject<Response>(message.Body);
}
}
}

public class GetActiveRepoListRequest
{
public const string Header = nameof(GetActiveRepoListRequest);

public static GetActiveRepoListRequest FromMessage(Message message)
{
return JsonConvert.DeserializeObject<GetActiveRepoListRequest>(message.Body);
}

public Message ToMessage()
{
return new Message(Header, JsonConvert.SerializeObject(this));
}

public class Response : BaseResponse<GetActiveRepoListRequest>
{
public List<string> RepoList { get; set; }

public static Response FromMessage(Message message)
{
return JsonConvert.DeserializeObject<Response>(message.Body);
}
}
}

public class BaseResponse<TRequest>
{
public const string Header = nameof(TRequest) + ResponseSuffix;
Expand Down
11 changes: 11 additions & 0 deletions Scalar.Common/RepoRegistry/IScalarRepoRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace Scalar.Common.RepoRegistry
{
public interface IScalarRepoRegistry
{
bool TryRegisterRepo(string normalizedRepoRoot, string userId, out string errorMessage);
bool TryUnregisterRepo(string normalizedRepoRoot, out string errorMessage);
IEnumerable<ScalarRepoRegistration> GetRegisteredRepos();
}
}
14 changes: 14 additions & 0 deletions Scalar.Common/RepoRegistry/IScalarRepoRegistryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Scalar.Common.RepoRegistry
{
public static class IScalarRepoRegistryExtensions
{
public static IEnumerable<ScalarRepoRegistration> GetRegisteredReposForUser(this IScalarRepoRegistry registry, string userId)
wilbaker marked this conversation as resolved.
Show resolved Hide resolved
{
return registry.GetRegisteredRepos().Where(x => x.UserId.Equals(userId, StringComparison.CurrentCultureIgnoreCase));
}
}
}
40 changes: 40 additions & 0 deletions Scalar.Common/RepoRegistry/ScalarRepoRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Newtonsoft.Json;

namespace Scalar.Common.RepoRegistry
{
public class ScalarRepoRegistration
{
public ScalarRepoRegistration()
{
}

public ScalarRepoRegistration(string normalizedRepoRoot, string userId)
{
this.NormalizedRepoRoot = normalizedRepoRoot;
this.UserId = userId;
}

public string NormalizedRepoRoot { get; set; }
public string UserId { get; set; }

public static ScalarRepoRegistration FromJson(string json)
{
return JsonConvert.DeserializeObject<ScalarRepoRegistration>(
json,
new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore
});
}

public override string ToString()
{
return $"({this.UserId}) {this.NormalizedRepoRoot}";
}

public string ToJson()
{
return JsonConvert.SerializeObject(this);
}
}
}
207 changes: 207 additions & 0 deletions Scalar.Common/RepoRegistry/ScalarRepoRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
using Scalar.Common.FileSystem;
using Scalar.Common.Tracing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;

namespace Scalar.Common.RepoRegistry
{
public class ScalarRepoRegistry : IScalarRepoRegistry
{
private const string EtwArea = nameof(ScalarRepoRegistry);
private const string RegistryFileExtension = ".repo";
private const string RegistryTempFileExtension = ".temp";

private string registryFolderPath;
private ITracer tracer;
private PhysicalFileSystem fileSystem;
wilbaker marked this conversation as resolved.
Show resolved Hide resolved

public ScalarRepoRegistry(
ITracer tracer,
PhysicalFileSystem fileSystem,
string repoRegistryLocation)
{
this.tracer = tracer;
this.fileSystem = fileSystem;
this.registryFolderPath = repoRegistryLocation;
}

public bool TryRegisterRepo(string normalizedRepoRoot, string userId, out string errorMessage)
{
try
{
if (!this.fileSystem.DirectoryExists(this.registryFolderPath))
wilbaker marked this conversation as resolved.
Show resolved Hide resolved
{
EventMetadata metadata = CreateEventMetadata();
metadata.Add(nameof(this.registryFolderPath), this.registryFolderPath);
this.tracer.RelatedEvent(
EventLevel.Informational,
$"{nameof(this.TryRegisterRepo)}_CreatingRegistryDirectory",
metadata);

// TODO #136: Make sure this does the right thing with ACLs on Windows
this.fileSystem.CreateDirectory(this.registryFolderPath);
}
}
catch (Exception e)
{
errorMessage = $"Error while ensuring registry directory '{this.registryFolderPath}' exists: {e.Message}";

EventMetadata metadata = CreateEventMetadata(e);
metadata.Add(nameof(normalizedRepoRoot), normalizedRepoRoot);
metadata.Add(nameof(this.registryFolderPath), this.registryFolderPath);
this.tracer.RelatedError(metadata, $"{nameof(this.TryRegisterRepo)}: Exception while ensuring registry directory exists");
return false;
}

string tempRegistryPath = this.GetRepoRegistryTempFilePath(normalizedRepoRoot);

try
{
ScalarRepoRegistration repoRegistration = new ScalarRepoRegistration(normalizedRepoRoot, userId);
string registryFileContents = repoRegistration.ToJson();

using (Stream tempFile = this.fileSystem.OpenFileStream(
tempRegistryPath,
FileMode.Create,
FileAccess.Write,
FileShare.None,
callFlushFileBuffers: true))
using (StreamWriter writer = new StreamWriter(tempFile))
{
writer.WriteLine(registryFileContents);
tempFile.Flush();
}
}
catch (Exception e)
{
errorMessage = $"Error while registering repo {normalizedRepoRoot}: {e.Message}";

EventMetadata metadata = CreateEventMetadata(e);
metadata.Add(nameof(normalizedRepoRoot), normalizedRepoRoot);
metadata.Add(nameof(tempRegistryPath), tempRegistryPath);
this.tracer.RelatedError(metadata, $"{nameof(this.TryRegisterRepo)}: Exception while writing temp registry file");
return false;
}

string registryFilePath = this.GetRepoRegistryFilePath(normalizedRepoRoot);
try
{
this.fileSystem.MoveAndOverwriteFile(tempRegistryPath, registryFilePath);
}
catch (Win32Exception e)
{
errorMessage = $"Error while registering repo {normalizedRepoRoot}: {e.Message}";

EventMetadata metadata = CreateEventMetadata(e);
metadata.Add(nameof(normalizedRepoRoot), normalizedRepoRoot);
metadata.Add(nameof(tempRegistryPath), tempRegistryPath);
metadata.Add(nameof(registryFilePath), registryFilePath);
this.tracer.RelatedError(metadata, $"{nameof(this.TryRegisterRepo)}: Exception while renaming temp registry file");
return false;
}

errorMessage = null;
return true;
}

public bool TryUnregisterRepo(string normalizedRepoRoot, out string errorMessage)
{
string registryPath = this.GetRepoRegistryFilePath(normalizedRepoRoot);
if (!this.fileSystem.FileExists(registryPath))
{
errorMessage = $"Attempted to remove non-existent repo '{normalizedRepoRoot}'";

EventMetadata metadata = CreateEventMetadata();
metadata.Add(nameof(normalizedRepoRoot), normalizedRepoRoot);
metadata.Add(nameof(registryPath), registryPath);
this.tracer.RelatedWarning(
metadata,
$"{nameof(this.TryUnregisterRepo)}: Attempted to remove non-existent repo");

return false;
}

try
{
this.fileSystem.DeleteFile(registryPath);
}
catch (Exception e)
{
errorMessage = $"Error while removing repo {normalizedRepoRoot}: {e.Message}";

EventMetadata metadata = CreateEventMetadata(e);
metadata.Add(nameof(normalizedRepoRoot), normalizedRepoRoot);
metadata.Add(nameof(registryPath), registryPath);
this.tracer.RelatedWarning(
metadata,
$"{nameof(this.TryUnregisterRepo)}: Exception while removing repo");

return false;
}

errorMessage = null;
return true;
}

public IEnumerable<ScalarRepoRegistration> GetRegisteredRepos()
{
if (this.fileSystem.DirectoryExists(this.registryFolderPath))
{
IEnumerable<string> registryFilePaths = this.fileSystem.EnumerateFiles(this.registryFolderPath, $"*{RegistryFileExtension}");
foreach (string registryFilePath in registryFilePaths)
{
ScalarRepoRegistration registration = null;
try
{
string repoData = this.fileSystem.ReadAllText(registryFilePath);
registration = ScalarRepoRegistration.FromJson(repoData);
}
catch (Exception e)
{
EventMetadata metadata = CreateEventMetadata(e);
metadata.Add(nameof(registryFilePath), registryFilePath);
this.tracer.RelatedWarning(
metadata,
$"{nameof(this.GetRegisteredRepos)}: Failed to read registry file");
}

if (registration != null)
{
yield return registration;
}
}
}
}

internal static string GetRepoRootSha(string normalizedRepoRoot)
wilbaker marked this conversation as resolved.
Show resolved Hide resolved
{
return SHA1Util.SHA1HashStringForUTF8String(normalizedRepoRoot.ToLowerInvariant());
}

private static EventMetadata CreateEventMetadata(Exception e = null)
{
EventMetadata metadata = new EventMetadata();
metadata.Add("Area", EtwArea);
if (e != null)
{
metadata.Add("Exception", e.ToString());
}

return metadata;
}

private string GetRepoRegistryTempFilePath(string normalizedRepoRoot)
{
string repoTempFilename = $"{GetRepoRootSha(normalizedRepoRoot)}{RegistryTempFileExtension}";
return Path.Combine(this.registryFolderPath, repoTempFilename);
}

private string GetRepoRegistryFilePath(string normalizedRepoRoot)
{
string repoFilename = $"{GetRepoRootSha(normalizedRepoRoot)}{RegistryFileExtension}";
return Path.Combine(this.registryFolderPath, repoFilename);
}
}
}
5 changes: 5 additions & 0 deletions Scalar.Common/ScalarConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public static class Service
public const string UIName = "Scalar.Service.UI";
}

public static class RepoRegistry
{
public const string RegistryDirectoryName = "Scalar.RepoRegistry";
}

public static class MediaTypes
{
public const string PrefetchPackFilesAndIndexesMediaType = "application/x-gvfs-timestamped-packfiles-indexes";
Expand Down
Loading