Plugin tool: ensure URIs are absolute URIs (#340)

* Plugin tool: ensure URIs are absolute URIs

* Check if GetAboutInfo is overridden
This commit is contained in:
Luke Bordonaro 2024-10-16 15:38:32 -07:00 коммит произвёл GitHub
Родитель 98bea7d6de
Коммит 774b642add
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 85 добавлений и 67 удалений

Просмотреть файл

@ -30,21 +30,6 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
this.PhoneNumbers = phoneNumbers;
}
/// <summary>
/// Initializes a new instance of the <see cref="ContactInfo"/> class from a <see cref="SDK.Processing.ContactInfo"/> instance.
/// </summary>
/// <param name="contactInfo">
/// The <see cref="SDK.Processing.ContactInfo"/> instance to copy.
/// </param>
public ContactInfo(SDK.Processing.ContactInfo contactInfo)
: this(
contactInfo?.Name,
contactInfo?.Address,
contactInfo?.EmailAddresses?.ToArray(),
contactInfo?.PhoneNumbers?.ToArray())
{
}
/// <summary>
/// Gets the name of the contact, if any.
/// </summary>

Просмотреть файл

@ -19,7 +19,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
[JsonConstructor]
public LicenseInfo(
string name,
string uri,
Uri uri,
string text)
{
this.Name = name;
@ -27,17 +27,6 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
this.Text = text;
}
/// <summary>
/// Initializes a new instance of the <see cref="LicenseInfo"/> class from a <see cref="SDK.Processing.LicenseInfo"/> instance.
/// </summary>
/// <param name="licenseInfo">
/// The license info to copy.
/// </param>
public LicenseInfo(SDK.Processing.LicenseInfo licenseInfo)
: this(licenseInfo?.Name, licenseInfo?.Uri, licenseInfo?.Text)
{
}
/// <summary>
/// Gets the name of the license.
/// </summary>
@ -46,7 +35,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
/// <summary>
/// Gets the URI where the license text may be found.
/// </summary>
public string Uri { get; }
public Uri Uri { get; }
/// <summary>
/// Gets the full text of the license, if desired.
@ -74,7 +63,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
}
return string.Equals(this.Name, other.Name, StringComparison.Ordinal)
&& string.Equals(this.Uri, other.Uri, StringComparison.Ordinal)
&& string.Equals(this.Uri.OriginalString, other.Uri.OriginalString, StringComparison.Ordinal)
&& string.Equals(this.Text, other.Text, StringComparison.Ordinal);
}

Просмотреть файл

@ -32,20 +32,6 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
this.AdditionalInformation = additionalInformation;
}
/// <summary>
/// Initializes a new instance of the <see cref="ProcessingSourceInfo"/> class from a <see cref="SDK.Processing.ProcessingSourceInfo"/> instance.
/// </summary>
/// <param name="other"></param>
public ProcessingSourceInfo(SDK.Processing.ProcessingSourceInfo other)
: this(
other?.Owners?.Select(x => new ContactInfo(x)).ToArray(),
new ProjectInfo(other?.ProjectInfo),
new LicenseInfo(other?.LicenseInfo),
other?.CopyrightNotice,
other?.AdditionalInformation?.ToArray())
{
}
/// <summary>
/// Gets the contact information of the owners
/// of the <see cref="IProcessingSource"/>.
@ -68,7 +54,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
public string CopyrightNotice { get; }
/// <summary>
/// Gets any additional information about the <see cref="IProcessingSource"/> to convey to the user.
/// Gets any additional information about the <see cref="IProcessingSource"/> to convey to the user.
/// Each entry in the array is logically a new paragraph.
/// </summary>
public string[] AdditionalInformation { get; }

Просмотреть файл

@ -19,24 +19,15 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
/// The URI to the page for this project.
/// </param>
[JsonConstructor]
public ProjectInfo(string uri)
public ProjectInfo(Uri uri)
{
this.Uri = uri;
}
/// <summary>
/// Initializes a new instance of the <see cref="ProjectInfo"/> class from a <see cref="SDK.Processing.ProjectInfo"/> instance.
/// </summary>
/// <param name="projectInfo"></param>
public ProjectInfo(SDK.Processing.ProjectInfo projectInfo)
: this(projectInfo?.Uri)
{
}
/// <summary>
/// Gets the URI to the page for this project.
/// </summary>
public string Uri { get; }
public Uri Uri { get; }
/// <inheritdoc />
public override bool Equals(object obj)
@ -57,7 +48,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
return true;
}
return string.Equals(this.Uri, other.Uri, StringComparison.Ordinal);
return string.Equals(this.Uri.OriginalString, other.Uri.OriginalString, StringComparison.Ordinal);
}
/// <inheritdoc />

Просмотреть файл

@ -56,6 +56,12 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Manifest
return false;
}
if (!pluginManifest.ProjectUrl.IsAbsoluteUri)
{
this.logger.LogError($"Manifest {manifestFilePath} is invalid: project URL {pluginManifest.ProjectUrl.OriginalString} is not an absolute URI.");
return false;
}
return true;
}
}

Просмотреть файл

