Skip to content

Commit d5d881b

Browse files
committed
Add example projects and tests
1 parent 8314439 commit d5d881b

File tree

70 files changed

+5121
-15
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+5121
-15
lines changed

JsonApiDotNetCore.MongoDb.sln

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
44
VisualStudioVersion = 15.0.26124.0
55
MinimumVisualStudioVersion = 15.0.26124.0
6-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DDAB9F03-9137-4BA5-9932-96C006C88583}"
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7E29AA10-F938-4CF8-9CAB-7ACD2D6DC784}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.MongoDb", "src\JsonApiDotNetCore.MongoDb\JsonApiDotNetCore.MongoDb.csproj", "{E8C38068-3E3E-477D-A09A-D536D662FC1C}"
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.MongoDb.GettingStarted", "src\JsonApiDotNetCore.MongoDb.GettingStarted\JsonApiDotNetCore.MongoDb.GettingStarted.csproj", "{600A3E66-E63F-427D-A991-4CD2067041F9}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.MongoDb", "src\JsonApiDotNetCore.MongoDb\JsonApiDotNetCore.MongoDb.csproj", "{FD312677-2A62-4B8F-A965-879B059F1755}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{19A533AA-E006-496D-A476-364DF2B637A1}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.MongoDb.Example.Tests", "test\JsonApiDotNetCore.MongoDb.Example.Tests\JsonApiDotNetCore.MongoDb.Example.Tests.csproj", "{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonApiDotNetCore.MongoDb.Example", "src\JsonApiDotNetCore.MongoDb.Example\JsonApiDotNetCore.MongoDb.Example.csproj", "{743C32A5-2584-4FA0-987B-B4E97CDAADE8}"
917
EndProject
1018
Global
1119
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -20,20 +28,59 @@ Global
2028
HideSolutionNode = FALSE
2129
EndGlobalSection
2230
GlobalSection(ProjectConfigurationPlatforms) = postSolution
23-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
25-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|x64.ActiveCfg = Debug|Any CPU
26-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|x64.Build.0 = Debug|Any CPU
27-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|x86.ActiveCfg = Debug|Any CPU
28-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Debug|x86.Build.0 = Debug|Any CPU
29-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
30-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|Any CPU.Build.0 = Release|Any CPU
31-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|x64.ActiveCfg = Release|Any CPU
32-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|x64.Build.0 = Release|Any CPU
33-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|x86.ActiveCfg = Release|Any CPU
34-
{E8C38068-3E3E-477D-A09A-D536D662FC1C}.Release|x86.Build.0 = Release|Any CPU
31+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
33+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|x64.ActiveCfg = Debug|Any CPU
34+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|x64.Build.0 = Debug|Any CPU
35+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|x86.ActiveCfg = Debug|Any CPU
36+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Debug|x86.Build.0 = Debug|Any CPU
37+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|Any CPU.Build.0 = Release|Any CPU
39+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|x64.ActiveCfg = Release|Any CPU
40+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|x64.Build.0 = Release|Any CPU
41+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|x86.ActiveCfg = Release|Any CPU
42+
{600A3E66-E63F-427D-A991-4CD2067041F9}.Release|x86.Build.0 = Release|Any CPU
43+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|Any CPU.Build.0 = Debug|Any CPU
45+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|x64.ActiveCfg = Debug|Any CPU
46+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|x64.Build.0 = Debug|Any CPU
47+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|x86.ActiveCfg = Debug|Any CPU
48+
{FD312677-2A62-4B8F-A965-879B059F1755}.Debug|x86.Build.0 = Debug|Any CPU
49+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|Any CPU.ActiveCfg = Release|Any CPU
50+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|Any CPU.Build.0 = Release|Any CPU
51+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|x64.ActiveCfg = Release|Any CPU
52+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|x64.Build.0 = Release|Any CPU
53+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|x86.ActiveCfg = Release|Any CPU
54+
{FD312677-2A62-4B8F-A965-879B059F1755}.Release|x86.Build.0 = Release|Any CPU
55+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
57+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|x64.ActiveCfg = Debug|Any CPU
58+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|x64.Build.0 = Debug|Any CPU
59+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|x86.ActiveCfg = Debug|Any CPU
60+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Debug|x86.Build.0 = Debug|Any CPU
61+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
62+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|Any CPU.Build.0 = Release|Any CPU
63+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|x64.ActiveCfg = Release|Any CPU
64+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|x64.Build.0 = Release|Any CPU
65+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|x86.ActiveCfg = Release|Any CPU
66+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1}.Release|x86.Build.0 = Release|Any CPU
67+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|x64.ActiveCfg = Debug|Any CPU
70+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|x64.Build.0 = Debug|Any CPU
71+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|x86.ActiveCfg = Debug|Any CPU
72+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Debug|x86.Build.0 = Debug|Any CPU
73+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
74+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|Any CPU.Build.0 = Release|Any CPU
75+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|x64.ActiveCfg = Release|Any CPU
76+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|x64.Build.0 = Release|Any CPU
77+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|x86.ActiveCfg = Release|Any CPU
78+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8}.Release|x86.Build.0 = Release|Any CPU
3579
EndGlobalSection
3680
GlobalSection(NestedProjects) = preSolution
37-
{E8C38068-3E3E-477D-A09A-D536D662FC1C} = {DDAB9F03-9137-4BA5-9932-96C006C88583}
81+
{600A3E66-E63F-427D-A991-4CD2067041F9} = {7E29AA10-F938-4CF8-9CAB-7ACD2D6DC784}
82+
{FD312677-2A62-4B8F-A965-879B059F1755} = {7E29AA10-F938-4CF8-9CAB-7ACD2D6DC784}
83+
{24CE53FA-9C49-4E20-A060-4A43DFB8C8F1} = {19A533AA-E006-496D-A476-364DF2B637A1}
84+
{743C32A5-2584-4FA0-987B-B4E97CDAADE8} = {7E29AA10-F938-4CF8-9CAB-7ACD2D6DC784}
3885
EndGlobalSection
3986
EndGlobal

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,35 @@ public class Startup
6868
}
6969
```
7070

71+
72+
## Development
73+
74+
Restore all NuGet packages with:
75+
76+
```bash
77+
dotnet restore
78+
```
79+
80+
### Testing
81+
82+
You don't need to have a running instance of MongoDB on your machine. To run the tests just type the following command in your terminal:
83+
84+
```bash
85+
dotnet test
86+
```
87+
88+
If you want to run the examples and explore them on your own **you are** going to need that running instance of MongoDB. If you have docker installed you can launch it like this:
89+
90+
```bash
91+
docker run -p 27017:27017 -d mongo:latest
92+
```
93+
94+
And then to run the API:
95+
96+
```bash
97+
dotnet run
98+
```
99+
71100
## Limitations
72101

73102
- Relationships are not supported
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class ArticlesController : JsonApiController<Article, string>
10+
{
11+
public ArticlesController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<Article, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class AuthorsController : JsonApiController<Author, string>
10+
{
11+
public AuthorsController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<Author, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class BlogsController : JsonApiController<Blog, string>
10+
{
11+
public BlogsController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<Blog, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class PeopleController : JsonApiController<Person, string>
10+
{
11+
public PeopleController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<Person, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class TodoItemsController : JsonApiController<TodoItem, string>
10+
{
11+
public TodoItemsController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<TodoItem, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Services;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.MongoDb.Example.Controllers
8+
{
9+
public sealed class UsersController : JsonApiController<User, string>
10+
{
11+
public UsersController(
12+
IJsonApiOptions options,
13+
ILoggerFactory loggerFactory,
14+
IResourceService<User, string> resourceService)
15+
: base(options, loggerFactory, resourceService)
16+
{ }
17+
}
18+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Net;
4+
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Errors;
6+
using JsonApiDotNetCore.Hooks.Internal.Execution;
7+
using JsonApiDotNetCore.MongoDb.Example.Models;
8+
using JsonApiDotNetCore.Resources;
9+
using JsonApiDotNetCore.Serialization.Objects;
10+
11+
namespace JsonApiDotNetCore.MongoDb.Example.Definitions
12+
{
13+
public class ArticleHooksDefinition : ResourceHooksDefinition<Article>
14+
{
15+
public ArticleHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
16+
17+
public override IEnumerable<Article> OnReturn(HashSet<Article> resources, ResourcePipeline pipeline)
18+
{
19+
if (pipeline == ResourcePipeline.GetSingle && resources.Any(r => r.Caption == "Classified"))
20+
{
21+
throw new JsonApiException(new Error(HttpStatusCode.Forbidden)
22+
{
23+
Title = "You are not allowed to see this article."
24+
});
25+
}
26+
27+
return resources.Where(t => t.Caption != "This should not be included");
28+
}
29+
}
30+
}
31+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Net;
4+
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Errors;
6+
using JsonApiDotNetCore.MongoDb.Example.Models;
7+
using JsonApiDotNetCore.Resources;
8+
using JsonApiDotNetCore.Serialization.Objects;
9+
10+
namespace JsonApiDotNetCore.MongoDb.Example.Definitions
11+
{
12+
public abstract class LockableHooksDefinition<T> : ResourceHooksDefinition<T> where T : class, IIsLockable, IIdentifiable
13+
{
14+
protected LockableHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
15+
16+
protected void DisallowLocked(IEnumerable<T> resources)
17+
{
18+
foreach (var e in resources ?? Enumerable.Empty<T>())
19+
{
20+
if (e.IsLocked)
21+
{
22+
throw new JsonApiException(new Error(HttpStatusCode.Forbidden)
23+
{
24+
Title = "You are not allowed to update fields or relationships of locked todo items."
25+
});
26+
}
27+
}
28+
}
29+
}
30+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Net;
4+
using JsonApiDotNetCore.Configuration;
5+
using JsonApiDotNetCore.Errors;
6+
using JsonApiDotNetCore.Hooks.Internal.Execution;
7+
using JsonApiDotNetCore.MongoDb.Example.Models;
8+
using JsonApiDotNetCore.Serialization.Objects;
9+
10+
namespace JsonApiDotNetCore.MongoDb.Example.Definitions
11+
{
12+
public class TodoHooksDefinition : LockableHooksDefinition<TodoItem>
13+
{
14+
public TodoHooksDefinition(IResourceGraph resourceGraph) : base(resourceGraph) { }
15+
16+
public override void BeforeRead(ResourcePipeline pipeline, bool isIncluded = false, string stringId = null)
17+
{
18+
if (stringId == "1337")
19+
{
20+
throw new JsonApiException(new Error(HttpStatusCode.Forbidden)
21+
{
22+
Title = "You are not allowed to update the author of todo items."
23+
});
24+
}
25+
}
26+
27+
public override void BeforeImplicitUpdateRelationship(IRelationshipsDictionary<TodoItem> resourcesByRelationship, ResourcePipeline pipeline)
28+
{
29+
List<TodoItem> todos = resourcesByRelationship.GetByRelationship<Person>().SelectMany(kvp => kvp.Value).ToList();
30+
DisallowLocked(todos);
31+
}
32+
}
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.MongoDb.Example.Models;
4+
using JsonApiDotNetCore.Resources;
5+
6+
namespace JsonApiDotNetCore.MongoDb.Example.Definitions
7+
{
8+
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, string>
9+
{
10+
public TodoItemDefinition(IResourceGraph resourceGraph) : base(resourceGraph)
11+
{
12+
}
13+
14+
public override IDictionary<string, object> GetMeta(TodoItem resource)
15+
{
16+
if (resource.Description != null && resource.Description.StartsWith("Important:"))
17+
{
18+
return new Dictionary<string, object>
19+
{
20+
["hasHighPriority"] = true
21+
};
22+
}
23+
24+
return base.GetMeta(resource);
25+
}
26+
}
27+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM microsoft/dotnet:latest
2+
3+
COPY . /app
4+
5+
WORKDIR /app
6+
7+
RUN ["dotnet", "restore"]
8+
9+
RUN ["dotnet", "build"]
10+
11+
EXPOSE 14140/tcp
12+
13+
CMD ["dotnet", "run", "--server.urls", "http://*:14140"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
4+
</PropertyGroup>
5+
6+
<ItemGroup>
7+
<PackageReference Include="JsonApiDotNetCore" Version="$(JsonApiDotNetCoreVersion)" />
8+
<PackageReference Include="MongoDB.Driver" Version="$(MongoDBDriverVersion)" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\JsonApiDotNetCore.MongoDb\JsonApiDotNetCore.MongoDb.csproj" />
13+
</ItemGroup>
14+
</Project>

0 commit comments

Comments
 (0)