Skip to content

Commit

Permalink
Maintenance release (#248)
Browse files Browse the repository at this point in the history
* Added codefix for CS7036, when no ID has been assigned to Dolittle attributes

* Upgraded MongoDB dependencies
  • Loading branch information
mhelleborg authored Nov 7, 2024
1 parent 4b1e000 commit c4d510a
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageVersion Include="Microsoft.Reactive.Testing" Version="6.0.1" />
<PackageVersion Include="MongoDB.Driver" Version="2.30.0" />
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.5.0" />
<PackageVersion Include="MongoDB.Driver" Version="3.0.0" />
<PackageVersion Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.0.0" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
63 changes: 62 additions & 1 deletion Source/Analyzers/CodeFixes/AttributeIdentityCodeFixProvider.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Dolittle. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
Expand All @@ -21,14 +23,25 @@ namespace Dolittle.SDK.Analyzers.CodeFixes;
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AttributeIdentityCodeFixProvider)), Shared]
public class AttributeIdentityCodeFixProvider : CodeFixProvider
{
const string NoArgumentCorrespondsToRequiredParameter = "CS7036";

/// <inheritdoc />
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIds.AttributeInvalidIdentityRuleId, DiagnosticIds.RedactionEventIncorrectPrefix);
public override ImmutableArray<string> FixableDiagnosticIds { get; } = [
NoArgumentCorrespondsToRequiredParameter,
DiagnosticIds.AttributeInvalidIdentityRuleId,
DiagnosticIds.RedactionEventIncorrectPrefix];

/// <inheritdoc />
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var diagnostic = context.Diagnostics[0];
if(diagnostic.Id.Equals(NoArgumentCorrespondsToRequiredParameter, StringComparison.Ordinal))
{
RegisterCs7036IfApplicable(context, diagnostic, document);
return Task.CompletedTask;
}

if (!diagnostic.Properties.TryGetValue("identityParameter", out var identityParameterName))
{
return Task.CompletedTask;
Expand All @@ -54,6 +67,43 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return Task.CompletedTask;
}

void RegisterCs7036IfApplicable(CodeFixContext context, Diagnostic diagnostic, Document document)
{
try
{
var (identityParameterName, type) = Extract7036Arguments(diagnostic);

switch (type, identityParameterName)
{
case ("EventTypeAttribute", "eventTypeId"):
case ("EventHandlerAttribute", "eventHandlerId"):
case ("ProjectionAttribute", "projectionId"):
case ("AggregateRootAttribute", "id"):
break;
default:
return;
}

context.RegisterCodeFix(
CodeAction.Create(
"Add identity",
ct => AddIdentity(context, document, identityParameterName, IdentityGenerator.Generate, ct),
nameof(AttributeIdentityCodeFixProvider) + ".AddIdentity"),
diagnostic);

}
catch
{
// ignored
}
}

(string parameterName, string typeName) Extract7036Arguments(Diagnostic diagnostic)
{
var message = diagnostic.GetMessage();
var parts = message.Split('\'');
return (parts[1], parts[3].Split('.').FirstOrDefault());
}

static async Task<Document> UpdateIdentity(CodeFixContext context, Document document, string identityParameterName,
CreateIdentity createIdentity,
Expand All @@ -65,6 +115,17 @@ static async Task<Document> UpdateIdentity(CodeFixContext context, Document docu
var updatedRoot = root.ReplaceNode(attribute, GenerateIdentityAttribute(attribute, identityParameterName, createIdentity));
return document.WithSyntaxRoot(updatedRoot);
}

static async Task<Document> AddIdentity(CodeFixContext context, Document document, string identityParameterName,
CreateIdentity createIdentity,
CancellationToken cancellationToken)
{
var root = await context.Document.GetSyntaxRootAsync(cancellationToken);
if (root is null) return document;
if (!TryGetTargetNode(context, root, out AttributeSyntax attribute)) return document; // Target not found
var updatedRoot = root.ReplaceNode(attribute, GenerateIdentityAttribute(attribute, identityParameterName, createIdentity));
return document.WithSyntaxRoot(updatedRoot);
}

static AttributeSyntax GenerateIdentityAttribute(AttributeSyntax existing, string identityParameterName, CreateIdentity generate)
{
Expand Down
1 change: 1 addition & 0 deletions Tests/Analyzers/Analyzers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit"/>
Expand Down
106 changes: 106 additions & 0 deletions Tests/Analyzers/CodeFixes/AnnotationIdentityCodeFixTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,112 @@ class SomeEvent
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}

[Fact]
public async Task FixEventTypeAttributeWithNoIdentity()
{
var test = @"
using Dolittle.SDK.Events;
[EventType]
class SomeEvent
{
public string Name {get; set;}
}";

var expected = @"
using Dolittle.SDK.Events;
[EventType(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
class SomeEvent
{
public string Name {get; set;}
}";
IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
.WithSpan(4, 2, 4, 11)
.WithArguments("eventTypeId", "Dolittle.SDK.Events.EventTypeAttribute.EventTypeAttribute(string, uint, string?)");
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}

[Fact]
public async Task FixEventHandlerAttributeWithNoIdentity()
{
var test = @"
using Dolittle.SDK.Events.Handling;
[EventHandler]
class SomeHandler
{
public string Name {get; set;}
}";

var expected = @"
using Dolittle.SDK.Events.Handling;
[EventHandler(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
class SomeHandler
{
public string Name {get; set;}
}";
IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
.WithSpan(4, 2, 4, 14)
.WithArguments("eventHandlerId", "Dolittle.SDK.Events.Handling.EventHandlerAttribute.EventHandlerAttribute(string, bool, string?, string?, int, Dolittle.SDK.Events.Handling.ProcessFrom, string?, string?)");
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}

[Fact]
public async Task FixProjectionAttributeWithNoIdentity()
{
var test = @"
using Dolittle.SDK.Projections;
[Projection]
class SomeProjection: ReadModel
{
public string Name {get; set;}
}";

var expected = @"
using Dolittle.SDK.Projections;
[Projection(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
class SomeProjection: ReadModel
{
public string Name {get; set;}
}";
IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";
var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
.WithSpan(4, 2, 4, 12)
.WithArguments("projectionId", "Dolittle.SDK.Projections.ProjectionAttribute.ProjectionAttribute(string, string?, string?, string?, bool)");
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}

[Fact]
public async Task FixAggregateRootAttributeWithNoIdentity()
{
var test = @"
using Dolittle.SDK.Aggregates;
[AggregateRoot]
class SomeAggregate: AggregateRoot
{
}";

var expected = @"
using Dolittle.SDK.Aggregates;
[AggregateRoot(""61359cf4-3ae7-4a26-8a81-6816d3877f81"")]
class SomeAggregate: AggregateRoot
{
}";
IdentityGenerator.Override = "61359cf4-3ae7-4a26-8a81-6816d3877f81";

var diagnosticResult = DiagnosticResult.CompilerError("CS7036")
.WithSpan(4, 2, 4, 15)
.WithArguments("id", "Dolittle.SDK.Aggregates.AggregateRootAttribute.AggregateRootAttribute(string, string?)");
await VerifyCodeFixAsync(test, expected, diagnosticResult);
}
[Fact]
public async Task FixesAttributeWithInvalidIdentityWithNamedArguments()
{
Expand Down

0 comments on commit c4d510a

Please sign in to comment.