@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Microsoft.Performance.SDK;
using Microsoft.Performance.SDK.Processing;
@ -41,7 +42,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
/// <inheritdoc />
public bool TryProcess(PluginArtifacts artifacts, [NotNullWhen(true)] out ProcessedPluginResult? processedPlugin)
{
processedPlugin = null;
processedPlugin = null;
if (!TryProcessSourceDir(artifacts.SourceDirectoryFullPath, out ProcessedPluginSourceDirectory? processedDir))
{
this.logger.LogError($"Failed to process source directory {artifacts.SourceDirectoryFullPath}.");
@ -77,7 +78,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
x => new SandboxPreloadValidator(x, versionChecker),
Logger.Create<PluginsLoader>());
bool loadSuccess = pluginsLoader.TryLoadPlugin(processedDir.FullPath, out ErrorInfo errorInfo);
bool loadSuccess = pluginsLoader.TryLoadPlugin(processedDir.FullPath, out ErrorInfo errorInfo);
if (!loadSuccess)
{
// TODO: Check error codes and throw more specific exceptions
@ -121,10 +122,10 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
if (!loadSuccess || !isVersionSupported)
{
return false;
}
}
PluginMetadata metadata = GenerateMetadata(processedDir, manifest, pluginSDKversion);
if (!TryGenerateContentsMetadata(pluginsLoader, out PluginContentsMetadata? contentsMetadata))
{
this.logger.LogError($"Failed to generate contents metadata for plugin.");
@ -177,7 +178,7 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
filesToPack,
manifestFilePath,
totalSize);
return true;
}
@ -204,7 +205,18 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
private bool TryGenerateContentsMetadata(PluginsLoader pluginsLoader, out PluginContentsMetadata? contentsMetadata)
{
var processingSourcesMetadata = pluginsLoader.LoadedProcessingSources.Select(x => CreateProcessingSourceMetadata(x)).ToList();
HashSet<ProcessingSourceMetadata> processingSourcesMetadata = new();
foreach (ProcessingSourceReference psr in pluginsLoader.LoadedProcessingSources)
{
if (!TryCreateProcessingSourceMetadata(psr, out var metadata))
{
contentsMetadata = null;
return false;
}
processingSourcesMetadata.Add(metadata);
}
// TODO: #294 Figure out how to extract description of a datacooker.
var dataCookers = pluginsLoader.Extensions.SourceDataCookers
@ -220,12 +232,12 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
x.TableDescriptor.Category,
x.TableDescriptor.IsMetadataTable))
.ToList();
contentsMetadata = new(processingSourcesMetadata, dataCookers, tables);
return true;
}
private static ProcessingSourceMetadata CreateProcessingSourceMetadata(ProcessingSourceReference psr)
private bool TryCreateProcessingSourceMetadata(ProcessingSourceReference psr, [NotNullWhen(true)] out ProcessingSourceMetadata? metadata)
{
// Tables
IEnumerable<TableMetadata> dataTables = psr.Instance.DataTables.Select(x => new TableMetadata(
@ -264,16 +276,65 @@ namespace Microsoft.Performance.Toolkit.Plugins.Cli.Processing
}
}
ProcessingSourceMetadata metadata = new(
if (!TryGetAboutInfo(psr, out ProcessingSourceInfo? aboutInfo))
{
metadata = null;
return false;
}
metadata = new(
version: Version.Parse(psr.Version),
name: psr.Name,
description: psr.Description,
guid: psr.Instance.TryGetGuid(),
aboutInfo: new ProcessingSourceInfo(psr.Instance.GetAboutInfo()),
aboutInfo: aboutInfo,
availableTables: dataTables.Concat(metadataTables),
supportedDataSources: dataSourcesMetadata);
return metadata;
return true;
}
private bool TryGetAboutInfo(ProcessingSourceReference psr, [NotNullWhen(true)] out ProcessingSourceInfo? processingSourceInfo)
{
processingSourceInfo = null;
var aboutInfo = psr.Instance.GetAboutInfo();
if (aboutInfo == null)
{
this.logger.LogError($"Unable to generate processing source info: {psr.Name} does not contain about info.");
return false;
}
// Check if the GetAboutInfo method is overridden
MethodInfo? methodInfo = psr.Instance.GetType().GetMethod(nameof(ProcessingSource.GetAboutInfo));
if (methodInfo == null || methodInfo.DeclaringType == typeof(ProcessingSource))
{
this.logger.LogError($"Unable to generate processing source info: {psr.Name} does not override the virtual method {nameof(ProcessingSource)}.{nameof(ProcessingSource.GetAboutInfo)}.");
}
var owners = aboutInfo.Owners
.Select(o => new Core.Metadata.ContactInfo(o.Name, o.Address, o.EmailAddresses, o.PhoneNumbers))
.ToArray();
if (!Uri.TryCreate(aboutInfo.ProjectInfo.Uri, UriKind.Absolute, out Uri? projectUri))
{
this.logger.LogError($"Unable to generate processing source info: {aboutInfo.ProjectInfo.Uri} is not an absolute URI.");
return false;
}
Core.Metadata.ProjectInfo projectInfo = new(projectUri);
if (!Uri.TryCreate(aboutInfo.LicenseInfo.Uri, UriKind.Absolute, out Uri? licenseUri))
{
this.logger.LogError($"Unable to generate processing source info: {aboutInfo.LicenseInfo.Uri} is not an absolute URI.");
return false;
}
Core.Metadata.LicenseInfo licenseInfo = new(aboutInfo.LicenseInfo.Name, licenseUri, aboutInfo.LicenseInfo.Text);
processingSourceInfo = new ProcessingSourceInfo(owners, projectInfo, licenseInfo, aboutInfo.CopyrightNotice, aboutInfo.AdditionalInformation);
return true;
}
private record ProcessedPluginSourceDirectory(