Skip to content

Commit 24ce6a2

Browse files
committed
Merge branch 'release/v4.0.0'
2 parents bfa3031 + 2eb97ce commit 24ce6a2

File tree

76 files changed

+3810
-896
lines changed

Some content is hidden

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

76 files changed

+3810
-896
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,4 @@ MigrationBackup/
350350
.ionide/
351351

352352
Wissance.WebApiToolkit/.idea/
353+
/Wissance.WebApiToolkit/Wissance.WebApiToolkit.AWS.S3.Tests/settings.json

README.md

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@
55
![GitHub Release Date](https://img.shields.io/github/release-date/wissance/WebApiToolkit)
66
![GitHub release (latest by date)](https://img.shields.io/github/downloads/wissance/WebApiToolkit/v2.0.0/total?style=plastic)
77

8-
#### This lib helps to build `REST API` with `C#` and `AspNet` easier than writing it from scratch over and over in different projects. It helps to build consistent API (with same `REST` routes scheme) with minimal amount of code: minimal REST controller contains 10 lines of code.
8+
## 10 Lines of code = Full CRUD and even BULK with swagger docs
99

10-
![WebApiToolkit helps to build application easily](/img/cover.png)
10+
This ultimate lib helps to build `REST API` with `C#` and `AspNet` easier than writing it from scratch over and over in different projects. It helps to build a consistent API (with the same `REST` routes approach for different controllers) with minimal amount of code: the minimal REST controller contains **10 lines of code** with full *auto* support for all `CRUD` and `BULK` operations.
11+
12+
For the easiest way you only need:
13+
1. EntityFramework `Entities`
14+
2. DbContext with `DbSets`
15+
3. Inject from DI Manager Class on startup level.
16+
17+
![WebApiToolkit helps to build application easily](./img/logo_v4.0.0_256x256.jpg)
1118

1219
* [1. Key Features](#1-key-features)
1320
* [2. API Contract](#2-api-contract)
@@ -85,7 +92,30 @@ If this toolkit should be used with `EntityFramework` you should derive you reso
8592

8693
### 4. Toolkit usage algorithm with EntityFramework
8794

88-
#### 4.1 REST Services
95+
#### 4.1.0 Minimal REST service
96+
97+
What do we need Entity classes and `DbContext` and init am appropriate Manager class:
98+
```csharp
99+
services.AddScoped(sp =>
100+
{
101+
return SimplifiedEfBasedManagerFactory.Create<RoleEntity, int>(sp.GetRequiredService<ModelContext>(),
102+
null, sp.GetRequiredService<ILoggerFactory>());
103+
104+
});
105+
```
106+
107+
and controller class:
108+
```csharp
109+
public class RoleController : BasicBulkCrudController<RoleEntity, RoleEntity, int, EmptyAdditionalFilters>
110+
{
111+
public RoleController(IModelManager<RoleEntity, RoleEntity, int> manager)
112+
{
113+
Manager = manager;
114+
}
115+
}
116+
```
117+
118+
#### 4.1.1 REST Services with full Declaration
89119

90120
Full example is mentioned in section 6 (see below). But if you are starting to build new `REST Resource`
91121
`API` you should do following:
@@ -224,7 +254,7 @@ public class CodeGrpcService : CodeService.CodeServiceBase
224254
Unfortunately GRPC generates all types Request and therefore we should implement additional mapping to convert `DTO` to Response, see full example in this solution in the `Wissance.WebApiToolkit.TestApp` project
225255

226256
### 5. Nuget package
227-
You could find nuget-package [here](https://www.nuget.org/packages/Wissance.WebApiToolkit)
257+
* [Actual for version <= `3.x.y`, obsolete since `4.0.0`](https://www.nuget.org/packages/Wissance.WebApiToolkit)
228258

229259
### 6. Examples
230260
Here we consider only Full CRUD controllers because **Full CRUD = Read Only + Additional Operations (CREATE, UPDATE, DELETE)**, a **full example = full application** created with **Wissance.WebApiToolkit** could be found [here]( https://github.com/Wissance/WeatherControl)
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
using System.Buffers.Text;
2+
using System.Text;
3+
using System.Text.Unicode;
4+
using Microsoft.Extensions.Logging;
5+
using Newtonsoft.Json;
6+
using Wissance.WebApiToolkit.AWS.S3.Managers;
7+
using Wissance.WebApiToolkit.AWS.S3.Settings;
8+
using Wissance.WebApiToolkit.Core.Data.Files;
9+
using Wissance.WebApiToolkit.Dto;
10+
11+
namespace Wissance.WebApiToolkit.AWS.S3.Tests.Managers
12+
{
13+
public class TestAwsCompatibleCloudFileStorageManagerOnYandex : IDisposable
14+
{
15+
/// <summary>
16+
/// These tests were written for testing IAWSCompatibleFileStorageManager operation
17+
/// with Yandex Object Storage
18+
/// 1. It is required to have service account with role storage.editor
19+
/// Don't forget to save key and secret to file settings.json in this project root dir and set them to copy to output
20+
/// path, this file has a following content (values changed):
21+
/// {
22+
/// "AccessKey": "YassEZBPassMGzTelzassigfQ",
23+
/// "SecretAccessKey": "YCMeYuWMassSWPBAasselV7assQ0assDNUassdkS"
24+
/// }
25+
/// 2. Prior to tests you have to create following buckets:
26+
/// - y-s3-test-bucket
27+
/// - y-s3-test-bucket-2
28+
/// These tests depends on S3 predefined structure, bucket "y-s3-test-bucket-2" must have the following
29+
/// structure:
30+
/// /
31+
/// --/artifacts
32+
/// --/archives
33+
/// --/txt
34+
/// --/test_data.txt (content: test_data:1234567890 qwertyuiopasdfghjklzxcvbnm)
35+
/// --/test_data2.txt (content: 01234567890987654321)
36+
/// --/bin
37+
/// --/docs
38+
/// --/src
39+
/// </summary>
40+
public TestAwsCompatibleCloudFileStorageManagerOnYandex()
41+
{
42+
// todo(UMV): take keys from env vars
43+
string jsonStr = File.ReadAllText("./settings.json");
44+
// reading trimmed key && secret from settings.json (non-tracking)
45+
S3StorageSettings keyAndSecrets = JsonConvert.DeserializeObject<S3StorageSettings>(jsonStr);
46+
S3StorageSettings yandexTestSettings = new S3StorageSettings()
47+
{
48+
StorageType = S3StorageType.Yandex,
49+
AccessKey = keyAndSecrets.AccessKey,
50+
SecretAccessKey = keyAndSecrets.SecretAccessKey,
51+
Endpoint = "https://storage.yandexcloud.net"
52+
};
53+
_manager = new AWSCompatibleCloudFileStorageManager(new Dictionary<string, S3StorageSettings>()
54+
{
55+
{WissanceYandexTestSource, yandexTestSettings}
56+
}, new LoggerFactory());
57+
}
58+
59+
public void Dispose()
60+
{
61+
_manager.Dispose();
62+
}
63+
64+
[Fact]
65+
public async Task TestGetBuckets()
66+
{
67+
OperationResultDto<IList<string>> result = await _manager.GetBucketsAsync(WissanceYandexTestSource);
68+
Assert.True(result.Success);
69+
Assert.Contains(result.Data, b => b == WissanceYandexTestBucket);
70+
}
71+
72+
[Theory]
73+
[InlineData("y-new-bucket-000")]
74+
public async Task TestCreateAndDeleteBucket(string newBucket)
75+
{
76+
OperationResultDto<bool> createResult = await _manager.CreateBucketAsync(WissanceYandexTestSource, newBucket);
77+
Assert.True(createResult.Success);
78+
MemoryStream fileContent = new MemoryStream(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
79+
string path = "";
80+
string fileName = "go.mod";
81+
OperationResultDto<string> result = await _manager.CreateFileAsync(WissanceYandexTestSource, path, fileName, fileContent,
82+
new Dictionary<string, string>()
83+
{
84+
{AWSCompatibleCloudFileStorageManager.BucketParam, newBucket}
85+
});
86+
Assert.True(result.Success);
87+
Assert.Equal(fileName, result.Data);
88+
// non empty bucket can't be deleted
89+
OperationResultDto<bool> rmResult = await _manager.DeleteFileAsync(WissanceYandexTestSource, fileName, new Dictionary<string, string>()
90+
{
91+
{AWSCompatibleCloudFileStorageManager.BucketParam, newBucket}
92+
});
93+
Assert.True(rmResult.Success);
94+
OperationResultDto<bool> deleteResult = await _manager.DeleteBucketAsync(WissanceYandexTestSource, newBucket);
95+
Assert.True(deleteResult.Success);
96+
}
97+
98+
[Theory]
99+
[InlineData(WissanceYandexTestBucket, "", 4)]
100+
[InlineData(WissanceYandexTestBucket, "artifacts", 2)]
101+
[InlineData(WissanceYandexTestBucket, "artifacts/txt", 2)]
102+
[InlineData(WissanceYandexTestBucket, "artifacts/archives", 0)]
103+
public async Task TestGetFiles(string bucket, string path, int expectedItems)
104+
{
105+
OperationResultDto<IList<TinyFileInfo>> result = await _manager.GetFilesAsync(WissanceYandexTestSource, path, new Dictionary<string, string>()
106+
{
107+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
108+
});
109+
Assert.True(result.Success);
110+
Assert.Equal(expectedItems, result.Data.Count);
111+
}
112+
113+
[Theory]
114+
[InlineData(WissanceYandexTestBucket, "artifacts/txt/test_data2.txt", "01234567890987654321")]
115+
public async Task TestGetFileContent(string bucket, string path, string expectedContent)
116+
{
117+
OperationResultDto<MemoryStream> result = await _manager.GetFileContentAsync(WissanceYandexTestSource, path, new Dictionary<string, string>()
118+
{
119+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
120+
});
121+
Assert.True(result.Success);
122+
byte[] buffer = result.Data.ToArray();
123+
string actualContent = UTF8Encoding.UTF8.GetString(buffer);
124+
Assert.Equal(expectedContent, actualContent);
125+
}
126+
127+
[Theory]
128+
[InlineData(WissanceYandexTestBucket, "src", "cli")]
129+
public async Task CreateDirectorySuccessfully(string bucket, string path, string dirName)
130+
{
131+
OperationResultDto<string> result = await _manager.CreateDirAsync(WissanceYandexTestSource, path, dirName, new Dictionary<string, string>()
132+
{
133+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
134+
});
135+
Assert.True(result.Success);
136+
string dirPath = $"{path}/{dirName}";
137+
Assert.Equal($"{dirPath}/", result.Data);
138+
// get list
139+
OperationResultDto<IList<TinyFileInfo>> files = await _manager.GetFilesAsync(WissanceYandexTestSource, path,
140+
new Dictionary<string, string>()
141+
{
142+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
143+
});
144+
Assert.True(files.Success);
145+
Assert.True(files.Data.Any(f => f.Name == dirName));
146+
OperationResultDto<bool> rmResult = await _manager.DeleteDirAsync(WissanceYandexTestSource, dirPath, new Dictionary<string, string>()
147+
{
148+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
149+
});
150+
Assert.True(rmResult.Success);
151+
files = await _manager.GetFilesAsync(WissanceYandexTestSource, path,
152+
new Dictionary<string, string>()
153+
{
154+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
155+
});
156+
Assert.True(files.Success);
157+
Assert.False(files.Data.Any(f => f.Name == dirName));
158+
}
159+
160+
[Theory]
161+
[InlineData(WissanceYandexTestBucket, "src", "go.mod")]
162+
public async Task CreateFileSuccessfully(string bucket, string path, string fileName)
163+
{
164+
MemoryStream fileContent = new MemoryStream(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
165+
OperationResultDto<string> result = await _manager.CreateFileAsync(WissanceYandexTestSource, path, fileName, fileContent,
166+
new Dictionary<string, string>()
167+
{
168+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
169+
});
170+
Assert.True(result.Success);
171+
// todo(UMV) : check result path
172+
string filePath = $"{path}/{fileName}";
173+
Assert.Equal(filePath, result.Data);
174+
OperationResultDto<IList<TinyFileInfo>> files = await _manager.GetFilesAsync(WissanceYandexTestSource, path,
175+
new Dictionary<string, string>()
176+
{
177+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
178+
});
179+
Assert.True(files.Success);
180+
Assert.True(files.Data.Any(f => f.Name == fileName));
181+
OperationResultDto<bool> rmResult = await _manager.DeleteFileAsync(WissanceYandexTestSource, filePath, new Dictionary<string, string>()
182+
{
183+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
184+
});
185+
Assert.True(rmResult.Success);
186+
}
187+
188+
[Theory]
189+
[InlineData(WissanceYandexTestBucket, "src", "go.mod")]
190+
public async Task TestUpdateFileSuccessfully(string bucket, string path, string fileName)
191+
{
192+
byte[] expectedContent = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
193+
MemoryStream fileContent = new MemoryStream(expectedContent);
194+
OperationResultDto<string> result = await _manager.CreateFileAsync(WissanceYandexTestSource, path, fileName, fileContent,
195+
new Dictionary<string, string>()
196+
{
197+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
198+
});
199+
Assert.True(result.Success);
200+
// todo(UMV) : check result path
201+
string filePath = $"{path}/{fileName}";
202+
Assert.Equal(filePath, result.Data);
203+
OperationResultDto<MemoryStream> contentResult = await _manager.GetFileContentAsync(WissanceYandexTestSource, filePath, new Dictionary<string, string>()
204+
{
205+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
206+
});
207+
Assert.True(result.Success);
208+
byte[] buffer = contentResult.Data.ToArray();
209+
string actualContent = UTF8Encoding.UTF8.GetString(buffer);
210+
Assert.Equal(UTF8Encoding.UTF8.GetString(expectedContent), actualContent);
211+
212+
expectedContent = new byte[] {2, 4, 8, 16, 32, 64, 128, 255};
213+
fileContent = new MemoryStream(expectedContent);
214+
OperationResultDto<bool> updateResult = await _manager.UpdateFileAsync(WissanceYandexTestSource, filePath, fileContent,
215+
new Dictionary<string, string>()
216+
{
217+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
218+
});
219+
Assert.True(updateResult.Success);
220+
contentResult = await _manager.GetFileContentAsync(WissanceYandexTestSource, filePath, new Dictionary<string, string>()
221+
{
222+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
223+
});
224+
Assert.True(result.Success);
225+
buffer = contentResult.Data.ToArray();
226+
actualContent = UTF8Encoding.UTF8.GetString(buffer);
227+
Assert.Equal(UTF8Encoding.UTF8.GetString(expectedContent), actualContent);
228+
229+
OperationResultDto<bool> rmResult = await _manager.DeleteFileAsync(WissanceYandexTestSource, filePath, new Dictionary<string, string>()
230+
{
231+
{AWSCompatibleCloudFileStorageManager.BucketParam, bucket}
232+
});
233+
Assert.True(rmResult.Success);
234+
}
235+
236+
private const string WissanceYandexTestSource = "wissance";
237+
private const string WissanceYandexTestBucket = "y-s3-test-bucket-2";
238+
239+
private readonly IAWSCompatibleFileStorageManager _manager;
240+
}
241+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
15+
<PackageReference Include="xunit" Version="2.5.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<Using Include="Xunit" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<ProjectReference Include="..\Wissance.WebApiToolkit.AWS.S3\Wissance.WebApiToolkit.AWS.S3.csproj" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<None Update="settings.json">
29+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
30+
</None>
31+
</ItemGroup>
32+
33+
</Project>

0 commit comments

Comments
 (0)