(feat): mongo grid fs
This commit is contained in:
parent
9c97e50c53
commit
6e37619ac0
@ -1183,6 +1183,138 @@
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "File",
|
||||
"item": [
|
||||
{
|
||||
"name": "01-Get",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
},
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "https://localhost:7151/api/File/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/3930ff55-67e1-4763-be59-37407f91e0a9",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "7151",
|
||||
"path": [
|
||||
"api",
|
||||
"File",
|
||||
"404c8232-9048-4519-bfba-6e78dc7005ca",
|
||||
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
|
||||
"3930ff55-67e1-4763-be59-37407f91e0a9"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Files",
|
||||
"item": [
|
||||
{
|
||||
"name": "01-Post",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"warning": "This is a duplicate header and will be overridden by the Content-Type header generated by Postman.",
|
||||
"key": "Content-Type",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
},
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "formdata",
|
||||
"formdata": [
|
||||
{
|
||||
"key": "file",
|
||||
"type": "file",
|
||||
"src": "/C:/Users/maksym/Pictures/IMG_0146.JPG"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://localhost:7151/api/Files/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "7151",
|
||||
"path": [
|
||||
"api",
|
||||
"Files",
|
||||
"404c8232-9048-4519-bfba-6e78dc7005ca",
|
||||
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "02-Delete",
|
||||
"request": {
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"warning": "This is a duplicate header and will be overridden by the Content-Type header generated by Postman.",
|
||||
"key": "Content-Type",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
},
|
||||
{
|
||||
"key": "Accept",
|
||||
"value": "application/json",
|
||||
"type": "default"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "formdata",
|
||||
"formdata": [
|
||||
{
|
||||
"key": "file",
|
||||
"type": "file",
|
||||
"src": "/C:/Users/maksym/Pictures/IMG_0146.JPG"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://localhost:7151/api/Files/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "7151",
|
||||
"path": [
|
||||
"api",
|
||||
"Files",
|
||||
"404c8232-9048-4519-bfba-6e78dc7005ca",
|
||||
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
using Core.Abstractions.DomainObjects;
|
||||
using Core.DomainObjects.L10n;
|
||||
|
||||
namespace Core.DomainObjects {
|
||||
namespace Core.DomainObjects.Documents {
|
||||
public class Category : DomainObjectDocumentBase<Category> {
|
||||
public Guid SiteId { get; set; }
|
||||
public List<CategoryL10n> L10n { get; set; }
|
||||
|
||||
199
webapi/DataProviders/Abstractions/BucketDataProviderBase.cs
Normal file
199
webapi/DataProviders/Abstractions/BucketDataProviderBase.cs
Normal file
@ -0,0 +1,199 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.GridFS;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
public BucketDataProviderBase(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client
|
||||
) : base (logger, client){ }
|
||||
|
||||
#region Upload
|
||||
private protected (Guid?, IDomainResult) Upload(Guid siteId, Guid userId, BucketFile file, string bucketName) =>
|
||||
UploadAsync(siteId, userId, file, bucketName).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UploadAsync(Guid siteId, Guid userId, BucketFile file, string bucketName) =>
|
||||
UploadAsyncCore(siteId, userId, file, bucketName);
|
||||
#endregion
|
||||
|
||||
#region Upload many
|
||||
private protected (List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) =>
|
||||
UploadAsync(siteId, userId, files, bucketName).Result;
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> UploadAsync(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) =>
|
||||
UploadManyAsyncCore(siteId, userId, files, bucketName);
|
||||
#endregion
|
||||
|
||||
#region Download
|
||||
private protected (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
|
||||
DownloadAsync(siteId, userId, fileId, bucketName).Result;
|
||||
|
||||
private protected Task<(BucketFile?, IDomainResult)> DownloadAsync(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
|
||||
DownloadAsyncCore(siteId, userId, fileId, bucketName);
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
private protected IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
|
||||
DeleteOneAsync(siteId, userId, fileId, bucketName).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteOneAsync(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
|
||||
DeleteOneAsyncCore(siteId, userId, fileId, bucketName);
|
||||
#endregion
|
||||
|
||||
#region Delete many
|
||||
private protected IDomainResult DeleteMany(Guid siteId, Guid userId, string bucketName) =>
|
||||
DeleteManyAsync(siteId, userId, bucketName).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyAsync(Guid siteId, Guid userId, string bucketName) =>
|
||||
DeleteManyAsyncCore(siteId, userId, bucketName);
|
||||
#endregion
|
||||
|
||||
#region Core methods
|
||||
private GridFSBucket CreateBucket(string bucketName) => new GridFSBucket(_client.GetDatabase(_databaseName), new GridFSBucketOptions {
|
||||
BucketName = bucketName,
|
||||
ChunkSizeBytes = 1048576, // 1MB
|
||||
WriteConcern = WriteConcern.WMajority,
|
||||
ReadPreference = ReadPreference.Secondary
|
||||
});
|
||||
|
||||
private async Task<(Guid?, IDomainResult)> UploadAsyncCore(Guid siteId, Guid userId, BucketFile file, string bucketName) {
|
||||
var (list, result) = await UploadManyAsyncCore(siteId, userId, new List<BucketFile> { file }, bucketName);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public async Task<(List<Guid>?, IDomainResult)> UploadManyAsyncCore(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) {
|
||||
var options = new GridFSUploadOptions {
|
||||
ChunkSizeBytes = 64512, // 63KB
|
||||
};
|
||||
|
||||
try {
|
||||
var bucket = CreateBucket(bucketName);
|
||||
|
||||
var result = new List<Guid>();
|
||||
foreach (var file in files) {
|
||||
|
||||
options.Metadata = new BsonDocument {
|
||||
{ "siteId", $"{siteId}"},
|
||||
{ "userId", $"{userId}"},
|
||||
{ "fileName", file.Name },
|
||||
{ "contentType", file.ContentType }
|
||||
};
|
||||
|
||||
var fileId = Guid.NewGuid();
|
||||
await bucket.UploadFromBytesAsync($"{fileId}", file.Bytes, options);
|
||||
result.Add(fileId);
|
||||
}
|
||||
|
||||
return result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Bucket data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(BucketFile?, IDomainResult)> DownloadAsyncCore(Guid siteId, Guid userId, Guid fileId, string bucketName) {
|
||||
try {
|
||||
var filter = Builders<GridFSFileInfo>.Filter.And(
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
|
||||
);
|
||||
|
||||
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
|
||||
|
||||
var bucket = CreateBucket(bucketName);
|
||||
|
||||
using var cursor = await bucket.FindAsync(filter, new GridFSFindOptions {
|
||||
Limit = 1,
|
||||
Sort = sort,
|
||||
});
|
||||
|
||||
// fileInfo either has the matching file information or is null
|
||||
var fileInfo = (await cursor.ToListAsync()).FirstOrDefault();
|
||||
if (fileInfo == null)
|
||||
return IDomainResult.NotFound<BucketFile?>();
|
||||
|
||||
var fileName = fileInfo.Metadata["fileName"].ToString() ?? "";
|
||||
if (fileName == null)
|
||||
return IDomainResult.Failed<BucketFile?>();
|
||||
|
||||
var contentType = fileInfo.Metadata["contentType"].ToString() ?? "";
|
||||
if (contentType == null)
|
||||
return IDomainResult.Failed<BucketFile?>();
|
||||
|
||||
var bytes = await bucket.DownloadAsBytesByNameAsync($"{fileId}", new GridFSDownloadByNameOptions {
|
||||
Revision = -1
|
||||
});
|
||||
|
||||
if(bytes == null)
|
||||
return IDomainResult.Failed<BucketFile?>();
|
||||
|
||||
return IDomainResult.Success(new BucketFile(fileName, bytes, contentType));
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Bucket data provider error");
|
||||
return IDomainResult.Failed<BucketFile?>();
|
||||
}
|
||||
}
|
||||
|
||||
private Task<IDomainResult> DeleteOneAsyncCore(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
|
||||
DeleteAsyncCore(siteId, userId, Builders<GridFSFileInfo>.Filter.And(
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
|
||||
), bucketName);
|
||||
|
||||
private Task<IDomainResult> DeleteManyAsyncCore(Guid siteId, Guid userId, string bucketName) =>
|
||||
DeleteAsyncCore(siteId, userId, Builders<GridFSFileInfo>.Filter.And(
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
|
||||
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}")
|
||||
), bucketName);
|
||||
|
||||
private async Task<IDomainResult> DeleteAsyncCore(Guid siteId, Guid userId, FilterDefinition<GridFSFileInfo> filter, string bucketName) {
|
||||
try {
|
||||
var bucket = CreateBucket(bucketName);
|
||||
|
||||
using var cursor = await bucket.FindAsync(filter);
|
||||
|
||||
var count = 0;
|
||||
foreach (var fileInfo in await cursor.ToListAsync()) {
|
||||
await bucket.DeleteAsync(fileInfo.Id);
|
||||
count++;
|
||||
}
|
||||
|
||||
return count > 0
|
||||
? IDomainResult.Success()
|
||||
: IDomainResult.NotFound();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Bucket data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
220
webapi/DataProviders/Abstractions/CollectionDataProviderBase.cs
Normal file
220
webapi/DataProviders/Abstractions/CollectionDataProviderBase.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using Core.Abstractions.DomainObjects;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class CollectionDataProviderBase<T> : DataProviderBase<CollectionDataProviderBase<T>> where T : DomainObjectDocumentBase<T> {
|
||||
|
||||
private protected readonly IIdGenerator _idGenerator;
|
||||
private protected readonly ISessionService _sessionService;
|
||||
|
||||
/// <summary>
|
||||
/// Main constructor
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="idGenerator"></param>
|
||||
/// <param name="dataProviderUtils"></param>
|
||||
public CollectionDataProviderBase(
|
||||
ILogger<CollectionDataProviderBase<T>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService
|
||||
) : base (logger, client){
|
||||
_idGenerator = idGenerator;
|
||||
_sessionService = sessionService;
|
||||
}
|
||||
|
||||
#region Insert
|
||||
private protected (Guid?, IDomainResult) Insert(T obj, string collectionName) =>
|
||||
InsertAsync(obj, collectionName).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) Insert(T obj, string collectionName, Guid sessionId) =>
|
||||
InsertAsync(obj, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName) =>
|
||||
InsertAsyncCore(obj, collectionName, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName, Guid sessionId) =>
|
||||
InsertAsyncCore(obj, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region InsertMany
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, string collectionName) =>
|
||||
InsertManyAsync(objList, collectionName).Result;
|
||||
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, string collectionName, Guid sessionId) =>
|
||||
InsertManyAsync(objList, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, string collectionName) =>
|
||||
InsertManyAsyncCore(objList, collectionName, null);
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, string collectionName, Guid sessionId) =>
|
||||
InsertManyAsyncCore(objList, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
GetWithPredicateCore(predicate, 0, 0, collectionName);
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate, int skip, int limit, string collectionName) =>
|
||||
GetWithPredicateCore(predicate, skip, limit, collectionName);
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, collectionName).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
UpdateWithPredicateAsyncCore(obj, predicate, collectionName, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
UpdateWithPredicateAsyncCore(obj, predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteWithPredicateAsync(predicate, collectionName).Result;
|
||||
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteWithPredicateAsyncCore(predicate, collectionName, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteWithPredicateAsyncCore(predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region DeleteMany
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteManyWithPredicateAsync(predicate, collectionName).Result;
|
||||
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteManyWithPredicateAsyncCore(predicate, collectionName, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsyncCore(predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region Core methods
|
||||
private async protected Task<(Guid?, IDomainResult)> InsertAsyncCore(T obj, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj);
|
||||
else
|
||||
collection.InsertOne(obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(List<Guid>?, IDomainResult)> InsertManyAsyncCore(List<T> objList, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList);
|
||||
else
|
||||
collection.InsertMany(objList);
|
||||
|
||||
return IDomainResult.Success(objList.Select(x => x.Id).ToList());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
|
||||
private (List<T>?, IDomainResult) GetWithPredicateCore(Expression<Func<T, bool>> predicate, int skip, int limit, string collectionName) {
|
||||
try {
|
||||
var result = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName)
|
||||
.Find(predicate).Skip(skip).Limit(limit).ToList();
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<T>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<T>?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsyncCore(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj);
|
||||
else
|
||||
await collection.ReplaceOneAsync(predicate, obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDomainResult> DeleteWithPredicateAsyncCore(Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.DeleteOneAsync<T>(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await collection.DeleteOneAsync<T>(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDomainResult> DeleteManyWithPredicateAsyncCore(Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await collection.DeleteManyAsync(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,264 +1,34 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using Core.Abstractions.DomainObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
public abstract class DataProviderBase<T> where T : DomainObjectDocumentBase<T> {
|
||||
|
||||
private protected const string _databaseName = "reactredux";
|
||||
|
||||
private protected readonly ILogger<DataProviderBase<T>> _logger;
|
||||
private protected readonly IMongoClient _client;
|
||||
private protected readonly IIdGenerator _idGenerator;
|
||||
private protected readonly ISessionService _sessionService;
|
||||
|
||||
private protected List<T>? _collection;
|
||||
|
||||
/// <summary>
|
||||
/// Main constructor
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class DataProviderBase<T> {
|
||||
private protected const string _databaseName = "reactredux";
|
||||
|
||||
private protected readonly ILogger<T> _logger;
|
||||
private protected readonly IMongoClient _client;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="idGenerator"></param>
|
||||
/// <param name="dataProviderUtils"></param>
|
||||
public DataProviderBase(
|
||||
ILogger<DataProviderBase<T>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService
|
||||
ILogger<T> logger,
|
||||
IMongoClient client
|
||||
) {
|
||||
_logger = logger;
|
||||
_client = client;
|
||||
_idGenerator = idGenerator;
|
||||
_sessionService = sessionService;
|
||||
}
|
||||
|
||||
#region Insert
|
||||
private protected (Guid?, IDomainResult) Insert(T obj, string collectionName) =>
|
||||
InsertAsync(obj, collectionName).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) Insert(T obj, string collectionName, Guid sessionId) =>
|
||||
InsertAsync(obj, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName) =>
|
||||
InsertAsyncCore(obj, collectionName, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName, Guid sessionId) =>
|
||||
InsertAsyncCore(obj, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region InsertMany
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, string collectionName) =>
|
||||
InsertManyAsync(objList, collectionName).Result;
|
||||
|
||||
private protected (List<Guid>?, IDomainResult) InsertMany(List<T> objList, string collectionName, Guid sessionId) =>
|
||||
InsertManyAsync(objList, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, string collectionName) =>
|
||||
InsertManyAsyncCore(objList, collectionName, null);
|
||||
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, string collectionName, Guid sessionId) =>
|
||||
InsertManyAsyncCore(objList, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
GetWithPredicateCore(predicate, 0, 0, collectionName);
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate, int skip, int limit, string collectionName) =>
|
||||
GetWithPredicateCore(predicate, skip, limit, collectionName);
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, collectionName).Result;
|
||||
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
UpdateWithPredicateAsyncCore(obj, predicate, collectionName, null);
|
||||
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
UpdateWithPredicateAsyncCore(obj, predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
#region Exists
|
||||
private protected (Guid?, IDomainResult) Exists(Guid id, string collectionName) {
|
||||
var (_resultList, result) = GetWithPredicate(x => x.Id == id, 0, 0, collectionName);
|
||||
|
||||
return (result.Status != DomainOperationStatus.Failed && _resultList != null && _resultList.Count > 0
|
||||
? id
|
||||
:null,
|
||||
result);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteWithPredicateAsync(predicate, collectionName).Result;
|
||||
|
||||
private protected IDomainResult DeleteWithPredicate(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteWithPredicateAsyncCore(predicate, collectionName, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteWithPredicateAsyncCore(predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
|
||||
#region DeleteMany
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteManyWithPredicateAsync(predicate, collectionName).Result;
|
||||
|
||||
private protected IDomainResult DeleteManyWithPredicate(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, collectionName, sessionId).Result;
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName) =>
|
||||
DeleteManyWithPredicateAsyncCore(predicate, collectionName, null);
|
||||
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, string collectionName, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsyncCore(predicate, collectionName, sessionId);
|
||||
#endregion
|
||||
|
||||
|
||||
#region Core methods
|
||||
private async protected Task<(Guid?, IDomainResult)> InsertAsyncCore(T obj, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
if (_collection != null) {
|
||||
obj.Id = Guid.NewGuid();
|
||||
_collection.Add(obj);
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj);
|
||||
else
|
||||
collection.InsertOne(obj);
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(List<Guid>?, IDomainResult)> InsertManyAsyncCore(List<T> objList, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
if (_collection != null) {
|
||||
_collection = _collection.Concat(objList).ToList();
|
||||
return IDomainResult.Success(objList.Select(x => x.Id).ToList());
|
||||
}
|
||||
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList);
|
||||
else
|
||||
collection.InsertMany(objList);
|
||||
|
||||
return IDomainResult.Success(objList.Select(x => x.Id).ToList());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
}
|
||||
}
|
||||
|
||||
private (List<T>?, IDomainResult) GetWithPredicateCore(Expression<Func<T, bool>> predicate, int skip, int limit, string collectionName) {
|
||||
try {
|
||||
List<T>? result;
|
||||
|
||||
if (_collection != null) {
|
||||
result = _collection?.AsQueryable()
|
||||
.Where(predicate).ToList();
|
||||
}
|
||||
else {
|
||||
result = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName)
|
||||
.Find(predicate).Skip(skip).Limit(limit).ToList();
|
||||
}
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<T>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<T>?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsyncCore(T obj, Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
if (_collection != null) {
|
||||
// remove element(s) from list
|
||||
foreach (var element in _collection.AsQueryable().Where(predicate))
|
||||
_collection = _collection.Where(x => x.Id != element.Id).ToList();
|
||||
|
||||
// add updated element
|
||||
_collection.Add(obj);
|
||||
}
|
||||
else {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj);
|
||||
else
|
||||
await collection.ReplaceOneAsync(predicate, obj);
|
||||
}
|
||||
|
||||
return IDomainResult.Success(obj.Id);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDomainResult> DeleteWithPredicateAsyncCore(Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.DeleteOneAsync<T>(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await collection.DeleteOneAsync<T>(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDomainResult> DeleteManyWithPredicateAsyncCore(Expression<Func<T, bool>> predicate, string collectionName, Guid? sessionId) {
|
||||
try {
|
||||
var collection = _client.GetDatabase(_databaseName).GetCollection<T>(collectionName);
|
||||
|
||||
if (sessionId != null)
|
||||
await collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
else
|
||||
await collection.DeleteManyAsync(predicate);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
19
webapi/DataProviders/BucketFile.cs
Normal file
19
webapi/DataProviders/BucketFile.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders {
|
||||
public class BucketFile {
|
||||
public string Name { get; private set; }
|
||||
public byte[] Bytes { get; private set; }
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
public BucketFile(string name, byte[] bytes, string contentType) {
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
webapi/DataProviders/Buckets/ImagesBucketDataProvider.cs
Normal file
122
webapi/DataProviders/Buckets/ImagesBucketDataProvider.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
namespace DataProviders.Buckets {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IImagesBucketDataProvider {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) Upload(Guid siteId, Guid userId, BucketFile file);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
(List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
(BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult DeletMany(Guid siteId, Guid userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ImagesBucketDataProvider : BucketDataProviderBase, IImagesBucketDataProvider {
|
||||
|
||||
private const string _bucketName = "images";
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="client"></param>
|
||||
public ImagesBucketDataProvider(
|
||||
ILogger<BucketDataProviderBase> logger,
|
||||
IMongoClient client
|
||||
) : base(logger, client) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public (Guid?, IDomainResult) Upload(Guid siteId, Guid userId, BucketFile file) => Upload(siteId, userId, file, _bucketName);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
public (List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files) => UploadMany(siteId, userId, files, _bucketName);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId) => Download(siteId, userId, fileId, _bucketName);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
public IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId) => DeleteOne(siteId, userId, fileId, _bucketName);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public IDomainResult DeletMany(Guid siteId, Guid userId) => DeleteMany(siteId, userId, _bucketName);
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ using MongoDB.Driver;
|
||||
using DataProviders.Abstractions;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace DataProviders {
|
||||
namespace DataProviders.Collections {
|
||||
public interface IBlogCatalogDataProvider {
|
||||
(Guid?, IDomainResult) Insert(BlogItem blogItem);
|
||||
(BlogItem?, IDomainResult) Get(Guid siteId, Guid blogId);
|
||||
@ -20,12 +20,12 @@ namespace DataProviders {
|
||||
IDomainResult DeleteAll(Guid siteId);
|
||||
}
|
||||
|
||||
public class BlogCatalogDataProvider : DataProviderBase<BlogItem>, IBlogCatalogDataProvider {
|
||||
public class BlogCatalogDataProvider : CollectionDataProviderBase<BlogItem>, IBlogCatalogDataProvider {
|
||||
|
||||
private const string _collectionName = "blogcatalog";
|
||||
|
||||
public BlogCatalogDataProvider(
|
||||
ILogger<DataProviderBase<BlogItem>> logger,
|
||||
ILogger<CollectionDataProviderBase<BlogItem>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
|
||||
@ -6,9 +6,9 @@ using MongoDB.Bson.Serialization;
|
||||
using DomainResults.Common;
|
||||
|
||||
using DataProviders.Abstractions;
|
||||
using Core.DomainObjects;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace DataProviders {
|
||||
namespace DataProviders.Collections {
|
||||
|
||||
public interface ICategoryDataProvider {
|
||||
(Guid?, IDomainResult) Insert(Category obj);
|
||||
@ -21,11 +21,11 @@ namespace DataProviders {
|
||||
IDomainResult DeleteAll(Guid siteId);
|
||||
}
|
||||
|
||||
public class CategoryDataProvider : DataProviderBase<Category>, ICategoryDataProvider {
|
||||
public class CategoryDataProvider : CollectionDataProviderBase<Category>, ICategoryDataProvider {
|
||||
|
||||
private const string _collectionName = "categories";
|
||||
public CategoryDataProvider(
|
||||
ILogger<DataProviderBase<Category>> logger,
|
||||
ILogger<CollectionDataProviderBase<Category>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
|
||||
@ -1,21 +1,24 @@
|
||||
using Core.DomainObjects.Documents;
|
||||
using DataProviders.Abstractions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using DomainResults.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace DataProviders {
|
||||
using DataProviders.Abstractions;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
|
||||
public interface IContentDataProvider {
|
||||
(Content?, IDomainResult) Get(Guid siteId, string locale);
|
||||
}
|
||||
|
||||
public class ContentDataProvider : DataProviderBase<Content>, IContentDataProvider {
|
||||
public class ContentDataProvider : CollectionDataProviderBase<Content>, IContentDataProvider {
|
||||
|
||||
private const string _collectionName = "content";
|
||||
public ContentDataProvider(
|
||||
ILogger<DataProviderBase<Content>> logger,
|
||||
ILogger<CollectionDataProviderBase<Content>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
|
||||
@ -8,7 +8,7 @@ using MongoDB.Driver;
|
||||
using DataProviders.Abstractions;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace DataProviders {
|
||||
namespace DataProviders.Collections {
|
||||
|
||||
public interface IShopCartDataProvider {
|
||||
(Guid?, IDomainResult) Insert(ShopCartItem obj);
|
||||
@ -19,10 +19,10 @@ namespace DataProviders {
|
||||
IDomainResult DeleteAll(Guid siteId, Guid userId);
|
||||
}
|
||||
|
||||
public class ShopCartDataProvider : DataProviderBase<ShopCartItem>, IShopCartDataProvider {
|
||||
public class ShopCartDataProvider : CollectionDataProviderBase<ShopCartItem>, IShopCartDataProvider {
|
||||
private const string _collectionName = "shopcart";
|
||||
public ShopCartDataProvider(
|
||||
ILogger<DataProviderBase<ShopCartItem>> logger,
|
||||
ILogger<CollectionDataProviderBase<ShopCartItem>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
|
||||
@ -8,7 +8,7 @@ using MongoDB.Driver;
|
||||
using Core.DomainObjects.Documents;
|
||||
using DataProviders.Abstractions;
|
||||
|
||||
namespace DataProviders {
|
||||
namespace DataProviders.Collections {
|
||||
public interface IShopCatalogDataProvider {
|
||||
(Guid?, IDomainResult) Insert(ShopItem obj);
|
||||
(ShopItem?, IDomainResult) Get(Guid siteId, string sku);
|
||||
@ -20,12 +20,12 @@ namespace DataProviders {
|
||||
IDomainResult DeleteAll(Guid siteId);
|
||||
}
|
||||
|
||||
public class ShopCatalogDataProvider : DataProviderBase<ShopItem>, IShopCatalogDataProvider {
|
||||
public class ShopCatalogDataProvider : CollectionDataProviderBase<ShopItem>, IShopCatalogDataProvider {
|
||||
|
||||
private const string _collectionName = "shopcatalog";
|
||||
|
||||
public ShopCatalogDataProvider(
|
||||
ILogger<DataProviderBase<ShopItem>> logger,
|
||||
ILogger<CollectionDataProviderBase<ShopItem>> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
|
||||
@ -37,8 +37,6 @@ namespace DataProviders.Converters {
|
||||
default:
|
||||
throw new NotImplementedException($"No implementation to deserialize {type}");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
private void Serialize(IBsonWriter writer, List<T> values) {
|
||||
if (values != null) {
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.17.1" />
|
||||
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.17.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -4,6 +4,9 @@ using MongoDB.Driver;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.IdGenerators;
|
||||
|
||||
using DataProviders.Collections;
|
||||
using DataProviders.Buckets;
|
||||
|
||||
namespace DataProviders.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions {
|
||||
@ -13,14 +16,21 @@ namespace DataProviders.Extensions
|
||||
services.AddSingleton<IMongoClient>(x => new MongoClient(config.ConnectionString));
|
||||
services.AddSingleton<IIdGenerator, GuidGenerator>();
|
||||
|
||||
Mappings.RegisterClassMap();
|
||||
|
||||
services.AddSingleton<ISessionService, SessionService>();
|
||||
|
||||
#region Collections
|
||||
services.AddSingleton<IContentDataProvider, ContentDataProvider>();
|
||||
services.AddSingleton<IShopCatalogDataProvider, ShopCatalogDataProvider>();
|
||||
services.AddSingleton<IShopCartDataProvider, ShopCartDataProvider>();
|
||||
services.AddSingleton<ICategoryDataProvider, CategoryDataProvider>();
|
||||
services.AddSingleton<IBlogCatalogDataProvider, BlogCatalogDataProvider>();
|
||||
Mappings.RegisterClassMap();
|
||||
#endregion
|
||||
|
||||
#region Buckets
|
||||
services.AddSingleton<IImagesBucketDataProvider, ImagesBucketDataProvider>();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
webapi/WeatherForecast/Controllers/FileController.cs
Normal file
62
webapi/WeatherForecast/Controllers/FileController.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using DomainResults.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Net.Http.Headers;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[AllowAnonymous]
|
||||
[Route("api/[controller]")]
|
||||
public class FileController : Controller {
|
||||
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fileService"></param>
|
||||
public FileController(
|
||||
IFileService fileService
|
||||
) {
|
||||
_fileService = fileService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userid"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{siteId}/{userId}/{fileId}")]
|
||||
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userid, [FromRoute] Guid fileId) {
|
||||
var (file, result) = _fileService.Get(siteId, userid, fileId);
|
||||
|
||||
if (!result.IsSuccess || file == null)
|
||||
return result.ToActionResult();
|
||||
|
||||
var stream = new MemoryStream(file.Bytes);
|
||||
return new FileStreamResult(stream, file.ContentType) {
|
||||
FileDownloadName = file.Name
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpDelete("{siteId}/{userId}/{fileId}")]
|
||||
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] Guid fileId) {
|
||||
var result = _fileService.Delete(siteId, userId, fileId);
|
||||
return result.ToActionResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
54
webapi/WeatherForecast/Controllers/FilesController.cs
Normal file
54
webapi/WeatherForecast/Controllers/FilesController.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using DomainResults.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[AllowAnonymous]
|
||||
[Route("api/[controller]")]
|
||||
public class FilesController : Controller {
|
||||
|
||||
private readonly IFilesService _filesService;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="filesService"></param>
|
||||
public FilesController(
|
||||
IFilesService filesService
|
||||
) {
|
||||
_filesService = filesService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("{siteId}/{userId}")]
|
||||
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, List<IFormFile> file) {
|
||||
var result = _filesService.Post(siteId, userId, file);
|
||||
return result.ToActionResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpDelete("{siteId}/{userId}")]
|
||||
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) {
|
||||
var result = _filesService.Delete(siteId, userId);
|
||||
return result.ToActionResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using Core.Abstractions.DomainObjects;
|
||||
using Core.Abstractions.Models;
|
||||
using Core.DomainObjects;
|
||||
using Core.DomainObjects.Documents;
|
||||
using Core.Enumerations;
|
||||
using WeatherForecast.Models.Responses;
|
||||
using WeatherForecast.Models.Responses.L10n;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using Core.Abstractions.Models;
|
||||
using Core.DomainObjects;
|
||||
using Core.DomainObjects.Documents;
|
||||
using Core.DomainObjects.L10n;
|
||||
using Core.Enumerations;
|
||||
using Extensions;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using Core.Abstractions.Models;
|
||||
using Core.DomainObjects;
|
||||
using Core.DomainObjects.Documents;
|
||||
using Core.Enumerations;
|
||||
using WeatherForecast.Models.Responses.L10n;
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ using ExtensionMethods;
|
||||
using DataProviders;
|
||||
|
||||
using Core.DomainObjects;
|
||||
using Core.DomainObjects.Documents;
|
||||
using Core.DomainObjects.L10n;
|
||||
using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models.Requests;
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -7,6 +7,8 @@ using Core.DomainObjects;
|
||||
using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models.Requests;
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ using Core.Abstractions;
|
||||
using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using DomainResults.Common;
|
||||
|
||||
using DataProviders;
|
||||
using DataProviders.Collections;
|
||||
|
||||
using WeatherForecast.Models.Responses;
|
||||
|
||||
|
||||
87
webapi/WeatherForecast/Services/FileService.cs
Normal file
87
webapi/WeatherForecast/Services/FileService.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IFileService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
(BucketFile?, IDomainResult) Get(Guid siteId, Guid userId, Guid fileId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult Delete(Guid siteId, Guid userId, Guid fileId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FileService : IFileService {
|
||||
|
||||
private readonly ILogger<FilesService> _logger;
|
||||
private readonly IImagesBucketDataProvider _imageBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="imageBucketDataProvider"></param>
|
||||
public FileService(
|
||||
ILogger<FilesService> logger,
|
||||
IImagesBucketDataProvider imageBucketDataProvider
|
||||
) {
|
||||
_logger = logger;
|
||||
_imageBucketDataProvider = imageBucketDataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
public (BucketFile?, IDomainResult) Get(Guid siteId, Guid userId, Guid fileId) {
|
||||
try {
|
||||
var (file, result) = _imageBucketDataProvider.Download(siteId, userId, fileId);
|
||||
if (!result.IsSuccess || file == null)
|
||||
return (null, result);
|
||||
|
||||
return IDomainResult.Success(file);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return IDomainResult.Failed<BucketFile?>(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileId"></param>
|
||||
/// <returns></returns>
|
||||
public IDomainResult Delete(Guid siteId, Guid userId, Guid fileId) {
|
||||
try {
|
||||
return _imageBucketDataProvider.DeleteOne(siteId, userId, fileId);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return IDomainResult.Failed(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
webapi/WeatherForecast/Services/FilesService.cs
Normal file
100
webapi/WeatherForecast/Services/FilesService.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IFilesService {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
(List<Guid>?, IDomainResult) Post(Guid siteId, Guid userId, List<IFormFile> files);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
IDomainResult Delete(Guid siteId, Guid userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FilesService : IFilesService {
|
||||
|
||||
private readonly ILogger<FilesService> _logger;
|
||||
private readonly IImagesBucketDataProvider _imageBucketDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="imageBucketDataProvider"></param>
|
||||
public FilesService(
|
||||
ILogger<FilesService> logger,
|
||||
IImagesBucketDataProvider imageBucketDataProvider
|
||||
) {
|
||||
_logger = logger;
|
||||
_imageBucketDataProvider = imageBucketDataProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process uploaded files
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
public (List<Guid>?, IDomainResult) Post(Guid siteId, Guid userId, List<IFormFile> files) {
|
||||
try {
|
||||
// Don't rely on or trust the FileName property without validation.
|
||||
|
||||
var newFiles = new List<BucketFile>();
|
||||
foreach (var formFile in files) {
|
||||
if (formFile.Length > 0) {
|
||||
using var ms = new MemoryStream();
|
||||
formFile.CopyTo(ms);
|
||||
|
||||
newFiles.Add(new BucketFile(formFile.FileName, ms.ToArray(), formFile.ContentType));
|
||||
}
|
||||
}
|
||||
|
||||
var (list, result) = _imageBucketDataProvider.UploadMany(siteId, userId, newFiles);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return IDomainResult.Failed<List<Guid>?>();
|
||||
|
||||
return IDomainResult.Success(list);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return IDomainResult.Failed<List<Guid>?> (ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public IDomainResult Delete(Guid siteId, Guid userId) {
|
||||
try {
|
||||
return _imageBucketDataProvider.DeletMany(siteId, userId);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return IDomainResult.Failed(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using DomainResults.Common;
|
||||
|
||||
using DataProviders;
|
||||
using DataProviders.Collections;
|
||||
|
||||
using Core.Abstractions;
|
||||
using Core.Enumerations;
|
||||
|
||||
@ -6,6 +6,7 @@ using Core.Enumerations;
|
||||
using Core.Abstractions;
|
||||
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@ using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models;
|
||||
using WeatherForecast.Models.Requests;
|
||||
using DataProviders.Collections;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ using Core.Enumerations;
|
||||
|
||||
using WeatherForecast.Models;
|
||||
using WeatherForecast.Models.Responses;
|
||||
using DataProviders.Collections;
|
||||
using Core.DomainObjects.Documents;
|
||||
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
|
||||
@ -80,6 +80,9 @@ namespace WeatherForecast {
|
||||
services.AddScoped<ICategoryItemService, CategoryItemService>();
|
||||
services.AddScoped<ICategoryItemsService, CategoryItemsService>();
|
||||
|
||||
services.AddScoped<IFileService, FileService>();
|
||||
services.AddScoped<IFilesService, FilesService>();
|
||||
|
||||
services.RegisterDataproviders(appSettings);
|
||||
|
||||
#region Swagger
|
||||
|
||||
Loading…
Reference in New Issue
Block a user