From a765313ac38bc24b8693d9af3ef417c79f1dba27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mielowski?= Date: Fri, 22 Nov 2024 11:36:47 +0100 Subject: [PATCH] feat: use Microsoft.CodeAnalysis.Diagnostics for error messages (#25) --- example/source/ApiOperationPolicy.cs | 2 +- src/Compiling/CompilerOptions.cs | 2 + src/Compiling/Compiling.csproj | 84 +++---- src/Compiling/Program.cs | 21 +- src/Core/Compiling/CSharpPolicyCompiler.cs | 8 +- src/Core/Compiling/CompilationContext.cs | 7 +- src/Core/Compiling/CompilerUtils.cs | 51 ++++- .../Diagnostics/CompilationErrors.cs | 207 ++++++++++++++++++ src/Core/Compiling/ICompilationContext.cs | 2 +- src/Core/Compiling/ICompilationResult.cs | 4 +- .../Policy/AuthenticationBasicCompiler.cs | 7 +- .../AuthenticationCertificateCompiler.cs | 22 +- .../AuthenticationManagedIdentityCompiler.cs | 9 +- .../Compiling/Policy/CacheLookupCompiler.cs | 18 +- .../Policy/CacheLookupValueCompiler.cs | 18 +- .../Policy/CacheRemoveValueCompiler.cs | 10 +- .../Compiling/Policy/CacheStoreCompiler.cs | 7 +- .../Policy/CacheStoreValueCompiler.cs | 41 ++-- .../Compiling/Policy/CheckHeaderCompiler.cs | 45 +++- src/Core/Compiling/Policy/CorsCompiler.cs | 62 +++++- .../Compiling/Policy/EmitMetricCompiler.cs | 39 +++- .../Policy/EmitTokenMetricCompiler.cs | 26 ++- .../Policy/ForwardRequestCompiler.cs | 31 ++- .../Compiling/Policy/InlinePolicyCompiler.cs | 15 +- src/Core/Compiling/Policy/IpFilterCompiler.cs | 40 +++- src/Core/Compiling/Policy/JsonPCompiler.cs | 7 +- .../Compiling/Policy/JsonToXmlCompiler.cs | 9 +- .../Compiling/Policy/MockResponseCompiler.cs | 16 +- src/Core/Compiling/Policy/QuotaCompiler.cs | 55 ++++- .../Policy/RateLimitByKeyCompiler.cs | 23 +- .../Compiling/Policy/RateLimitCompiler.cs | 38 +++- .../Policy/ReturnResponseCompiler.cs | 16 +- .../Compiling/Policy/RewriteUriCompiler.cs | 7 +- .../Policy/SemanticCacheLookupCompiler.cs | 36 +-- .../Policy/SemanticCacheStoreCompiler.cs | 7 +- .../Compiling/Policy/SendRequestCompiler.cs | 76 +++++-- .../Policy/SetBackendServiceCompiler.cs | 11 +- src/Core/Compiling/Policy/SetBodyCompiler.cs | 36 ++- .../Compiling/Policy/SetHeaderCompiler.cs | 21 +- .../Compiling/Policy/SetMethodCompiler.cs | 7 +- .../Policy/SetQueryParameterCompiler.cs | 7 +- .../Compiling/Policy/SetVariableCompiler.cs | 7 +- .../Compiling/Policy/ValidateJwtCompiler.cs | 89 ++++++-- src/Core/Compiling/SubCompilationContext.cs | 2 +- src/Core/Compiling/Syntax/BlockCompiler.cs | 8 +- .../Syntax/ExpressionStatementCompiler.cs | 22 +- .../Compiling/Syntax/IfStatementCompiler.cs | 26 ++- .../LocalDeclarationStatementCompiler.cs | 4 +- src/Core/Core.csproj | 29 +-- src/Core/PolicyValidationException.cs | 11 - src/Testing/Testing.csproj | 76 +++---- .../Assertions/CompilationResultAssertion.cs | 2 +- .../Compiling/ForwardRequestTests.cs | 2 +- 53 files changed, 1123 insertions(+), 305 deletions(-) create mode 100644 src/Core/Compiling/Diagnostics/CompilationErrors.cs delete mode 100644 src/Core/PolicyValidationException.cs diff --git a/example/source/ApiOperationPolicy.cs b/example/source/ApiOperationPolicy.cs index 1c429ae..7f6bb56 100644 --- a/example/source/ApiOperationPolicy.cs +++ b/example/source/ApiOperationPolicy.cs @@ -5,7 +5,7 @@ namespace Contoso.Apis; -[Document("echo-api.retrieve-resource")] +[Document("echo-api_retrieve-resource")] public class ApiOperationPolicy : IDocument { public void Inbound(IInboundContext c) diff --git a/src/Compiling/CompilerOptions.cs b/src/Compiling/CompilerOptions.cs index c0c50b2..0971c35 100644 --- a/src/Compiling/CompilerOptions.cs +++ b/src/Compiling/CompilerOptions.cs @@ -12,6 +12,7 @@ public class CompilerOptions public string SourceFolder { get; } public string OutputFolder { get; } public bool Format { get; } + public string FileExtension { get; } public XmlWriterSettings XmlWriterSettings => new XmlWriterSettings() { @@ -27,6 +28,7 @@ public CompilerOptions(IConfigurationRoot configuration) configuration["out"] ?? throw new NullReferenceException("Output folder not provided"); + FileExtension = configuration["ext"] ?? "xml"; Format = bool.TryParse(configuration["format"] ?? "false", out var fmt) && fmt; } } \ No newline at end of file diff --git a/src/Compiling/Compiling.csproj b/src/Compiling/Compiling.csproj index 1b7929b..fcc03e1 100644 --- a/src/Compiling/Compiling.csproj +++ b/src/Compiling/Compiling.csproj @@ -1,42 +1,42 @@ - - - - Azure.ApiManagement.PolicyToolkit.Compiling - 0.0.1 - 0.0.1 - 0.0.1 - 0.0.1 - Microsoft - MIT - true - © Microsoft Corporation. All rights reserved. - https://github.com/Azure/azure-api-management-policy-toolkit - https://github.com/Azure/azure-api-management-policy-toolkit - - Azure API Management Policy Toolkit Compiling is a dotnet tool allowing you to transform policy document(s) from C# code to XML. - Read more about it at https://github.com/Azure/azure-api-management-policy-toolkit - - Azure;Azure API Management;API Gateway;API Management;Policy;Policies;Policy Toolkit;Compiling;Policy Toolkit Compiling - - - - Exe - .net8 - enable - enable - - false - true - azure-apim-policy-compiler - ..\..\output - - - - - - - - - - - + + + + Azure.ApiManagement.PolicyToolkit.Compiling + 0.0.1 + 0.0.1 + 0.0.1 + 0.0.1 + Microsoft + MIT + true + © Microsoft Corporation. All rights reserved. + https://github.com/Azure/azure-api-management-policy-toolkit + https://github.com/Azure/azure-api-management-policy-toolkit + + Azure API Management Policy Toolkit Compiling is a dotnet tool allowing you to transform policy document(s) from C# code to XML. + Read more about it at https://github.com/Azure/azure-api-management-policy-toolkit + + Azure;Azure API Management;API Gateway;API Management;Policy;Policies;Policy Toolkit;Compiling;Policy Toolkit Compiling + + + + Exe + .net8 + enable + enable + + false + true + azure-apim-policy-compiler + ..\..\output + + + + + + + + + + + diff --git a/src/Compiling/Program.cs b/src/Compiling/Program.cs index 106d1e7..1f33298 100644 --- a/src/Compiling/Program.cs +++ b/src/Compiling/Program.cs @@ -9,6 +9,7 @@ using Azure.ApiManagement.PolicyToolkit.Compiling; using Azure.ApiManagement.PolicyToolkit.Serialization; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Configuration; @@ -19,12 +20,14 @@ var options = new CompilerOptions(config); var files = Directory.GetFiles(options.SourceFolder, "*.cs", SearchOption.AllDirectories).Where(p => !Regex.IsMatch(p, @".*[\\/](obj|bin)[\\/].*")); +int numberOfErrors = 0; + foreach (var file in files) { var fileName = Path.GetFileNameWithoutExtension(file); Console.Out.WriteLine($"File {fileName} Processing"); var code = File.ReadAllText(file); - var syntax = CSharpSyntaxTree.ParseText(code); + var syntax = CSharpSyntaxTree.ParseText(code, path: file); var documents = syntax.GetRoot() .DescendantNodes() @@ -34,9 +37,11 @@ { var result = new CSharpPolicyCompiler(document).Compile(); - foreach (var error in result.Errors) + var formatter = new DiagnosticFormatter(); + numberOfErrors += result.Diagnostics.Count; + foreach (var error in result.Diagnostics) { - Console.Out.WriteLine(error); + Console.Out.WriteLine(formatter.Format(error)); } var codeBuilder = new StringBuilder(); @@ -54,7 +59,11 @@ } var policyFileName = document.ExtractDocumentFileName(); - policyFileName = policyFileName.EndsWith(".xml") ? policyFileName : $"{policyFileName}.xml"; + var extension = Path.GetExtension(policyFileName); + if (string.IsNullOrWhiteSpace(extension)) + { + policyFileName = $"{policyFileName}.{options.FileExtension}"; + } string targetFolder = Path.GetFullPath(Path.Combine(options.OutputFolder, Path.GetFullPath(file).Split(Path.GetFullPath(options.SourceFolder))[1].Replace(Path.GetFileName(file), ""))); var targetFile = Path.Combine(targetFolder, policyFileName); @@ -69,4 +78,6 @@ } Console.Out.WriteLine($"File {fileName} processed"); -} \ No newline at end of file +} + +return numberOfErrors; \ No newline at end of file diff --git a/src/Core/Compiling/CSharpPolicyCompiler.cs b/src/Core/Compiling/CSharpPolicyCompiler.cs index e594cf9..d7f5f2b 100644 --- a/src/Core/Compiling/CSharpPolicyCompiler.cs +++ b/src/Core/Compiling/CSharpPolicyCompiler.cs @@ -4,9 +4,11 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; using Azure.ApiManagement.PolicyToolkit.Compiling.Policy; using Azure.ApiManagement.PolicyToolkit.Compiling.Syntax; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling; @@ -109,7 +111,11 @@ private void CompileSection(ICompilationContext context, string section, MethodD { if (method.Body is null) { - context.ReportError($"Method {section} is not allowed as expression. ({method.GetLocation()})"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicySectionCannotBeExpression, + method.GetLocation(), + method.Identifier.ValueText + )); return; } diff --git a/src/Core/Compiling/CompilationContext.cs b/src/Core/Compiling/CompilationContext.cs index c20eb5f..924b3dc 100644 --- a/src/Core/Compiling/CompilationContext.cs +++ b/src/Core/Compiling/CompilationContext.cs @@ -9,7 +9,7 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling; public class CompilationContext : ICompilationContext, ICompilationResult { - private readonly IList _errors = new List(); + private readonly IList _diagnostics = new List(); private readonly XElement _rootElement; public CompilationContext(SyntaxNode syntaxRoot, XElement rootElement) @@ -19,12 +19,11 @@ public CompilationContext(SyntaxNode syntaxRoot, XElement rootElement) } public void AddPolicy(XNode element) => _rootElement.Add(element); - - public void ReportError(string message) => _errors.Add(message); + public void Report(Diagnostic diagnostic) => _diagnostics.Add(diagnostic); public SyntaxNode SyntaxRoot { get; } public XElement Document => _rootElement; - public IReadOnlyList Errors => _errors.AsReadOnly(); + public IReadOnlyList Diagnostics => _diagnostics.AsReadOnly(); } \ No newline at end of file diff --git a/src/Core/Compiling/CompilerUtils.cs b/src/Core/Compiling/CompilerUtils.cs index e1f3358..3107798 100644 --- a/src/Core/Compiling/CompilerUtils.cs +++ b/src/Core/Compiling/CompilerUtils.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; using System.Xml.Linq; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -26,13 +28,19 @@ public static string ProcessParameter(this ExpressionSyntax expression, ICompila $"{{context.Variables[\"{interpolation.Expression.ToString()}\"]}}", _ => "" }); - var interpolationExpression = CSharpSyntaxTree.ParseText($"context => $\"{string.Join("", interpolationParts)}\"").GetRoot(); - var lambda = interpolationExpression.DescendantNodesAndSelf().OfType().FirstOrDefault(); + var interpolationExpression = CSharpSyntaxTree + .ParseText($"context => $\"{string.Join("", interpolationParts)}\"").GetRoot(); + var lambda = interpolationExpression.DescendantNodesAndSelf().OfType() + .FirstOrDefault(); lambda = Normalize(lambda!); return $"@({lambda.ExpressionBody})"; case InvocationExpressionSyntax syntax: return FindCode(syntax, context); default: + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedParameter, + expression.GetLocation() + )); return ""; } } @@ -41,8 +49,10 @@ public static string FindCode(this InvocationExpressionSyntax syntax, ICompilati { if (syntax.Expression is not IdentifierNameSyntax identifierSyntax) { - context.ReportError( - $"Invalid expression. It should be IdentifierNameSyntax but was {syntax.Expression.GetType()}. {syntax.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.InvalidExpression, + syntax.GetLocation() + )); return ""; } @@ -72,11 +82,22 @@ public static InitializerValue Process( ICompilationContext context) { var result = new Dictionary(); + if (creationSyntax.Initializer is null) + { + context.Report(Diagnostic.Create( + CompilationErrors.PolicyObjectCreationDoesNotContainInitializerSection, + creationSyntax.GetLocation() + )); + } + foreach (var expression in creationSyntax.Initializer?.Expressions ?? []) { if (expression is not AssignmentExpressionSyntax assignment) { - context.ReportError($"Is not AssignmentExpressionSyntax. {expression.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ObjectInitializerContainsNotAnAssigmentExpression, + expression.GetLocation() + )); continue; } @@ -168,7 +189,10 @@ public static bool TryExtractingConfigParameter( if (node.ArgumentList.Arguments.Count != 1) { - context.ReportError($"Wrong argument count for {policy} policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + policy)); return false; } @@ -183,15 +207,24 @@ public static bool TryExtractingConfig(this ExpressionSyntax syntax, values = null; if (syntax is not ObjectCreationExpressionSyntax config) { - context.ReportError( - $"{policy} policy argument must be an object creation expression. {syntax.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotAnObjectCreation, + syntax.GetLocation(), + policy, + typeof(T).Name + )); return false; } var initializer = config.Process(context); if (!initializer.TryGetValues(out var result)) { - context.ReportError($"{policy} policy argument must be of type {typeof(T).Name}. {syntax.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + syntax.GetLocation(), + policy, + typeof(T).Name + )); return false; } diff --git a/src/Core/Compiling/Diagnostics/CompilationErrors.cs b/src/Core/Compiling/Diagnostics/CompilationErrors.cs new file mode 100644 index 0000000..258e106 --- /dev/null +++ b/src/Core/Compiling/Diagnostics/CompilationErrors.cs @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.CodeAnalysis; + +namespace Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; + +public static class CompilationErrors +{ + public readonly static DiagnosticDescriptor PolicySectionCannotBeExpression = new( + "APIM9999", + "Policy section method is not allowed as expression", + "Method '{0}' is not allowed as expression because it is a policy section method", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor OnlyOneOfTreeShouldBeDefined = new( + "APIM9998", + "Only one of three parameters should be defined", + "Policy '{0}' requires only one of '{1}', '{2}' or '{3}' to be defined", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor OnlyOneOfTwoShouldBeDefined = new( + "APIM9997", + "Only one of two parameters should be defined", + "Policy '{0}' requires only one of '{1}' or '{2}' to be defined", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor AtLeastOneOfTwoShouldBeDefined = new( + "APIM9996", + "At least One of two parameters should be defined", + "Policy '{0}' requires at least one of '{1}' or '{2}' to be defined", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor NotSupportedType = new( + "APIM9995", + "Not supported type", + "Not supported type '{1}' for '{0}' policy", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor ValueShouldBe = new( + "APIM9995", + "Value should be", + "Value for '{0}' policy should be '{1}' but is not", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor NotSupportedStatement = new( + "APIM9994", + "Not supported statement", + "Statement '{0}' is not supported in policy document", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor ExpressionNotSupported = new( + "APIM9993", + "Not supported expression", + "Expression of type '{0}' not supported in policy document. Only '{1}' is supported.", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor MethodNotSupported = new( + "APIM9992", + "Not supported method", + "Method '{0}' not supported in policy document", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor ArgumentCountMissMatchForPolicy = new( + "APIM2001", + "Argument count miss match for policy", + "Argument count miss match for '{0}' policy", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor PolicyArgumentIsNotAnObjectCreation = new( + "APIM2002", + "Argument should be an object creation expression", + "Argument for '{0}' policy should be an object creation expression", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor PolicyArgumentIsNotOfRequiredType = new( + "APIM2002", + "Argument should be of required type", + "Argument for '{0}' policy should of type '{1}' but is not", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor PolicyObjectCreationDoesNotContainInitializerSection = new( + "APIM2002", + "Object creation should contain initializer section", + "Argument should be an object creation expression with initializer section", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor ObjectInitializerContainsNotAnAssigmentExpression = new( + "APIM2003", + "Object initializer should only contain assigment expressions", + "Object initializer should only contain assigment expressions", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor InvalidExpression = new( + "APIM2004", + "Argument should be an method call expression", + "Argument should be an method call expression", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor NotSupportedParameter = new( + "APIM2005", + "Parameter definition is not supported", + "Parameter definition is not supported", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor RequiredParameterNotDefined = new( + "APIM2006", + "Required parameter was not defined", + "Required '{1}' parameter was not defined for '{0}' policy", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); + + public readonly static DiagnosticDescriptor RequiredParameterIsEmpty = new( + "APIM2007", + "Required parameter requires at least one element", + "Required '{1}' parameter for '{0}' policy is empty but needs at least one element", + "PolicyDocumentCompilation", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Description.", + helpLinkUri: "TODO", + customTags: ["APIM", "ApiManagement"]); +} \ No newline at end of file diff --git a/src/Core/Compiling/ICompilationContext.cs b/src/Core/Compiling/ICompilationContext.cs index 844265f..aca1745 100644 --- a/src/Core/Compiling/ICompilationContext.cs +++ b/src/Core/Compiling/ICompilationContext.cs @@ -10,7 +10,7 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling; public interface ICompilationContext { void AddPolicy(XNode element); - void ReportError(string message); + void Report(Diagnostic diagnostic); SyntaxNode SyntaxRoot { get; } } \ No newline at end of file diff --git a/src/Core/Compiling/ICompilationResult.cs b/src/Core/Compiling/ICompilationResult.cs index 98616b9..d89d860 100644 --- a/src/Core/Compiling/ICompilationResult.cs +++ b/src/Core/Compiling/ICompilationResult.cs @@ -3,10 +3,12 @@ using System.Xml.Linq; +using Microsoft.CodeAnalysis; + namespace Azure.ApiManagement.PolicyToolkit.Compiling; public interface ICompilationResult { XElement Document { get; } - IReadOnlyList Errors { get; } + IReadOnlyList Diagnostics { get; } } \ No newline at end of file diff --git a/src/Core/Compiling/Policy/AuthenticationBasicCompiler.cs b/src/Core/Compiling/Policy/AuthenticationBasicCompiler.cs index 4ba5b25..e0dd40d 100644 --- a/src/Core/Compiling/Policy/AuthenticationBasicCompiler.cs +++ b/src/Core/Compiling/Policy/AuthenticationBasicCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -16,7 +18,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments.Count != 2) { - context.ReportError($"Wrong argument count for authentication-basic policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "authentication-basic")); return; } diff --git a/src/Core/Compiling/Policy/AuthenticationCertificateCompiler.cs b/src/Core/Compiling/Policy/AuthenticationCertificateCompiler.cs index 2634a27..73c0965 100644 --- a/src/Core/Compiling/Policy/AuthenticationCertificateCompiler.cs +++ b/src/Core/Compiling/Policy/AuthenticationCertificateCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -24,13 +26,21 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) } var certElement = new XElement("authentication-certificate"); - var thumbprint = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Thumbprint), "thumbprint"); - var certId = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.CertificateId), "certificate-id"); - var body = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Body), "body"); - if (!(thumbprint ^ certId ^ body)) + if (new[] + { + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Thumbprint), "thumbprint"), + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.CertificateId), "certificate-id"), + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Body), "body") + }.Count(x => x) != 1) { - context.ReportError( - $"One of {nameof(CertificateAuthenticationConfig.Thumbprint)}, {nameof(CertificateAuthenticationConfig.CertificateId)}, {nameof(CertificateAuthenticationConfig.Body)} must be present. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTreeShouldBeDefined, + node.ArgumentList.GetLocation(), + "authentication-certificate", + nameof(CertificateAuthenticationConfig.Thumbprint), + nameof(CertificateAuthenticationConfig.CertificateId), + nameof(CertificateAuthenticationConfig.Body) + )); return; } diff --git a/src/Core/Compiling/Policy/AuthenticationManagedIdentityCompiler.cs b/src/Core/Compiling/Policy/AuthenticationManagedIdentityCompiler.cs index 5dcb455..7475848 100644 --- a/src/Core/Compiling/Policy/AuthenticationManagedIdentityCompiler.cs +++ b/src/Core/Compiling/Policy/AuthenticationManagedIdentityCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -27,7 +29,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(ManagedIdentityAuthenticationConfig.Resource), "resource")) { - context.ReportError($"{nameof(ManagedIdentityAuthenticationConfig.Resource)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "authentication-managed-identity", + nameof(ManagedIdentityAuthenticationConfig.Resource) + )); return; } diff --git a/src/Core/Compiling/Policy/CacheLookupCompiler.cs b/src/Core/Compiling/Policy/CacheLookupCompiler.cs index 0c369b7..58c6d10 100644 --- a/src/Core/Compiling/Policy/CacheLookupCompiler.cs +++ b/src/Core/Compiling/Policy/CacheLookupCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -24,15 +26,23 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(CacheLookupConfig.VaryByDeveloper), "vary-by-developer")) { - context.ReportError( - $"{nameof(CacheLookupConfig.VaryByDeveloper)} is required for cache-lookup policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-lookup", + nameof(CacheLookupConfig.VaryByDeveloper) + )); return; } if (!element.AddAttribute(values, nameof(CacheLookupConfig.VaryByDeveloperGroups), "vary-by-developer-groups")) { - context.ReportError( - $"{nameof(CacheLookupConfig.VaryByDeveloperGroups)} is required for cache-lookup policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-lookup", + nameof(CacheLookupConfig.VaryByDeveloperGroups) + )); return; } diff --git a/src/Core/Compiling/Policy/CacheLookupValueCompiler.cs b/src/Core/Compiling/Policy/CacheLookupValueCompiler.cs index 9e887b3..8e442eb 100644 --- a/src/Core/Compiling/Policy/CacheLookupValueCompiler.cs +++ b/src/Core/Compiling/Policy/CacheLookupValueCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -23,15 +25,23 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(CacheLookupValueConfig.Key), "key")) { - context.ReportError( - $"{nameof(CacheLookupValueConfig.Key)} is required for cache-lookup-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-lookup-value", + nameof(CacheLookupValueConfig.Key) + )); return; } if (!element.AddAttribute(values, nameof(CacheLookupValueConfig.VariableName), "variable-name")) { - context.ReportError( - $"{nameof(CacheLookupValueConfig.VariableName)} is required for cache-lookup-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-lookup-value", + nameof(CacheLookupValueConfig.VariableName) + )); return; } diff --git a/src/Core/Compiling/Policy/CacheRemoveValueCompiler.cs b/src/Core/Compiling/Policy/CacheRemoveValueCompiler.cs index 6c70243..d8fbcae 100644 --- a/src/Core/Compiling/Policy/CacheRemoveValueCompiler.cs +++ b/src/Core/Compiling/Policy/CacheRemoveValueCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -23,8 +25,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(CacheRemoveValueConfig.Key), "key")) { - context.ReportError( - $"{nameof(CacheRemoveValueConfig.Key)} is required for cache-remove-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-remove-value", + nameof(CacheRemoveValueConfig.Key) + )); return; } diff --git a/src/Core/Compiling/Policy/CacheStoreCompiler.cs b/src/Core/Compiling/Policy/CacheStoreCompiler.cs index 8c9ee7b..428d1f4 100644 --- a/src/Core/Compiling/Policy/CacheStoreCompiler.cs +++ b/src/Core/Compiling/Policy/CacheStoreCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -18,7 +20,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count is > 2 or < 1) { - context.ReportError($"Wrong argument count for cache-store policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "cache-store")); return; } diff --git a/src/Core/Compiling/Policy/CacheStoreValueCompiler.cs b/src/Core/Compiling/Policy/CacheStoreValueCompiler.cs index bc1d0bb..db41860 100644 --- a/src/Core/Compiling/Policy/CacheStoreValueCompiler.cs +++ b/src/Core/Compiling/Policy/CacheStoreValueCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -12,6 +14,7 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; public class CacheStoreValueCompiler : IMethodPolicyHandler { public string MethodName => nameof(IInboundContext.CacheStoreValue); + public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (!node.TryExtractingConfigParameter(context, "cache-store-value", out var values)) @@ -20,30 +23,42 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) } var element = new XElement("cache-store-value"); - + if (!element.AddAttribute(values, nameof(CacheStoreValueConfig.Key), "key")) { - context.ReportError( - $"{nameof(CacheStoreValueConfig.Key)} is required for cache-store-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-store-value", + nameof(CacheStoreValueConfig.Key) + )); return; } - - if(!element.AddAttribute(values, nameof(CacheStoreValueConfig.Value), "value")) + + if (!element.AddAttribute(values, nameof(CacheStoreValueConfig.Value), "value")) { - context.ReportError( - $"{nameof(CacheStoreValueConfig.Value)} is required for cache-store-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-store-value", + nameof(CacheStoreValueConfig.Value) + )); return; } - - if(!element.AddAttribute(values, nameof(CacheStoreValueConfig.Duration), "duration")) + + if (!element.AddAttribute(values, nameof(CacheStoreValueConfig.Duration), "duration")) { - context.ReportError( - $"{nameof(CacheStoreValueConfig.Duration)} is required for cache-store-value policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cache-store-value", + nameof(CacheStoreValueConfig.Duration) + )); return; } - + element.AddAttribute(values, nameof(CacheStoreValueConfig.CachingType), "caching-type"); - + context.AddPolicy(element); } } \ No newline at end of file diff --git a/src/Core/Compiling/Policy/CheckHeaderCompiler.cs b/src/Core/Compiling/Policy/CheckHeaderCompiler.cs index 8da6d21..31e4253 100644 --- a/src/Core/Compiling/Policy/CheckHeaderCompiler.cs +++ b/src/Core/Compiling/Policy/CheckHeaderCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -24,32 +26,57 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(CheckHeaderConfig.Name), "name")) { - context.ReportError($"{nameof(CheckHeaderConfig.Name)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.Name) + )); return; } if (!element.AddAttribute(values, nameof(CheckHeaderConfig.FailCheckHttpCode), "failed-check-httpcode")) { - context.ReportError($"{nameof(CheckHeaderConfig.FailCheckHttpCode)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.FailCheckHttpCode) + )); return; } if (!element.AddAttribute(values, nameof(CheckHeaderConfig.FailCheckErrorMessage), "failed-check-error-message")) { - context.ReportError($"{nameof(CheckHeaderConfig.FailCheckErrorMessage)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.FailCheckErrorMessage) + )); return; } if (!element.AddAttribute(values, nameof(CheckHeaderConfig.IgnoreCase), "ignore-case")) { - context.ReportError($"{nameof(CheckHeaderConfig.IgnoreCase)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.IgnoreCase) + )); return; } if (!values.TryGetValue(nameof(CheckHeaderConfig.Values), out var headerValues)) { - context.ReportError($"{nameof(CheckHeaderConfig.Values)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.Values) + )); return; } @@ -58,8 +85,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) .ToArray(); if (elements.Length == 0) { - context.ReportError( - $"{nameof(CheckHeaderConfig.Values)} must have at least one value. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + headerValues.Node.GetLocation(), + "check-header", + nameof(CheckHeaderConfig.Values) + )); return; } diff --git a/src/Core/Compiling/Policy/CorsCompiler.cs b/src/Core/Compiling/Policy/CorsCompiler.cs index e39a74e..c2bec8d 100644 --- a/src/Core/Compiling/Policy/CorsCompiler.cs +++ b/src/Core/Compiling/Policy/CorsCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -27,24 +29,54 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!values.TryGetValue(nameof(CorsConfig.AllowedOrigins), out var allowedOrigins)) { - context.ReportError($"{nameof(CorsConfig.AllowedOrigins)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cors", + nameof(CorsConfig.AllowedOrigins) + )); return; } - var allowedOriginsElement = new XElement("allowed-origins"); - var origins = (allowedOrigins.UnnamedValues ?? []).Select(origin => new XElement("origin", origin.Value!)); - allowedOriginsElement.Add(origins.ToArray()); - element.Add(allowedOriginsElement); + var origins = (allowedOrigins.UnnamedValues ?? []) + .Select(origin => new XElement("origin", origin.Value!)) + .ToArray(); + if (origins.Length == 0) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + allowedOrigins.Node.GetLocation(), + "cors", + nameof(CorsConfig.AllowedOrigins) + )); + return; + } + element.Add(new XElement("allowed-origins", origins)); if (!values.TryGetValue(nameof(CorsConfig.AllowedHeaders), out var allowedHeaders)) { - context.ReportError($"{nameof(CorsConfig.AllowedHeaders)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "cors", + nameof(CorsConfig.AllowedHeaders) + )); return; } var headers = (allowedHeaders.UnnamedValues ?? []) .Select(origin => new XElement("header", origin.Value!)) .ToArray(); + if (headers.Length == 0) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + allowedHeaders.Node.GetLocation(), + "cors", + nameof(CorsConfig.AllowedHeaders) + )); + return; + } element.Add(new XElement("allowed-headers", headers)); if (values.TryGetValue(nameof(CorsConfig.AllowedMethods), out var allowedMethods)) @@ -55,6 +87,15 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var methods = (allowedMethods.UnnamedValues ?? []) .Select(m => new XElement("method", m.Value!)) .ToArray(); + if (methods.Length == 0) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + allowedMethods.Node.GetLocation(), + "cors", + nameof(CorsConfig.AllowedMethods) + )); + } allowedMethodsElement.Add(methods); element.Add(allowedMethodsElement); } @@ -64,6 +105,15 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var exposeHeadersElements = (exposeHeaders.UnnamedValues ?? []) .Select(h => new XElement("header", h.Value!)) .ToArray(); + if (exposeHeadersElements.Length == 0) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + exposeHeaders.Node.GetLocation(), + "cors", + nameof(CorsConfig.ExposeHeaders) + )); + } element.Add(new XElement("expose-headers", exposeHeadersElements)); } diff --git a/src/Core/Compiling/Policy/EmitMetricCompiler.cs b/src/Core/Compiling/Policy/EmitMetricCompiler.cs index 597b7ca..424a56c 100644 --- a/src/Core/Compiling/Policy/EmitMetricCompiler.cs +++ b/src/Core/Compiling/Policy/EmitMetricCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -23,7 +25,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var element = new XElement("emit-metric"); if (!element.AddAttribute(values, nameof(EmitMetricConfig.Name), "name")) { - context.ReportError($"emit-metric {nameof(EmitMetricConfig.Name)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "emit-metric", + nameof(EmitMetricConfig.Name) + )); return; } @@ -32,16 +39,24 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!values.TryGetValue(nameof(EmitMetricConfig.Dimensions), out var dimensionsInitializer)) { - context.ReportError( - $"emit-metric {nameof(EmitMetricConfig.Dimensions)} must have been defined. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "emit-metric", + nameof(EmitMetricConfig.Dimensions) + )); return; } var dimensions = dimensionsInitializer.UnnamedValues ?? Array.Empty(); if (dimensions.Count == 0) { - context.ReportError( - $"emit-metric {nameof(EmitMetricConfig.Dimensions)} must have at least one value. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + dimensionsInitializer.Node.GetLocation(), + "emit-metric", + nameof(EmitMetricConfig.Dimensions) + )); return; } @@ -49,14 +64,24 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (!dimension.TryGetValues(out var result)) { + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + dimension.Node.GetLocation(), + "emit-metric.dimension", + nameof(MetricDimensionConfig) + )); continue; } var dimensionElement = new XElement("dimension"); if (!dimensionElement.AddAttribute(result, nameof(MetricDimensionConfig.Name), "name")) { - context.ReportError( - $"emit-metric.dimension {nameof(MetricDimensionConfig.Name)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "emit-metric.dimension", + nameof(MetricDimensionConfig.Name) + )); continue; } diff --git a/src/Core/Compiling/Policy/EmitTokenMetricCompiler.cs b/src/Core/Compiling/Policy/EmitTokenMetricCompiler.cs index 54ca8ab..f8af0f2 100644 --- a/src/Core/Compiling/Policy/EmitTokenMetricCompiler.cs +++ b/src/Core/Compiling/Policy/EmitTokenMetricCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -40,16 +42,24 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!values.TryGetValue(nameof(EmitTokenMetricConfig.Dimensions), out var dimensionsInitializer)) { - context.ReportError( - $"{_policyName} {nameof(EmitTokenMetricConfig.Dimensions)} must have been defined. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + _policyName, + nameof(EmitTokenMetricConfig.Dimensions) + )); return; } var dimensions = dimensionsInitializer.UnnamedValues ?? Array.Empty(); if (dimensions.Count == 0) { - context.ReportError( - $"{_policyName} {nameof(EmitTokenMetricConfig.Dimensions)} must have at least one value. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterIsEmpty, + dimensionsInitializer.Node.GetLocation(), + _policyName, + nameof(EmitTokenMetricConfig.Dimensions) + )); return; } @@ -63,8 +73,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var dimensionElement = new XElement("dimension"); if (!dimensionElement.AddAttribute(result, nameof(MetricDimensionConfig.Name), "name")) { - context.ReportError( - $"{_policyName}.dimension {nameof(MetricDimensionConfig.Name)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + dimension.Node.GetLocation(), + $"{_policyName}.dimension", + nameof(MetricDimensionConfig.Name) + )); continue; } diff --git a/src/Core/Compiling/Policy/ForwardRequestCompiler.cs b/src/Core/Compiling/Policy/ForwardRequestCompiler.cs index ada4305..e9dff7d 100644 --- a/src/Core/Compiling/Policy/ForwardRequestCompiler.cs +++ b/src/Core/Compiling/Policy/ForwardRequestCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -29,7 +31,11 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments.Count > 1) { - context.ReportError($"Wrong argument count for forward request policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "forward-request" + )); return; } @@ -38,16 +44,22 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments[0].Expression is not ObjectCreationExpressionSyntax config) { - context.ReportError( - $"Forward request policy argument must be an object creation expression. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotAnObjectCreation, + node.ArgumentList.Arguments[0].Expression.GetLocation(), + "forward-request")); return; } var initializer = config.Process(context); if (initializer.Type != nameof(ForwardRequestConfig)) { - context.ReportError( - $"Forward request policy argument must be of type ForwardRequestConfig. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + config.GetLocation(), + "forward-request", + nameof(ForwardRequestConfig) + )); return; } @@ -56,8 +68,13 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (initializer.NamedValues.ContainsKey(nameof(ForwardRequestConfig.Timeout)) && initializer.NamedValues.ContainsKey(nameof(ForwardRequestConfig.TimeoutMs))) { - context.ReportError( - $"Forward request policy cannot have both timeout and timeout-ms. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTwoShouldBeDefined, + config.GetLocation(), + "forward-request", + nameof(ForwardRequestConfig.Timeout), + nameof(ForwardRequestConfig.TimeoutMs) + )); } foreach ((string key, InitializerValue value) in initializer.NamedValues) diff --git a/src/Core/Compiling/Policy/InlinePolicyCompiler.cs b/src/Core/Compiling/Policy/InlinePolicyCompiler.cs index b1c5b23..7993394 100644 --- a/src/Core/Compiling/Policy/InlinePolicyCompiler.cs +++ b/src/Core/Compiling/Policy/InlinePolicyCompiler.cs @@ -4,8 +4,10 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; using Azure.ApiManagement.PolicyToolkit.Serialization; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -18,7 +20,11 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments.Count != 1) { - context.ReportError($"Wrong argument count for inline policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + MethodName + )); return; } @@ -26,7 +32,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (expression is not LiteralExpressionSyntax literal) { - context.ReportError($"Inline policy must be a string literal. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + expression.GetLocation(), + MethodName, + "string literal" + )); return; } diff --git a/src/Core/Compiling/Policy/IpFilterCompiler.cs b/src/Core/Compiling/Policy/IpFilterCompiler.cs index f03770d..74bd062 100644 --- a/src/Core/Compiling/Policy/IpFilterCompiler.cs +++ b/src/Core/Compiling/Policy/IpFilterCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -24,7 +26,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(IpFilterConfig.Action), "action")) { - context.ReportError($"{nameof(IpFilterConfig.Action)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "ip-filter", + nameof(IpFilterConfig.Action) + )); return; } @@ -45,28 +52,41 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (range.Type != nameof(AddressRange)) { - context.ReportError( - $"Address range argument must be of type {nameof(AddressRange)}. {range.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + range.Node.GetLocation(), + "ip-filter.address-range", + nameof(AddressRange) + )); continue; } var rangeValues = range.NamedValues; if (rangeValues is null) { - context.ReportError($"No initializer. {node.GetLocation()}"); return; } var rangeElement = new XElement("address-range"); if (!rangeElement.AddAttribute(rangeValues, nameof(AddressRange.From), "from")) { - context.ReportError($"{nameof(AddressRange.From)}. {range.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + range.Node.GetLocation(), + "ip-filter.address-range", + nameof(AddressRange.From) + )); continue; } if (!rangeElement.AddAttribute(rangeValues, nameof(AddressRange.To), "to")) { - context.ReportError($"{nameof(AddressRange.To)}. {range.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + range.Node.GetLocation(), + "ip-filter.address-range", + nameof(AddressRange.To) + )); continue; } @@ -78,7 +98,13 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!atLeastOneAddress && !atLeastOneRange) { - context.ReportError($"Ip filter elements. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.AtLeastOneOfTwoShouldBeDefined, + node.GetLocation(), + "ip-filter", + nameof(IpFilterConfig.Addresses), + nameof(IpFilterConfig.AddressRanges) + )); return; } diff --git a/src/Core/Compiling/Policy/JsonPCompiler.cs b/src/Core/Compiling/Policy/JsonPCompiler.cs index ba543d4..8c28d75 100644 --- a/src/Core/Compiling/Policy/JsonPCompiler.cs +++ b/src/Core/Compiling/Policy/JsonPCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -18,7 +20,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count != 1) { - context.ReportError($"Wrong argument count for jsonp policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "jsonp")); return; } diff --git a/src/Core/Compiling/Policy/JsonToXmlCompiler.cs b/src/Core/Compiling/Policy/JsonToXmlCompiler.cs index 85df484..3d03bf5 100644 --- a/src/Core/Compiling/Policy/JsonToXmlCompiler.cs +++ b/src/Core/Compiling/Policy/JsonToXmlCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -23,7 +25,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var element = new XElement("json-to-xml"); if (!element.AddAttribute(values, nameof(JsonToXmlConfig.Apply), "apply")) { - context.ReportError($"{nameof(JsonToXmlConfig.Apply)} must be present. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "json-to-xml", + nameof(JsonToXmlConfig.Apply) + )); return; } diff --git a/src/Core/Compiling/Policy/MockResponseCompiler.cs b/src/Core/Compiling/Policy/MockResponseCompiler.cs index cda369f..2054dfa 100644 --- a/src/Core/Compiling/Policy/MockResponseCompiler.cs +++ b/src/Core/Compiling/Policy/MockResponseCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -18,7 +20,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count > 1) { - context.ReportError($"Wrong argument count for mock response policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "mock-response")); return; } @@ -35,15 +40,18 @@ private void HandleConfig(ICompilationContext context, XElement element, Initial { if (value.Type != nameof(MockResponseConfig)) { - context.ReportError( - $"Mock response policy argument must be of type {nameof(MockResponseConfig)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + value.Node.GetLocation(), + "mock-response", + nameof(AddressRange) + )); return; } var values = value.NamedValues; if (values is null) { - context.ReportError($"TODO. {value.Node.GetLocation()}"); return; } diff --git a/src/Core/Compiling/Policy/QuotaCompiler.cs b/src/Core/Compiling/Policy/QuotaCompiler.cs index 5fe0735..f7e9736 100644 --- a/src/Core/Compiling/Policy/QuotaCompiler.cs +++ b/src/Core/Compiling/Policy/QuotaCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -27,14 +29,24 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!isCallsAdded && !isBandwidthAdded) { - context.ReportError( - $"{nameof(QuotaConfig.Calls)} or {nameof(QuotaConfig.Bandwidth)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTwoShouldBeDefined, + node.GetLocation(), + "quota", + nameof(QuotaConfig.Calls), + nameof(QuotaConfig.Bandwidth) + )); return; } if (!element.AddAttribute(values, nameof(QuotaConfig.RenewalPeriod), "renewal-period")) { - context.ReportError($"{nameof(QuotaConfig.RenewalPeriod)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "quota", + nameof(QuotaConfig.RenewalPeriod) + )); return; } @@ -44,7 +56,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (api.Type != nameof(ApiQuota)) { - context.ReportError($"Api must be of type {nameof(ApiQuota)}. {api.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + api.Node.GetLocation(), + "quota.api", + nameof(ApiQuota) + )); continue; } @@ -61,8 +78,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (operation.Type != nameof(OperationQuota)) { - context.ReportError( - $"Operation must be of type {nameof(OperationQuota)}. {operation.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + operation.Node.GetLocation(), + "quota.api.operation", + nameof(OperationQuota) + )); continue; } @@ -88,18 +109,28 @@ private bool Handle(ICompilationContext context, string name, InitializerValue v if (!isNameAdded && !isIdAdded) { - context.ReportError( - $"{nameof(EntityQuotaConfig.Name)} && {nameof(EntityQuotaConfig.Id)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.AtLeastOneOfTwoShouldBeDefined, + value.Node.GetLocation(), + name, + nameof(EntityQuotaConfig.Name), + nameof(EntityQuotaConfig.Id) + )); return false; } - var isCallsAdded = element.AddAttribute(values, nameof(QuotaConfig.Calls), "calls"); - var isBandwidthAdded = element.AddAttribute(values, nameof(QuotaConfig.Bandwidth), "bandwidth"); + var isCallsAdded = element.AddAttribute(values, nameof(EntityQuotaConfig.Calls), "calls"); + var isBandwidthAdded = element.AddAttribute(values, nameof(EntityQuotaConfig.Bandwidth), "bandwidth"); if (!isCallsAdded && !isBandwidthAdded) { - context.ReportError( - $"{nameof(QuotaConfig.Calls)} or {nameof(QuotaConfig.Bandwidth)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.AtLeastOneOfTwoShouldBeDefined, + value.Node.GetLocation(), + name, + nameof(EntityQuotaConfig.Calls), + nameof(EntityQuotaConfig.Bandwidth) + )); return false; } diff --git a/src/Core/Compiling/Policy/RateLimitByKeyCompiler.cs b/src/Core/Compiling/Policy/RateLimitByKeyCompiler.cs index ebbb656..70ba9cf 100644 --- a/src/Core/Compiling/Policy/RateLimitByKeyCompiler.cs +++ b/src/Core/Compiling/Policy/RateLimitByKeyCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -23,19 +25,34 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(RateLimitByKeyConfig.Calls), "calls")) { - context.ReportError($"{nameof(RateLimitByKeyConfig.Calls)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "rate-limit-by-key", + nameof(RateLimitByKeyConfig.Calls) + )); return; } if (!element.AddAttribute(values, nameof(RateLimitByKeyConfig.RenewalPeriod), "renewal-period")) { - context.ReportError($"{nameof(RateLimitConfig.RenewalPeriod)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "rate-limit-by-key", + nameof(RateLimitByKeyConfig.RenewalPeriod) + )); return; } if (!element.AddAttribute(values, nameof(RateLimitByKeyConfig.CounterKey), "counter-key")) { - context.ReportError($"{nameof(RateLimitByKeyConfig.CounterKey)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "rate-limit-by-key", + nameof(RateLimitByKeyConfig.CounterKey) + )); return; } diff --git a/src/Core/Compiling/Policy/RateLimitCompiler.cs b/src/Core/Compiling/Policy/RateLimitCompiler.cs index 57309ad..e148077 100644 --- a/src/Core/Compiling/Policy/RateLimitCompiler.cs +++ b/src/Core/Compiling/Policy/RateLimitCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -24,13 +26,23 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(RateLimitConfig.Calls), "calls")) { - context.ReportError($"{nameof(RateLimitConfig.Calls)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "rate-limit-by-key", + nameof(RateLimitConfig.Calls) + )); return; } if (!element.AddAttribute(values, nameof(RateLimitConfig.RenewalPeriod), "renewal-period")) { - context.ReportError($"{nameof(RateLimitConfig.RenewalPeriod)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "rate-limit-by-key", + nameof(RateLimitConfig.RenewalPeriod) + )); return; } @@ -76,19 +88,35 @@ private bool Handle(ICompilationContext context, string name, InitializerValue v if (!isNameAdded && !isIdAdded) { - context.ReportError($"{nameof(EntityLimitConfig.Name)} && {nameof(EntityLimitConfig.Id)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.AtLeastOneOfTwoShouldBeDefined, + value.Node.GetLocation(), + name, + nameof(EntityLimitConfig.Name), + nameof(EntityLimitConfig.Id) + )); return false; } if (!element.AddAttribute(values, nameof(EntityLimitConfig.Calls), "calls")) { - context.ReportError($"{nameof(EntityLimitConfig.Calls)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + value.Node.GetLocation(), + name, + nameof(EntityLimitConfig.Calls) + )); return false; } if (!element.AddAttribute(values, nameof(EntityLimitConfig.RenewalPeriod), "renewal-period")) { - context.ReportError($"{nameof(EntityLimitConfig.RenewalPeriod)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + value.Node.GetLocation(), + name, + nameof(EntityLimitConfig.RenewalPeriod) + )); return false; } diff --git a/src/Core/Compiling/Policy/ReturnResponseCompiler.cs b/src/Core/Compiling/Policy/ReturnResponseCompiler.cs index a72c1d5..e481583 100644 --- a/src/Core/Compiling/Policy/ReturnResponseCompiler.cs +++ b/src/Core/Compiling/Policy/ReturnResponseCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -46,7 +48,12 @@ private static void HandleStatus(ICompilationContext context, XElement element, { if (!status.TryGetValues(out var config)) { - context.ReportError($"{nameof(StatusConfig)}. {status.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + status.Node.GetLocation(), + $"{element.Name}.set-status", + nameof(StatusConfig) + )); return; } @@ -54,7 +61,12 @@ private static void HandleStatus(ICompilationContext context, XElement element, if (!statusElement.AddAttribute(config, nameof(StatusConfig.Code), "code")) { - context.ReportError($"{nameof(BodyConfig.Content)}. {status.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + status.Node.GetLocation(), + $"{element.Name}.set-status", + nameof(StatusConfig.Code) + )); return; } diff --git a/src/Core/Compiling/Policy/RewriteUriCompiler.cs b/src/Core/Compiling/Policy/RewriteUriCompiler.cs index dc14e2c..7085054 100644 --- a/src/Core/Compiling/Policy/RewriteUriCompiler.cs +++ b/src/Core/Compiling/Policy/RewriteUriCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -17,7 +19,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count is > 2 or 0) { - context.ReportError($"Wrong argument count for rewrite-uri policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "rewrite-uri")); return; } diff --git a/src/Core/Compiling/Policy/SemanticCacheLookupCompiler.cs b/src/Core/Compiling/Policy/SemanticCacheLookupCompiler.cs index fdddc82..d2a2158 100644 --- a/src/Core/Compiling/Policy/SemanticCacheLookupCompiler.cs +++ b/src/Core/Compiling/Policy/SemanticCacheLookupCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -37,24 +39,36 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(SemanticCacheLookupConfig.ScoreThreshold), "score-threshold")) { - context.ReportError( - $"{_policyName} {nameof(SemanticCacheLookupConfig.ScoreThreshold)} score-threshold. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + _policyName, + nameof(SemanticCacheLookupConfig.ScoreThreshold) + )); return; } if (!element.AddAttribute(values, nameof(SemanticCacheLookupConfig.EmbeddingsBackendId), "embeddings-backend-id")) { - context.ReportError( - $"{_policyName} {nameof(SemanticCacheLookupConfig.EmbeddingsBackendId)} embeddings-backend-id. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + _policyName, + nameof(SemanticCacheLookupConfig.EmbeddingsBackendId) + )); return; } if (!element.AddAttribute(values, nameof(SemanticCacheLookupConfig.EmbeddingsBackendAuth), "embeddings-backend-auth")) { - context.ReportError( - $"{_policyName} {nameof(SemanticCacheLookupConfig.EmbeddingsBackendAuth)} embeddings-backend-auth. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + _policyName, + nameof(SemanticCacheLookupConfig.EmbeddingsBackendAuth) + )); return; } @@ -65,15 +79,7 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { foreach (var varyBy in varyByInitializer.UnnamedValues ?? []) { - if (varyBy.Value is not null) - { - element.Add(new XElement("vary-by", varyBy.Value)); - } - else - { - context.ReportError( - $"{_policyName} {nameof(SemanticCacheLookupConfig.VaryBy)} vary-by. {varyBy.Node.GetLocation()}"); - } + element.Add(new XElement("vary-by", varyBy.Value)); } } diff --git a/src/Core/Compiling/Policy/SemanticCacheStoreCompiler.cs b/src/Core/Compiling/Policy/SemanticCacheStoreCompiler.cs index e6a28da..e0a623e 100644 --- a/src/Core/Compiling/Policy/SemanticCacheStoreCompiler.cs +++ b/src/Core/Compiling/Policy/SemanticCacheStoreCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -32,7 +34,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count != 1) { - context.ReportError($"Wrong argument count for {_policyName} policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + _policyName)); return; } diff --git a/src/Core/Compiling/Policy/SendRequestCompiler.cs b/src/Core/Compiling/Policy/SendRequestCompiler.cs index 4a5bcca..b9e54dd 100644 --- a/src/Core/Compiling/Policy/SendRequestCompiler.cs +++ b/src/Core/Compiling/Policy/SendRequestCompiler.cs @@ -4,6 +4,7 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -25,7 +26,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (!element.AddAttribute(values, nameof(SendRequestConfig.ResponseVariableName), "response-variable-name")) { - context.ReportError($"{nameof(SendRequestConfig.ResponseVariableName)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + "send-request", + nameof(SendRequestConfig.ResponseVariableName) + )); return; } @@ -70,14 +76,24 @@ private void HandleProxy(ICompilationContext context, XElement element, Initiali { if (!value.TryGetValues(out var values)) { - context.ReportError($"{nameof(ProxyConfig)} initializer. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + value.Node.GetLocation(), + $"{element.Name}.proxy", + nameof(ProxyConfig) + )); return; } var certificateElement = new XElement("proxy"); if (!certificateElement.AddAttribute(values, nameof(ProxyConfig.Url), "url")) { - context.ReportError($"{nameof(ProxyConfig.Url)}. {value.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + value.Node.GetLocation(), + $"{element.Name}.proxy", + nameof(ProxyConfig.Url) + )); return; } @@ -91,8 +107,6 @@ private void HandleAuthentication(ICompilationContext context, XElement element, var values = authentication.NamedValues; if (values is null) { - context.ReportError( - $"{nameof(SendRequestConfig.Authentication)} initializer. {authentication.Node.GetLocation()}"); return; } @@ -108,8 +122,12 @@ private void HandleAuthentication(ICompilationContext context, XElement element, HandleManagedIdentityAuthentication(context, element, values, authentication.Node); break; default: - context.ReportError( - $"{nameof(SendRequestConfig.Authentication)} {authentication.Type}. {authentication.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedType, + authentication.Node.GetLocation(), + $"{element.Name}", + authentication.Type + )); break; } } @@ -123,13 +141,23 @@ private void HandleBasicAuthentication( var basicElement = new XElement("authentication-basic"); if (!basicElement.AddAttribute(values, nameof(BasicAuthenticationConfig.Username), "username")) { - context.ReportError($"{nameof(BasicAuthenticationConfig.Username)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + $"{element.Name}.authentication-basic", + nameof(BasicAuthenticationConfig.Username) + )); return; } if (!basicElement.AddAttribute(values, nameof(BasicAuthenticationConfig.Password), "password")) { - context.ReportError($"{nameof(BasicAuthenticationConfig.Password)}. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + $"{element.Name}.authentication-basic", + nameof(BasicAuthenticationConfig.Password) + )); return; } @@ -143,15 +171,23 @@ private void HandleCertificateAuthentication( SyntaxNode node) { var certElement = new XElement("authentication-certificate"); - var thumbprint = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Thumbprint), "thumbprint"); - var certId = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.CertificateId), "certificate-id"); - var body = certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Body), "body"); certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Password), "password"); - if (!(thumbprint ^ certId ^ body)) + if (new[] + { + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Thumbprint), "thumbprint"), + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.CertificateId), "certificate-id"), + certElement.AddAttribute(values, nameof(CertificateAuthenticationConfig.Body), "body") + }.Count(b => b) != 1) { - context.ReportError( - $"One of {nameof(CertificateAuthenticationConfig.Thumbprint)}, {nameof(CertificateAuthenticationConfig.CertificateId)}, {nameof(CertificateAuthenticationConfig.Body)} must be present. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTreeShouldBeDefined, + node.GetLocation(), + $"{element.Name}.authentication-certificate", + nameof(CertificateAuthenticationConfig.Thumbprint), + nameof(CertificateAuthenticationConfig.CertificateId), + nameof(CertificateAuthenticationConfig.Body) + )); return; } @@ -165,7 +201,15 @@ private void HandleManagedIdentityAuthentication( SyntaxNode node) { var certElement = new XElement("authentication-managed-identity"); - certElement.AddAttribute(values, nameof(ManagedIdentityAuthenticationConfig.Resource), "resource"); + if (!certElement.AddAttribute(values, nameof(ManagedIdentityAuthenticationConfig.Resource), "resource")) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + node.GetLocation(), + $"{element.Name}.authentication-managed-identity", + nameof(ManagedIdentityAuthenticationConfig.Resource) + )); + } certElement.AddAttribute(values, nameof(ManagedIdentityAuthenticationConfig.ClientId), "client-id"); certElement.AddAttribute(values, nameof(ManagedIdentityAuthenticationConfig.OutputTokenVariableName), "output-token-variable-name"); diff --git a/src/Core/Compiling/Policy/SetBackendServiceCompiler.cs b/src/Core/Compiling/Policy/SetBackendServiceCompiler.cs index 92de533..4b02ec7 100644 --- a/src/Core/Compiling/Policy/SetBackendServiceCompiler.cs +++ b/src/Core/Compiling/Policy/SetBackendServiceCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -26,8 +28,13 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var backendIdDefined = element.AddAttribute(values, nameof(SetBackendServiceConfig.BackendId), "backend-id"); if (!(baseUrlDefined ^ backendIdDefined)) { - context.ReportError( - $"You need to specify either base-url or backend-id but not both. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTwoShouldBeDefined, + node.GetLocation(), + "set-backend-service", + nameof(SetBackendServiceConfig.BaseUrl), + nameof(SetBackendServiceConfig.BackendId) + )); return; } diff --git a/src/Core/Compiling/Policy/SetBodyCompiler.cs b/src/Core/Compiling/Policy/SetBodyCompiler.cs index 2284ff0..b69f0d7 100644 --- a/src/Core/Compiling/Policy/SetBodyCompiler.cs +++ b/src/Core/Compiling/Policy/SetBodyCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -18,7 +20,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) var arguments = node.ArgumentList.Arguments; if (arguments.Count is > 2 or 0) { - context.ReportError($"Wrong argument count for set-body policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "set-body")); return; } @@ -33,7 +38,12 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (template.Value != "liquid") { - context.ReportError($"Not liquid. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTwoShouldBeDefined, + template.Node.GetLocation(), + "forward-request.template", + "liquid" + )); } else { @@ -45,7 +55,13 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (xsiNil.Value != "blank" && xsiNil.Value != "null") { - context.ReportError($"Not bank or null. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTwoShouldBeDefined, + xsiNil.Node.GetLocation(), + "forward-request.xsi-nil", + "blank", + "null" + )); } else { @@ -67,13 +83,23 @@ public static void HandleBody(ICompilationContext context, XElement element, Ini { if (!body.TryGetValues(out var config)) { - context.ReportError($"{nameof(BodyConfig)}. {body.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + body.Node.GetLocation(), + $"{element.Name}.set-body", + nameof(BodyConfig) + )); return; } if (!config.TryGetValue(nameof(BodyConfig.Content), out var content)) { - context.ReportError($"{nameof(BodyConfig.Content)}. {body.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + body.Node.GetLocation(), + $"{element.Name}.set-body", + nameof(BodyConfig.Content) + )); return; } diff --git a/src/Core/Compiling/Policy/SetHeaderCompiler.cs b/src/Core/Compiling/Policy/SetHeaderCompiler.cs index 882cab1..e4a2189 100644 --- a/src/Core/Compiling/Policy/SetHeaderCompiler.cs +++ b/src/Core/Compiling/Policy/SetHeaderCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -39,8 +41,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (_type != "delete" && arguments.Count < 2 || _type == "delete" && arguments.Count != 1) { - context.ReportError($"Wrong argument count for set-header policy. {node.GetLocation()}"); - context.AddPolicy(new XComment("Issue: set-header")); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "set-header")); return; } @@ -64,13 +68,24 @@ public static void HandleHeaders(ICompilationContext context, XElement root, Ini { if (!header.TryGetValues(out var headerValues)) { - context.ReportError($"{nameof(HeaderConfig)}. {header.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + header.Node.GetLocation(), + $"{root.Name}.set-header", + nameof(HeaderConfig) + )); continue; } var headerElement = new XElement("set-header"); if (!headerElement.AddAttribute(headerValues, nameof(HeaderConfig.Name), "name")) { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + header.Node.GetLocation(), + $"{root.Name}.set-header", + nameof(HeaderConfig.Name) + )); continue; } diff --git a/src/Core/Compiling/Policy/SetMethodCompiler.cs b/src/Core/Compiling/Policy/SetMethodCompiler.cs index f7fba12..d09d7bf 100644 --- a/src/Core/Compiling/Policy/SetMethodCompiler.cs +++ b/src/Core/Compiling/Policy/SetMethodCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -17,7 +19,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments.Count != 1) { - context.ReportError($"Wrong argument count for set-method policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "set-method")); return; } diff --git a/src/Core/Compiling/Policy/SetQueryParameterCompiler.cs b/src/Core/Compiling/Policy/SetQueryParameterCompiler.cs index 34cc5d4..14a6044 100644 --- a/src/Core/Compiling/Policy/SetQueryParameterCompiler.cs +++ b/src/Core/Compiling/Policy/SetQueryParameterCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -39,7 +41,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) if (_type != "delete" && arguments.Count < 2 || _type == "delete" && arguments.Count != 1) { - context.ReportError($"Wrong argument count for set-query-parameter policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "set-query-parameter")); return; } diff --git a/src/Core/Compiling/Policy/SetVariableCompiler.cs b/src/Core/Compiling/Policy/SetVariableCompiler.cs index a58b266..a08aa17 100644 --- a/src/Core/Compiling/Policy/SetVariableCompiler.cs +++ b/src/Core/Compiling/Policy/SetVariableCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -17,7 +19,10 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) { if (node.ArgumentList.Arguments.Count != 2) { - context.ReportError($"Wrong argument count for set-variable policy. {node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ArgumentCountMissMatchForPolicy, + node.ArgumentList.GetLocation(), + "set-variable")); return; } var name = node.ArgumentList.Arguments[0].Expression.ProcessParameter(context); diff --git a/src/Core/Compiling/Policy/ValidateJwtCompiler.cs b/src/Core/Compiling/Policy/ValidateJwtCompiler.cs index f66094d..27398ed 100644 --- a/src/Core/Compiling/Policy/ValidateJwtCompiler.cs +++ b/src/Core/Compiling/Policy/ValidateJwtCompiler.cs @@ -4,7 +4,9 @@ using System.Xml.Linq; using Azure.ApiManagement.PolicyToolkit.Authoring; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Azure.ApiManagement.PolicyToolkit.Compiling.Policy; @@ -29,7 +31,14 @@ public void Handle(ICompilationContext context, InvocationExpressionSyntax node) element.AddAttribute(values, nameof(ValidateJwtConfig.TokenValue), "token-value"), }.Count(b => b) != 1) { - context.ReportError("Only one of HeaderName, QueryParameterName and TokenValue must be set"); + context.Report(Diagnostic.Create( + CompilationErrors.OnlyOneOfTreeShouldBeDefined, + node.ArgumentList.GetLocation(), + "validate-jwt", + nameof(ValidateJwtConfig.HeaderName), + nameof(ValidateJwtConfig.QueryParameterName), + nameof(ValidateJwtConfig.TokenValue) + )); return; } @@ -69,13 +78,24 @@ private static object[] HandleOpenIdConfigs(ICompilationContext context, Initial { if (!openIdConfig.TryGetValues(out var openIdConfigValues)) { + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + openIdConfig.Node.GetLocation(), + "openid-config", + nameof(OpenIdConfig) + )); continue; } var openIdElement = new XElement("openid-config"); if (!openIdElement.AddAttribute(openIdConfigValues, nameof(OpenIdConfig.Url), "url")) { - context.ReportError($"{nameof(OpenIdConfig.Url)}. {openIdConfig.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + openIdConfig.Node.GetLocation(), + "openid-config", + nameof(OpenIdConfig.Url) + )); continue; } @@ -92,13 +112,24 @@ private static XElement HandleRequiredClaims(ICompilationContext context, Initia { if (!claim.TryGetValues(out var claimValue)) { + context.Report(Diagnostic.Create( + CompilationErrors.PolicyArgumentIsNotOfRequiredType, + claim.Node.GetLocation(), + "required-claims", + nameof(ClaimConfig) + )); continue; } var claimElement = new XElement("claim"); if (!claimElement.AddAttribute(claimValue, nameof(ClaimConfig.Name), "name")) { - context.ReportError($"{nameof(ClaimConfig.Name)}. {claim.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + claim.Node.GetLocation(), + "claim", + nameof(ClaimConfig.Name) + )); continue; } @@ -147,37 +178,65 @@ private static void HandleKeys( case nameof(Base64KeyConfig): if (!keyValues.TryGetValue(nameof(Base64KeyConfig.Value), out var value)) { - context.ReportError( - $"{nameof(Base64KeyConfig.Value)} is required. {initializer.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + initializer.Node.GetLocation(), + "key", + nameof(Base64KeyConfig.Value) + )); continue; } + keyElement.Value = value.Value!; break; case nameof(CertificateKeyConfig): if (!keyElement.AddAttribute(keyValues, nameof(CertificateKeyConfig.CertificateId), "certificate-id")) { - context.ReportError( - $"{nameof(CertificateKeyConfig.CertificateId)} is required. {initializer.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + initializer.Node.GetLocation(), + "key", + nameof(CertificateKeyConfig.CertificateId) + )); continue; } + break; case nameof(AsymmetricKeyConfig): - var containsModulus = - keyElement.AddAttribute(keyValues, nameof(AsymmetricKeyConfig.Modulus), "n"); - var containsExponent = - keyElement.AddAttribute(keyValues, nameof(AsymmetricKeyConfig.Exponent), "e"); - if (!(containsModulus && containsExponent)) + if (!keyElement.AddAttribute(keyValues, nameof(AsymmetricKeyConfig.Modulus), "n")) + { + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + initializer.Node.GetLocation(), + "key", + nameof(AsymmetricKeyConfig.Modulus) + )); + continue; + } + + if (!keyElement.AddAttribute(keyValues, nameof(AsymmetricKeyConfig.Exponent), "e")) { - context.ReportError( - $"Modulus and Exponent are required. {initializer.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.RequiredParameterNotDefined, + initializer.Node.GetLocation(), + "key", + nameof(AsymmetricKeyConfig.Exponent) + )); continue; } + break; default: - context.ReportError($"Unknown key type {initializer.Type}. {initializer.Node.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedType, + initializer.Node.GetLocation(), + "key", + initializer.Type + )); continue; } + listElement.Add(keyElement); } diff --git a/src/Core/Compiling/SubCompilationContext.cs b/src/Core/Compiling/SubCompilationContext.cs index 61a407a..8c929fe 100644 --- a/src/Core/Compiling/SubCompilationContext.cs +++ b/src/Core/Compiling/SubCompilationContext.cs @@ -20,7 +20,7 @@ public SubCompilationContext(ICompilationContext parent, XElement element) public void AddPolicy(XNode element) => _element.Add(element); - public void ReportError(string message) => _parent.ReportError(message); + public void Report(Diagnostic diagnostic) => _parent.Report(diagnostic); public SyntaxNode SyntaxRoot => _parent.SyntaxRoot; } \ No newline at end of file diff --git a/src/Core/Compiling/Syntax/BlockCompiler.cs b/src/Core/Compiling/Syntax/BlockCompiler.cs index 8855d18..7c90e8d 100644 --- a/src/Core/Compiling/Syntax/BlockCompiler.cs +++ b/src/Core/Compiling/Syntax/BlockCompiler.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -35,7 +37,11 @@ public void Compile(ICompilationContext context, SyntaxNode node) } else { - context.ReportError(""); + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedStatement, + statement.GetLocation(), + statement.Kind().ToString() + )); } } } diff --git a/src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs b/src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs index bae9135..95d3e27 100644 --- a/src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs +++ b/src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -24,14 +26,24 @@ public void Compile(ICompilationContext context, SyntaxNode node) var invocation = statement.Expression as InvocationExpressionSyntax; if (invocation == null) { - context.ReportError($"{statement.Expression.GetType().Name} is not supported. {statement.Expression.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ExpressionNotSupported, + statement.Expression.GetLocation(), + statement.Expression.GetType().Name, + nameof(InvocationExpressionSyntax) + )); return; } var memberAccess = invocation.Expression as MemberAccessExpressionSyntax; if (memberAccess == null) { - context.ReportError($"{invocation.Expression.GetType().Name} is not supported. {invocation.Expression.GetLocation()}"); + context.Report(Diagnostic.Create( + CompilationErrors.ExpressionNotSupported, + invocation.Expression.GetLocation(), + invocation.Expression.GetType().Name, + nameof(MemberAccessExpressionSyntax) + )); return; } @@ -42,7 +54,11 @@ public void Compile(ICompilationContext context, SyntaxNode node) } else { - context.ReportError($"{name}"); + context.Report(Diagnostic.Create( + CompilationErrors.MethodNotSupported, + memberAccess.GetLocation(), + name + )); } } } \ No newline at end of file diff --git a/src/Core/Compiling/Syntax/IfStatementCompiler.cs b/src/Core/Compiling/Syntax/IfStatementCompiler.cs index 51136b8..1051dc4 100644 --- a/src/Core/Compiling/Syntax/IfStatementCompiler.cs +++ b/src/Core/Compiling/Syntax/IfStatementCompiler.cs @@ -3,6 +3,8 @@ using System.Xml.Linq; +using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -35,15 +37,24 @@ public void Compile(ICompilationContext context, SyntaxNode node) if (currentIf.Statement is not BlockSyntax block) { - context.ReportError( - $"{currentIf.Statement.GetType().Name} is not supported. ({currentIf.Statement.GetLocation()})"); + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedStatement, + currentIf.Statement.GetLocation(), + currentIf.Statement.GetType().Name + )); + nextIf = currentIf.Else?.Statement as IfStatementSyntax; continue; } if (currentIf.Condition is not InvocationExpressionSyntax condition) { - context.ReportError( - $"{currentIf.Condition.GetType().Name} is not supported. ({currentIf.Condition.GetLocation()})"); + context.Report(Diagnostic.Create( + CompilationErrors.ExpressionNotSupported, + currentIf.Condition.GetLocation(), + currentIf.Condition.GetType().Name, + nameof(InvocationExpressionSyntax) + )); + nextIf = currentIf.Else?.Statement as IfStatementSyntax; continue; } @@ -67,8 +78,11 @@ public void Compile(ICompilationContext context, SyntaxNode node) } else { - context.ReportError( - $"{currentIf.Else.Statement.GetType().Name} is not supported. ({currentIf.Else.Statement.GetLocation()})"); + context.Report(Diagnostic.Create( + CompilationErrors.NotSupportedStatement, + currentIf.Else.Statement.GetLocation(), + currentIf.Else.Statement.GetType().Name + )); } } } diff --git a/src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs b/src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs index 0f7c9ed..baf9a6b 100644 --- a/src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs +++ b/src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs @@ -24,7 +24,7 @@ public void Compile(ICompilationContext context, SyntaxNode node) var variables = syntax.Declaration.Variables; if (variables.Count > 1) { - context.ReportError(""); + // TODO return; } @@ -38,7 +38,7 @@ public void Compile(ICompilationContext context, SyntaxNode node) } else { - context.ReportError(""); + // TODO } } } \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 7621710..b0c5a49 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -1,20 +1,21 @@ - - .net8 - enable - enable + + .net8 + enable + enable - false - true - + false + true + Azure.ApiManagement.PolicyToolkit + - - - - + + + + - - - + + + \ No newline at end of file diff --git a/src/Core/PolicyValidationException.cs b/src/Core/PolicyValidationException.cs deleted file mode 100644 index 94dbc2b..0000000 --- a/src/Core/PolicyValidationException.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Azure.ApiManagement.PolicyToolkit.Exceptions; - -public class PolicyValidationException : Exception -{ - public PolicyValidationException(string message) : base(message) - { - } -} \ No newline at end of file diff --git a/src/Testing/Testing.csproj b/src/Testing/Testing.csproj index c8d60c8..af22c7c 100644 --- a/src/Testing/Testing.csproj +++ b/src/Testing/Testing.csproj @@ -1,39 +1,39 @@ - - - - Azure.ApiManagement.PolicyToolkit.Testing - 0.0.1 - Microsoft - MIT - true - © Microsoft Corporation. All rights reserved. - https://github.com/Azure/azure-api-management-policy-toolkit - https://github.com/Azure/azure-api-management-policy-toolkit - - Azure API Management Policy Toolkit Testing is a library which helps in testing expressions and policy documents created in code. - Read more about it at https://github.com/Azure/azure-api-management-policy-toolkit - - Azure;Azure API Management;API Gateway;API Management;Policy;Policies;Policy Toolkit;Policy Testing;Policy Expression Testing - - - - .net8 - enable - enable - true - ..\..\output - true - false - false - - - - - - - - - - - + + + + Azure.ApiManagement.PolicyToolkit.Testing + 0.0.1 + Microsoft + MIT + true + © Microsoft Corporation. All rights reserved. + https://github.com/Azure/azure-api-management-policy-toolkit + https://github.com/Azure/azure-api-management-policy-toolkit + + Azure API Management Policy Toolkit Testing is a library which helps in testing expressions and policy documents created in code. + Read more about it at https://github.com/Azure/azure-api-management-policy-toolkit + + Azure;Azure API Management;API Gateway;API Management;Policy;Policies;Policy Toolkit;Policy Testing;Policy Expression Testing + + + + .net8 + enable + enable + true + ..\..\output + true + false + false + + + + + + + + + + + \ No newline at end of file diff --git a/test/Test.Core/Assertions/CompilationResultAssertion.cs b/test/Test.Core/Assertions/CompilationResultAssertion.cs index 0cc9197..3b1b880 100644 --- a/test/Test.Core/Assertions/CompilationResultAssertion.cs +++ b/test/Test.Core/Assertions/CompilationResultAssertion.cs @@ -19,7 +19,7 @@ public AndConstraint BeSuccessful(string because = " using var scope = new AssertionScope(); scope.BecauseOf(because, becauseArgs); this.NotBeNull(); - Subject.Errors.Should().BeEmpty(); + Subject.Diagnostics.Should().BeEmpty(); Subject.Document.Should().NotBeNull(); return new AndConstraint(this); } diff --git a/test/Test.Core/Compiling/ForwardRequestTests.cs b/test/Test.Core/Compiling/ForwardRequestTests.cs index 220c837..80cbdd2 100644 --- a/test/Test.Core/Compiling/ForwardRequestTests.cs +++ b/test/Test.Core/Compiling/ForwardRequestTests.cs @@ -30,7 +30,7 @@ public class PolicyDocument : IDocument public class PolicyDocument : IDocument { public void Backend(IBackendContext context) { - context.ForwardRequest(new ForwardRequestConfig()); + context.ForwardRequest(new ForwardRequestConfig() { }); } } """,