Rest/MongoRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Sandbox;
namespace Mongo.Rest;
public abstract class MongoRepository<T> : IMongoRepository<T> where T : class, new()
{
private readonly JsonSerializerOptions _options = new()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
public string CollectionName => MongoCollectionHelper.GetAttribute( typeof(T) )?.Name ?? string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="MongoRepository{T}"/> class.
/// Mainly used for unit tests
/// </summary>
/// <param name="scene">The scene.</param>
protected MongoRepository( Scene? scene = null )
{
System = scene is null
? Game.ActiveScene.GetSystem<MongoRestSystem>()
: scene.GetSystem<MongoRestSystem>();
}
private MongoRestSystem System { get; }
public Type GetInnerType() => typeof(T);
private string Url =>
$"{System.Options.Url}/api/{System.Options.Version.ToString().ToLower()}/collections/{CollectionName}";
/// <summary>
/// Inserts a list of documents into the collection.
/// </summary>
/// <param name="values">The documents to insert.</param>
/// <returns>
/// A boolean indicating whether the insert was successful.
/// </returns>
public virtual async ValueTask<bool> InsertAsync( params T[] values )
{
var url = $"{Url}/insert";
var documents = new List<BsonDocument>();
foreach ( var value in values )
{
var document = BsonDocument.Parse( value );
documents.Add( document );
}
var json = JsonSerializer.Serialize( documents );
Log.Info( "Json: " + json );
var response =
await Http.RequestAsync( url, "POST", new StringContent( json, Encoding.UTF8, "application/json" ) );
return response.StatusCode is HttpStatusCode.OK;
}
/// <summary>
/// Retrieves a list of documents from the collection that match the filter.
/// </summary>
/// <param name="filter">The filter to apply to the documents.</param>
/// <param name="limit">The maximum number of documents to return. Defaults to 100.</param>
/// <returns>
/// A list of documents that match the filter.
/// </returns>
public virtual async ValueTask<IEnumerable<T>> GetAsync( Action<T>? filter = null, int limit = 1 )
{
var configureFilter = new T();
filter?.Invoke( configureFilter );
var url = $"{Url}/get?limit={limit}";
var json = filter is not null
? JsonSerializer.Serialize( configureFilter, _options )
: MongoFilter.All.ToString();
var response =
await Http.RequestAsync( url, "POST",
new StringContent( json, Encoding.UTF8, "application/json" ) );
if ( response.StatusCode is not HttpStatusCode.OK )
return Array.Empty<T>();
var jsonResult = await response.Content.ReadAsStringAsync();
var array = JsonNode.Parse( jsonResult );
if ( array is null ) return Array.Empty<T>();
var documents = new List<T>();
foreach ( var node in array.AsArray() )
{
if ( node is null ) continue;
var document = node.Deserialize<BsonDocument>( _options );
documents.Add( document.Data as T );
}
return documents;
}
/// <summary>
/// Updates multiple documents in the collection.
/// </summary>
/// <param name="filter">The filter to apply to the documents.</param>
/// <param name="update">The update to apply to the documents.</param>
/// <returns>
/// A boolean indicating whether the update was successful.
/// </returns>
public virtual async ValueTask<bool> UpdateAsync( Action<T> filter, Action<T> update )
{
var configureFilter = new T();
var configureUpdate = new T();
filter( configureFilter );
update( configureUpdate );
var url = $"{Url}/update";
var filterDocument = BsonDocument.Parse( configureFilter );
var updateDocument = BsonDocument.Parse( configureUpdate );
updateDocument.RemoveId();
var json = new UpdateRequest( filterDocument, updateDocument ).ToString();
var response =
await Http.RequestAsync( url, "PUT", new StringContent( json, Encoding.UTF8, "application/json" ) );
return response.StatusCode is HttpStatusCode.OK;
}
/// <summary>
/// Deletes multiple documents in the collection that match the filter.
/// </summary>
/// <param name="filter">The filter to apply to the documents.</param>
/// <returns>
/// A boolean indicating whether the delete was successful.
/// </returns>
public virtual async ValueTask<bool> DeleteAsync( Action<T>? filter = null )
{
var configureFilter = new T();
filter?.Invoke( configureFilter );
var url = $"{Url}/delete";
var json = filter is not null
? JsonSerializer.Serialize( configureFilter, _options )
: MongoFilter.All.ToString();
var content = new StringContent( json, Encoding.UTF8, "application/json" );
var response = await Http.RequestAsync( url, "POST", content );
return response.StatusCode is HttpStatusCode.OK;
}
/// <summary>
/// Counts the number of documents that match the filter.
/// </summary>
/// <param name="filter">The filter to apply to the documents.</param>
/// <returns>The number of documents that match the filter.</returns>
public virtual async ValueTask<int> CountAsync( Action<T>? filter = null )
{
var configureFilter = new T();
filter?.Invoke( configureFilter );
var url = $"{Url}/count";
var json = filter is not null
? JsonSerializer.Serialize( configureFilter, _options )
: MongoFilter.All.ToString();
var content = new StringContent( json, Encoding.UTF8, "application/json" );
var response = await Http.RequestAsync( url, "POST", content );
if ( response.StatusCode is not HttpStatusCode.OK ) return 0;
var result = await response.Content.ReadAsStringAsync();
return int.Parse( result );
}
/// <summary>
/// Checks if a document that matches the filter exists in the collection.
/// </summary>
/// <param name="filter">The filter to apply to the documents.</param>
/// <returns>
/// true if a document that matches the filter exists in the collection, false otherwise.
/// </returns>
public async ValueTask<bool> ExistsAsync( Action<T> filter ) => await CountAsync( filter ) > 0;
}