Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Nigusu-Allehu committed Oct 15, 2024
1 parent 63916a0 commit 3b03121
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 223 deletions.
23 changes: 16 additions & 7 deletions src/NuGet.Core/NuGet.ProjectModel/CacheFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using NuGet.Common;
using NuGet.Shared;

Expand All @@ -12,34 +13,42 @@ public class CacheFile : IEquatable<CacheFile>
{
internal const int CurrentVersion = 2;

public string DgSpecHash { get; }

[JsonPropertyName(CacheFileProperties.VersionProperty)]
public int Version { get; set; }

[JsonPropertyName(CacheFileProperties.DGSpecHashProperty)]
public string DgSpecHash { get; }

[JsonPropertyName(CacheFileProperties.SuccessProperty)]
public bool Success { get; set; }

/// <summary>
/// Gets or sets the full path to the project file.
/// </summary>
[JsonPropertyName(CacheFileProperties.ProjectFilePathProperty)]
public string ProjectFilePath { get; set; }

/// <summary>
/// Gets or sets a list of package paths that must exist in order for the project to be considered up-to-date.
/// </summary>
[JsonPropertyName(CacheFileProperties.ExpectedPackageFilesProperty)]
public IList<string> ExpectedPackageFilePaths { get; set; }

/// <summary>
/// Gets or sets a value indicating if one or more of the expected files are missing.
/// </summary>
[JsonIgnore]
[Obsolete("File existence checks are a function of time not the cache file content.")]
public bool HasAnyMissingPackageFiles
{
get => throw new NotImplementedException("This API is no longer support");
set => throw new NotImplementedException("This API is no longer support");
}

/// <summary>
/// Gets or sets the full path to the project file.
/// </summary>
public string ProjectFilePath { get; set; }

[JsonPropertyName(CacheFileProperties.LogsProperty)]
public IList<IAssetsLogMessage> LogMessages { get; set; }

[JsonIgnore]
public bool IsValid { get { return Version == CurrentVersion && Success && DgSpecHash != null; } }

public CacheFile(string dgSpecHash)
Expand Down
274 changes: 142 additions & 132 deletions src/NuGet.Core/NuGet.ProjectModel/CacheFileFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,169 @@
using System.Linq;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using NuGet.Common;

namespace NuGet.ProjectModel
{
public static class CacheFileFormat
{
private const string VersionProperty = "version";
private const string DGSpecHashProperty = "dgSpecHash";
private const string SuccessProperty = "success";
private const string ExpectedPackageFilesProperty = "expectedPackageFiles";
private const string ProjectFilePathProperty = "projectFilePath";

public static CacheFile Read(Stream stream, ILogger log, string path)
private static JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (log == null) throw new ArgumentNullException(nameof(log));
if (path == null) throw new ArgumentNullException(nameof(path));
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
Converters = { new AssetsLogMessageConverter() }
};

/// <summary>
/// since Log messages property in CacheFile is an interface type, we have the following custom converter to deserialize the IAssetsLogMessage objects.
/// </summary>
private class AssetsLogMessageConverter : JsonConverter<IAssetsLogMessage>
{
public override IAssetsLogMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
AssetsLogMessage assetsLogMessage = null;

if (reader.TokenType == JsonTokenType.StartObject)
{
using (JsonDocument document = JsonDocument.ParseValue(ref reader))
{
JsonElement json = document.RootElement;
var level = json.GetProperty(LogMessageProperties.LEVEL).GetString();
var code = json.GetProperty(LogMessageProperties.CODE).GetString();
var message = json.GetProperty(LogMessageProperties.MESSAGE).GetString();

if (Enum.TryParse(level, out LogLevel logLevel) && Enum.TryParse(code, out NuGetLogCode logCode))
{
assetsLogMessage = new AssetsLogMessage(logLevel, logCode, message)
{
TargetGraphs = json.TryGetProperty(LogMessageProperties.TARGET_GRAPHS, out var targetGraphs)
? targetGraphs.EnumerateArray().Select(x => x.GetString()).ToList()
: new List<string>()
};

if (logLevel == LogLevel.Warning && json.TryGetProperty(LogMessageProperties.WARNING_LEVEL, out var warningLevel))
{
assetsLogMessage.WarningLevel = (WarningLevel)Enum.ToObject(typeof(WarningLevel), warningLevel.GetInt32());
}

assetsLogMessage.ProjectPath = options.GetType().GetProperty("ProjectPath")?.GetValue(options)?.ToString();

if (json.TryGetProperty(LogMessageProperties.FILE_PATH, out var filePath))
{
assetsLogMessage.FilePath = filePath.GetString();
}
else
{
assetsLogMessage.FilePath = assetsLogMessage.ProjectPath;
}

if (json.TryGetProperty(LogMessageProperties.START_LINE_NUMBER, out var startLineNumber))
{
assetsLogMessage.StartLineNumber = startLineNumber.GetInt32();
}

if (json.TryGetProperty(LogMessageProperties.START_COLUMN_NUMBER, out var startColumnNumber))
{
assetsLogMessage.StartColumnNumber = startColumnNumber.GetInt32();
}

if (json.TryGetProperty(LogMessageProperties.END_LINE_NUMBER, out var endLineNumber))
{
assetsLogMessage.EndLineNumber = endLineNumber.GetInt32();
}

if (json.TryGetProperty(LogMessageProperties.END_COLUMN_NUMBER, out var endColumnNumber))
{
assetsLogMessage.EndColumnNumber = endColumnNumber.GetInt32();
}

if (json.TryGetProperty(LogMessageProperties.LIBRARY_ID, out var libraryId))
{
assetsLogMessage.LibraryId = libraryId.GetString();
}
}
}
}

return assetsLogMessage;
}

using (var textReader = new StreamReader(stream))
public override void Write(Utf8JsonWriter writer, IAssetsLogMessage value, JsonSerializerOptions options)
{
return Read(textReader, log, path);
var logJson = new Dictionary<string, object>
{
[LogMessageProperties.CODE] = value.Code.ToString(),
[LogMessageProperties.LEVEL] = value.Level.ToString(),
[LogMessageProperties.MESSAGE] = value.Message
};

if (value.Level == LogLevel.Warning)
{
logJson[LogMessageProperties.WARNING_LEVEL] = value.WarningLevel;
}

if (!string.IsNullOrEmpty(value.FilePath) &&
(value.ProjectPath == null || !PathUtility.GetStringComparerBasedOnOS().Equals(value.FilePath, value.ProjectPath)))
{
logJson[LogMessageProperties.FILE_PATH] = value.FilePath;
}

if (value.StartLineNumber > 0)
{
logJson[LogMessageProperties.START_LINE_NUMBER] = value.StartLineNumber;
}

if (value.StartColumnNumber > 0)
{
logJson[LogMessageProperties.START_COLUMN_NUMBER] = value.StartColumnNumber;
}

if (value.EndLineNumber > 0)
{
logJson[LogMessageProperties.END_LINE_NUMBER] = value.EndLineNumber;
}

if (value.EndColumnNumber > 0)
{
logJson[LogMessageProperties.END_COLUMN_NUMBER] = value.EndColumnNumber;
}

if (!string.IsNullOrEmpty(value.LibraryId))
{
logJson[LogMessageProperties.LIBRARY_ID] = value.LibraryId;
}

if (value.TargetGraphs != null && value.TargetGraphs.Any() && value.TargetGraphs.All(l => !string.IsNullOrEmpty(l)))
{
logJson[LogMessageProperties.TARGET_GRAPHS] = value.TargetGraphs;
}

JsonSerializer.Serialize(writer, logJson, options);
}
}

private static CacheFile Read(TextReader reader, ILogger log, string path)
public static CacheFile Read(Stream stream, ILogger log, string path)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (log == null) throw new ArgumentNullException(nameof(log));
if (path == null) throw new ArgumentNullException(nameof(path));

try
{
string jsonString = reader.ReadToEnd();
var json = JsonDocument.Parse(jsonString);
var cacheFile = ReadCacheFile(json.RootElement);
var cacheFile = JsonSerializer.DeserializeAsync<CacheFile>(utf8Json: stream, SerializerOptions).GetAwaiter().GetResult();
return cacheFile;
}
catch (Exception ex)
catch (Exception ex) when (ex is ArgumentNullException || ex is JsonException || ex is NotSupportedException)
{
log.LogWarning(string.Format(CultureInfo.CurrentCulture,
Strings.Log_ProblemReadingCacheFile,
path, ex.Message));

// Parsing error, the cache file is invalid.
return new CacheFile(null);
}

return new CacheFile(null);
}

public static void Write(string filePath, CacheFile lockFile)
Expand Down Expand Up @@ -78,119 +199,8 @@ public static void Write(Stream stream, CacheFile cacheFile)

private static void Write(TextWriter textWriter, CacheFile cacheFile)
{
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
//options.Converters.Add(new LogMessageJsonConverter());
string jsonString = JsonSerializer.Serialize(GetCacheFile(cacheFile), options);
string jsonString = JsonSerializer.Serialize(cacheFile, SerializerOptions);
textWriter.Write(jsonString);
}

private static CacheFile ReadCacheFile(JsonElement cursor)
{
var version = cursor.GetProperty(VersionProperty).GetInt32();
var hash = cursor.GetProperty(DGSpecHashProperty).GetString();
var success = cursor.GetProperty(SuccessProperty).GetBoolean();
var cacheFile = new CacheFile(hash)
{
Version = version,
Success = success
};

if (version >= 2)
{
cacheFile.ProjectFilePath = cursor.GetProperty(ProjectFilePathProperty).GetString();
cacheFile.ExpectedPackageFilePaths = new List<string>();
foreach (JsonElement expectedFile in cursor.GetProperty(ExpectedPackageFilesProperty).EnumerateArray())
{
string path = expectedFile.GetString();

if (!string.IsNullOrWhiteSpace(path))
{
cacheFile.ExpectedPackageFilePaths.Add(path);
}
}

cacheFile.LogMessages = LockFileFormat.ReadLogMessageArray(cursor.GetProperty(LockFileFormat.LogsProperty), cacheFile.ProjectFilePath);
}

return cacheFile;
}

private static object GetCacheFile(CacheFile cacheFile)
{
var json = new Dictionary<string, object>();
json[VersionProperty] = cacheFile.Version;
json[DGSpecHashProperty] = cacheFile.DgSpecHash;
json[SuccessProperty] = cacheFile.Success;

if (cacheFile.Version >= 2)
{
json[ProjectFilePathProperty] = cacheFile.ProjectFilePath;
json[ExpectedPackageFilesProperty] = cacheFile.ExpectedPackageFilePaths;

if (cacheFile.LogMessages != null && cacheFile.LogMessages.Count > 0)
{
json[LockFileFormat.LogsProperty] = cacheFile.LogMessages.Select(log =>
{
var logJson = new Dictionary<string, object>();
logJson[LogMessageProperties.CODE] = log.Code.ToString();
logJson[LogMessageProperties.LEVEL] = log.Level.ToString();

if (log.Level == LogLevel.Warning)
{
logJson[LogMessageProperties.WARNING_LEVEL] = (int)log.WarningLevel;
}

if (!string.IsNullOrEmpty(log.FilePath) &&
(log.ProjectPath == null || !PathUtility.GetStringComparerBasedOnOS().Equals(log.FilePath, log.ProjectPath)))
{
logJson[LogMessageProperties.FILE_PATH] = log.FilePath;
}

if (log.StartLineNumber > 0)
{
logJson[LogMessageProperties.START_LINE_NUMBER] = log.StartLineNumber;
}

if (log.StartColumnNumber > 0)
{
logJson[LogMessageProperties.START_COLUMN_NUMBER] = log.StartColumnNumber;
}

if (log.EndLineNumber > 0)
{
logJson[LogMessageProperties.END_LINE_NUMBER] = log.EndLineNumber;
}

if (log.EndColumnNumber > 0)
{
logJson[LogMessageProperties.END_COLUMN_NUMBER] = log.EndColumnNumber;
}

if (!string.IsNullOrEmpty(log.Message))
{
logJson[LogMessageProperties.MESSAGE] = log.Message;
}

if (!string.IsNullOrEmpty(log.LibraryId))
{
logJson[LogMessageProperties.LIBRARY_ID] = log.LibraryId;
}

if (log.TargetGraphs != null && log.TargetGraphs.Any() && log.TargetGraphs.All(l => !string.IsNullOrEmpty(l)))
{
logJson[LogMessageProperties.TARGET_GRAPHS] = log.TargetGraphs;
}

return logJson;
}).ToList();
}
}

return json;
}
}
}
15 changes: 15 additions & 0 deletions src/NuGet.Core/NuGet.ProjectModel/CacheFileProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.ProjectModel
{
internal class CacheFileProperties
{
internal const string VersionProperty = "version";
internal const string DGSpecHashProperty = "dgSpecHash";
internal const string SuccessProperty = "success";
internal const string ExpectedPackageFilesProperty = "expectedPackageFiles";
internal const string ProjectFilePathProperty = "projectFilePath";
internal const string LogsProperty = "logs";
}
}
1 change: 0 additions & 1 deletion src/NuGet.Core/NuGet.ProjectModel/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'IAssetsLogMessage AssetsLogMessage.Create(IRestoreLogMessage logMessage)', validate parameter 'logMessage' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.ProjectModel.AssetsLogMessage.Create(NuGet.Common.IRestoreLogMessage)~NuGet.ProjectModel.IAssetsLogMessage")]
[assembly: SuppressMessage("Build", "CA1031:Modify 'Read' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.ProjectModel.CacheFileFormat.Read(System.IO.TextReader,NuGet.Common.ILogger,System.String)~NuGet.ProjectModel.CacheFile")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void DependencyGraphSpec.AddProject(PackageSpec projectSpec)', validate parameter 'projectSpec' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.ProjectModel.DependencyGraphSpec.AddProject(NuGet.ProjectModel.PackageSpec)")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'DependencyGraphSpec DependencyGraphSpec.WithReplacedSpec(PackageSpec project)', validate parameter 'project' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.ProjectModel.DependencyGraphSpec.WithReplacedSpec(NuGet.ProjectModel.PackageSpec)~NuGet.ProjectModel.DependencyGraphSpec")]
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'FileFormatException FileFormatException.Create(Exception exception, JToken value, string path)', validate parameter 'exception' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.ProjectModel.FileFormatException.Create(System.Exception,Newtonsoft.Json.Linq.JToken,System.String)~NuGet.ProjectModel.FileFormatException")]
Expand Down
Loading

0 comments on commit 3b03121

Please sign in to comment.