diff --git a/src/Cosmonaut/Cosmonaut.csproj b/src/Cosmonaut/Cosmonaut.csproj index e109286..d298165 100644 --- a/src/Cosmonaut/Cosmonaut.csproj +++ b/src/Cosmonaut/Cosmonaut.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard2.0 Nick Chapsas A supercharged .NET SDK for Azure CosmosDB with ORM support A powerful and easy to use SDK for Azure CosmosDB. @@ -12,24 +12,16 @@ https://github.com/Elfocrash/Cosmonaut azure entitystore entity db orm microsoft cosmos cosmosdb documentdb docdb nosql azureofficial dotnetcore netcore netstandard Please report any issues on Github. - 2.11.3 - + 3.0 Nick Chapsas https://raw.githubusercontent.com/Elfocrash/Cosmonaut/develop/logo.png - 2.11.3 - - - - latest - - - - latest + 3.0 - + + diff --git a/src/Cosmonaut/Cosmonaut.csproj.DotSettings b/src/Cosmonaut/Cosmonaut.csproj.DotSettings deleted file mode 100644 index 58ad6c8..0000000 --- a/src/Cosmonaut/Cosmonaut.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp71 \ No newline at end of file diff --git a/src/Cosmonaut/CosmonautClient.cs b/src/Cosmonaut/CosmonautClient.cs index 68bcc29..7dac4f4 100644 --- a/src/Cosmonaut/CosmonautClient.cs +++ b/src/Cosmonaut/CosmonautClient.cs @@ -2,382 +2,114 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Cosmonaut.Diagnostics; using Cosmonaut.Extensions; using Cosmonaut.Factories; -using Cosmonaut.Response; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; -using Newtonsoft.Json; +using Microsoft.Azure.Cosmos; namespace Cosmonaut { public class CosmonautClient : ICosmonautClient { - private readonly JsonSerializerSettings _serializerSettings; + private readonly CosmosSerializer _serializerSettings; - public CosmonautClient(IDocumentClient documentClient, bool infiniteRetrying = true) + public CosmonautClient(CosmosClient cosmosClient, bool infiniteRetrying = true) { - DocumentClient = documentClient; + CosmosClient = cosmosClient; if (infiniteRetrying) - DocumentClient.SetupInfiniteRetries(); + CosmosClient.SetupInfiniteRetries(); - _serializerSettings = DocumentClient.GetJsonSerializerSettingsFromClient(); + _serializerSettings = CosmosClient.ClientOptions.Serializer; } - public CosmonautClient(Func documentClientFunc, bool infiniteRetrying = true) + public CosmonautClient(Func cosmosClientFunc, bool infiniteRetrying = true) { - DocumentClient = documentClientFunc(); + CosmosClient = cosmosClientFunc(); if (infiniteRetrying) - DocumentClient.SetupInfiniteRetries(); + CosmosClient.SetupInfiniteRetries(); - _serializerSettings = DocumentClient.GetJsonSerializerSettingsFromClient(); + _serializerSettings = CosmosClient.ClientOptions.Serializer; } public CosmonautClient( Uri endpoint, string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - ConsistencyLevel? desiredConsistencyLevel = null, + CosmosClientOptions clientOptions = null, bool infiniteRetrying = true) { - DocumentClient = DocumentClientFactory.CreateDocumentClient(endpoint, authKeyOrResourceToken, connectionPolicy, desiredConsistencyLevel); + CosmosClient = new CosmosClient(endpoint.ToString(), authKeyOrResourceToken, clientOptions); if (infiniteRetrying) - DocumentClient.SetupInfiniteRetries(); - - _serializerSettings = DocumentClient.GetJsonSerializerSettingsFromClient(); - } + CosmosClient.SetupInfiniteRetries(); - public CosmonautClient( - Uri endpoint, - string authKeyOrResourceToken, - JsonSerializerSettings jsonSerializerSettings, - ConnectionPolicy connectionPolicy = null, - ConsistencyLevel? desiredConsistencyLevel = null, - bool infiniteRetrying = true) - { - DocumentClient = DocumentClientFactory.CreateDocumentClient(endpoint, authKeyOrResourceToken, jsonSerializerSettings, connectionPolicy, desiredConsistencyLevel); - - if (infiniteRetrying) - DocumentClient.SetupInfiniteRetries(); - - _serializerSettings = DocumentClient.GetJsonSerializerSettingsFromClient(); + _serializerSettings = CosmosClient.ClientOptions.Serializer; } public CosmonautClient( string endpoint, string authKeyOrResourceToken, - ConnectionPolicy connectionPolicy = null, - ConsistencyLevel? desiredConsistencyLevel = null, - bool infiniteRetrying = true) : this(new Uri(endpoint), authKeyOrResourceToken, connectionPolicy, desiredConsistencyLevel, infiniteRetrying) - { - } - - public async Task GetDatabaseAsync(string databaseId, RequestOptions requestOptions = null) - { - var databaseUri = UriFactory.CreateDatabaseUri(databaseId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReadDatabaseAsync(databaseUri, requestOptions), databaseId) - .ExecuteCosmosQuery(); - } - - public async Task> QueryDatabasesAsync(Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default) + CosmosClientOptions clientOptions = null, + bool infiniteRetrying = true) : this(new Uri(endpoint), authKeyOrResourceToken, clientOptions, infiniteRetrying) { - if (predicate == null) predicate = x => true; - return await DocumentClient.CreateDatabaseQuery(feedOptions).Where(predicate).ToListAsync(cancellationToken); - } - - public async Task GetDocumentAsync(string databaseId, string collectionId, string documentId, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var documentUri = UriFactory.CreateDocumentUri(databaseId, collectionId, documentId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReadDocumentAsync(documentUri, requestOptions, cancellationToken), documentId) - .ExecuteCosmosQuery(); } - public async Task GetDocumentAsync(string databaseId, string collectionId, string documentId, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class + public async Task GetDatabaseAsync(string databaseId, RequestOptions requestOptions = null) { - var documentUri = UriFactory.CreateDocumentUri(databaseId, collectionId, documentId); - return await this.InvokeCosmosOperationAsync( - () => DocumentClient.ReadDocumentAsync(documentUri, requestOptions, cancellationToken), documentId) - .ExecuteCosmosQuery(); + return await CosmosClient.CreateDatabaseAsync(databaseId); } - public async Task> QueryCollectionsAsync(string databaseId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - if (predicate == null) predicate = x => true; - var databaseUri = UriFactory.CreateDatabaseUri(databaseId); - return await DocumentClient.CreateDocumentCollectionQuery(databaseUri, feedOptions).Where(predicate).ToListAsync(cancellationToken); - } - - public async Task> QueryDocumentsAsync(string databaseId, string collectionId, Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - if (predicate == null) predicate = x => true; - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await DocumentClient.CreateDocumentQuery(collectionUri, feedOptions).Where(predicate).ToListAsync(cancellationToken); - } - - public async Task> QueryDocumentsAsync(string databaseId, string collectionId, string sql, object parameters = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - var sqlParameters = parameters.ConvertToSqlParameterCollection(); - var sqlQuerySpec = sqlParameters != null && sqlParameters.Any() ? new SqlQuerySpec(sql, sqlParameters) : new SqlQuerySpec(sql); - return await DocumentClient.CreateDocumentQuery(collectionUri, sqlQuerySpec, feedOptions).ToListAsync(cancellationToken); - } - - public async Task> QueryDocumentsAsync(string databaseId, string collectionId, string sql, IDictionary parameters, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - var sqlParameters = parameters.ConvertDictionaryToSqlParameterCollection(); - var sqlQuerySpec = sqlParameters != null && sqlParameters.Any() ? new SqlQuerySpec(sql, sqlParameters) : new SqlQuerySpec(sql); - return await DocumentClient.CreateDocumentQuery(collectionUri, sqlQuerySpec, feedOptions).ToListAsync(cancellationToken); - } - - public async Task> QueryDocumentsAsync(string databaseId, string collectionId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - if (predicate == null) predicate = x => true; - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await DocumentClient.CreateDocumentQuery(collectionUri, feedOptions).Where(predicate).ToListAsync(cancellationToken); - } - - public async Task GetCollectionAsync(string databaseId, string collectionId, RequestOptions requestOptions = null) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReadDocumentCollectionAsync(collectionUri, requestOptions), collectionId) - .ExecuteCosmosQuery(); - } - - public async Task GetOfferForCollectionAsync(string databaseId, string collectionId, FeedOptions feedOptions = null, + public Task> QueryDatabasesAsync(Expression> predicate = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - var collection = await GetCollectionAsync(databaseId, collectionId); - - if (collection == null) - return null; - - return await DocumentClient.CreateOfferQuery(feedOptions).SingleOrDefaultAsync(x => x.ResourceLink == collection.SelfLink, cancellationToken); + throw new NotImplementedException(); } - public async Task GetOfferV2ForCollectionAsync(string databaseId, string collectionId, FeedOptions feedOptions = null, + public Task> QueryContainersAsync(string databaseId, Expression> predicate = null, ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - return (OfferV2) await GetOfferForCollectionAsync(databaseId, collectionId, feedOptions, cancellationToken); - } - - public async Task GetOfferForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default) - { - var database = await GetDatabaseAsync(databaseId); - - if (database == null) - return null; - - return await DocumentClient.CreateOfferQuery(feedOptions).SingleOrDefaultAsync(x => x.ResourceLink == database.SelfLink, cancellationToken); - } - - public async Task GetOfferV2ForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default) - { - return (OfferV2)await GetOfferForDatabaseAsync(databaseId, feedOptions, cancellationToken); - } - - public async Task> QueryOffersAsync(Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - if (predicate == null) predicate = x => true; - return await DocumentClient.CreateOfferQuery(feedOptions).Where(predicate).ToListAsync(cancellationToken); + throw new NotImplementedException(); } - public async Task> QueryOffersV2Async(Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) + public Task> QueryItemsAsync(string databaseId, string collectionId, Expression> predicate = null, + ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (predicate == null) predicate = x => true; - var offers = await DocumentClient.CreateOfferQuery(feedOptions).Where(predicate).ToListAsync(cancellationToken); - return offers.Cast(); + throw new NotImplementedException(); } - public async Task> UpdateOfferAsync(Offer offer) + public Task> QueryItemsAsync(string databaseId, string collectionId, string sql, object parameters = null, + ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReplaceOfferAsync(offer), offer.Id).ExecuteCosmosCommand(); + throw new NotImplementedException(); } - public async Task> QueryStoredProceduresAsync(string databaseId, string collectionId, Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default) + public Task> QueryItemsAsync(string databaseId, string collectionId, string sql, IDictionary parameters, + ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - if (predicate == null) predicate = x => true; - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await DocumentClient.CreateStoredProcedureQuery(collectionUri, feedOptions).Where(predicate).ToListAsync(cancellationToken); + throw new NotImplementedException(); } - public async Task GetStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, RequestOptions requestOptions = null) + public Task GetItemAsync(string databaseId, string collectionId, string documentId, + ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class { - var storedProcedureUri = UriFactory.CreateStoredProcedureUri(databaseId, collectionId, storedProcedureId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReadStoredProcedureAsync(storedProcedureUri, requestOptions), storedProcedureId) - .ExecuteCosmosQuery(); + throw new NotImplementedException(); } - public IQueryable Query(string databaseId, string collectionId, FeedOptions feedOptions = null) + public Task GetContainerAsync(string databaseId, string containerId, ContainerRequestOptions requestOptions = null) { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - var queryable = DocumentClient.CreateDocumentQuery(collectionUri, feedOptions); - return queryable; + throw new NotImplementedException(); } - public IQueryable Query(string databaseId, string collectionId, string sql, object parameters = null, FeedOptions feedOptions = null) + public IQueryable Query(string databaseId, string collectionId, ItemRequestOptions requestOptions = null) { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - var sqlParameters = parameters.ConvertToSqlParameterCollection(); - return GetSqlBasedQueryableForType(collectionUri, sql, sqlParameters, feedOptions); + throw new NotImplementedException(); } - public IQueryable Query(string databaseId, string collectionId, string sql, IDictionary parameters, FeedOptions feedOptions = null) + public IQueryable Query(string databaseId, string collectionId, string sql, object parameters = null, + ItemRequestOptions requestOptions = null) { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - var sqlParameters = parameters.ConvertDictionaryToSqlParameterCollection(); - return GetSqlBasedQueryableForType(collectionUri, sql, sqlParameters, feedOptions); + throw new NotImplementedException(); } - - public async Task> CreateCollectionAsync(string databaseId, DocumentCollection collection, - RequestOptions requestOptions = null) - { - var databaseUri = UriFactory.CreateDatabaseUri(databaseId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.CreateDocumentCollectionAsync(databaseUri, collection, requestOptions), collection.ToString()) - .ExecuteCosmosCommand(); - } - - public async Task> CreateDatabaseAsync(Database database, RequestOptions requestOptions = null) - { - return await this.InvokeCosmosOperationAsync(() => DocumentClient.CreateDatabaseAsync(database, requestOptions), database.ToString()) - .ExecuteCosmosCommand(); - } - - public async Task> CreateDocumentAsync(string databaseId, - string collectionId, Document obj, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.CreateDocumentAsync(collectionUri, obj, requestOptions, cancellationToken: cancellationToken), obj.GetDocumentId()) - .ExecuteCosmosCommand(); - } - - public async Task> CreateDocumentAsync(string databaseId, string collectionId, T obj, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class - { - var safeDocument = obj.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? _serializerSettings); - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.CreateDocumentAsync(collectionUri, safeDocument, requestOptions, cancellationToken: cancellationToken), obj.GetDocumentId()) - .ExecuteCosmosCommand(obj); - } - - public async Task> DeleteDocumentAsync(string databaseId, string collectionId, string documentId, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var documentUri = UriFactory.CreateDocumentUri(databaseId, collectionId, documentId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.DeleteDocumentAsync(documentUri, requestOptions, cancellationToken), documentId) - .ExecuteCosmosCommand(); - } - - public async Task> UpdateDocumentAsync(string databaseId, string collectionId, Document document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var documentUri = UriFactory.CreateDocumentUri(databaseId, collectionId, document.Id); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.ReplaceDocumentAsync(documentUri, document, requestOptions, cancellationToken), document.GetDocumentId()) - .ExecuteCosmosCommand(); - } - - public async Task> UpdateDocumentAsync(string databaseId, string collectionId, T document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class - { - var safeDocument = document.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? _serializerSettings); - var documentUri = UriFactory.CreateDocumentUri(databaseId, collectionId, safeDocument.Id); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.ReplaceDocumentAsync(documentUri, safeDocument, requestOptions, cancellationToken), document.GetDocumentId()) - .ExecuteCosmosCommand(document); - } - - public async Task> UpsertDocumentAsync(string databaseId, string collectionId, Document document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.UpsertDocumentAsync(collectionUri, document, requestOptions, cancellationToken: cancellationToken), document.Id) - .ExecuteCosmosCommand(); - } - - public async Task> UpsertDocumentAsync(string databaseId, string collectionId, - T document, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class - { - var safeDocument = document.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? _serializerSettings); - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => - DocumentClient.UpsertDocumentAsync(collectionUri, safeDocument, requestOptions, cancellationToken: cancellationToken), document.GetDocumentId()) - .ExecuteCosmosCommand(document); - } - - public async Task> DeleteDatabaseAsync(string databaseId, RequestOptions requestOptions = null) - { - var databaseUri = UriFactory.CreateDatabaseUri(databaseId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.DeleteDatabaseAsync(databaseUri, requestOptions), databaseId) - .ExecuteCosmosCommand(); - } - - public async Task> DeleteCollectionAsync(string databaseId, string collectionId, RequestOptions requestOptions = null) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.DeleteDocumentCollectionAsync(collectionUri, requestOptions), collectionId) - .ExecuteCosmosCommand(); - } - - public async Task> UpdateCollectionAsync(string databaseId, string collectionId, DocumentCollection documentCollection, - RequestOptions requestOptions = null) - { - var collectionUri = UriFactory.CreateDocumentCollectionUri(databaseId, collectionId); - return await this.InvokeCosmosOperationAsync(() => DocumentClient.ReplaceDocumentCollectionAsync(collectionUri, documentCollection, requestOptions), collectionId) - .ExecuteCosmosCommand(); - } - - public async Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - params object[] procedureParams) - { - var storedProcedureUri = UriFactory.CreateStoredProcedureUri(databaseId, collectionId, storedProcedureId); - return await this.InvokeCosmosOperationAsync( - () => DocumentClient.ExecuteStoredProcedureAsync(storedProcedureUri, procedureParams), storedProcedureId); - } - - public async Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - RequestOptions requestOptions, params object[] procedureParams) - { - var storedProcedureUri = UriFactory.CreateStoredProcedureUri(databaseId, collectionId, storedProcedureId); - return await this.InvokeCosmosOperationAsync( - () => DocumentClient.ExecuteStoredProcedureAsync(storedProcedureUri, requestOptions, procedureParams), storedProcedureId); - } - - public async Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - RequestOptions requestOptions, CancellationToken cancellationToken, params object[] procedureParams) - { - var storedProcedureUri = UriFactory.CreateStoredProcedureUri(databaseId, collectionId, storedProcedureId); - return await this.InvokeCosmosOperationAsync( - () => DocumentClient.ExecuteStoredProcedureAsync(storedProcedureUri, requestOptions, cancellationToken, procedureParams), storedProcedureId); - } - - private IQueryable GetSqlBasedQueryableForType(Uri collectionUri, string sql, - SqlParameterCollection parameters, FeedOptions feedOptions) - { - var sqlQuerySpec = parameters != null && parameters.Any() ? new SqlQuerySpec(sql, parameters) : new SqlQuerySpec(sql); - var queryable = DocumentClient.CreateDocumentQuery(collectionUri, sqlQuerySpec, feedOptions); - return queryable; - } - - public IDocumentClient DocumentClient { get; } + + public CosmosClient CosmosClient { get; } } } \ No newline at end of file diff --git a/src/Cosmonaut/CosmosConstants.cs b/src/Cosmonaut/CosmosConstants.cs index 5d4da0f..608b578 100644 --- a/src/Cosmonaut/CosmosConstants.cs +++ b/src/Cosmonaut/CosmosConstants.cs @@ -1,4 +1,4 @@ -using Microsoft.Azure.Documents; +using Microsoft.Azure.Cosmos; namespace Cosmonaut { @@ -8,8 +8,7 @@ public class CosmosConstants public const int MinimumCosmosThroughput = 400; public const int DefaultMaximumUpscaleThroughput = 10000; public const int TooManyRequestsStatusCode = 429; - public static readonly IndexingPolicy DefaultIndexingPolicy = - new IndexingPolicy(new RangeIndex(DataType.Number, -1), new RangeIndex(DataType.String, -1), new SpatialIndex(DataType.Point)); + public static readonly IndexingPolicy DefaultIndexingPolicy = new IndexingPolicy(); public static readonly UniqueKeyPolicy DefaultUniqueKeyPolicy = new UniqueKeyPolicy(); } } \ No newline at end of file diff --git a/src/Cosmonaut/CosmosStore.cs b/src/Cosmonaut/CosmosStore.cs index 2f8ea9f..1442f5b 100644 --- a/src/Cosmonaut/CosmosStore.cs +++ b/src/Cosmonaut/CosmosStore.cs @@ -1,346 +1,334 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using Cosmonaut.Extensions; -using Cosmonaut.Factories; -using Cosmonaut.Response; -using Cosmonaut.Storage; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; - + namespace Cosmonaut { public sealed class CosmosStore : ICosmosStore where TEntity : class { - public bool IsShared { get; internal set; } - - public string CollectionName { get; private set; } - - public string DatabaseName { get; } - - public CosmosStoreSettings Settings { get; } - - public ICosmonautClient CosmonautClient { get; } - - private readonly IDatabaseCreator _databaseCreator; - private readonly ICollectionCreator _collectionCreator; - - public CosmosStore(CosmosStoreSettings settings) : this(settings, string.Empty) - { - } - - public CosmosStore(CosmosStoreSettings settings, string overriddenCollectionName) - { - Settings = settings ?? throw new ArgumentNullException(nameof(settings)); - DatabaseName = settings.DatabaseName; - var documentClient = DocumentClientFactory.CreateDocumentClient(settings); - CosmonautClient = new CosmonautClient(documentClient, Settings.InfiniteRetries); - if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName)); - _collectionCreator = new CosmosCollectionCreator(CosmonautClient); - _databaseCreator = new CosmosDatabaseCreator(CosmonautClient); - InitialiseCosmosStore(overriddenCollectionName); - } - - public CosmosStore(ICosmonautClient cosmonautClient, - string databaseName) : this(cosmonautClient, databaseName, string.Empty, - new CosmosDatabaseCreator(cosmonautClient), - new CosmosCollectionCreator(cosmonautClient)) - { - } - - public CosmosStore(ICosmonautClient cosmonautClient, - string databaseName, - string overriddenCollectionName) : this(cosmonautClient, - databaseName, - overriddenCollectionName, - new CosmosDatabaseCreator(cosmonautClient), - new CosmosCollectionCreator(cosmonautClient)) - { - } - - internal CosmosStore(ICosmonautClient cosmonautClient, - string databaseName, - string overriddenCollectionName, - IDatabaseCreator databaseCreator = null, - ICollectionCreator collectionCreator = null) - { - DatabaseName = databaseName; - CosmonautClient = cosmonautClient ?? throw new ArgumentNullException(nameof(cosmonautClient)); - Settings = new CosmosStoreSettings(databaseName, cosmonautClient.DocumentClient.ServiceEndpoint.ToString(), string.Empty, cosmonautClient.DocumentClient.ConnectionPolicy); - if (Settings.InfiniteRetries) - CosmonautClient.DocumentClient.SetupInfiniteRetries(); - if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName)); - _collectionCreator = collectionCreator ?? new CosmosCollectionCreator(CosmonautClient); - _databaseCreator = databaseCreator ?? new CosmosDatabaseCreator(CosmonautClient); - InitialiseCosmosStore(overriddenCollectionName); - } - - public IQueryable Query(FeedOptions feedOptions = null) - { - var queryable = - CosmonautClient.Query(DatabaseName, CollectionName, GetFeedOptionsForQuery(feedOptions)); - - return IsShared ? queryable.Where(ExpressionExtensions.SharedCollectionExpression()) : queryable; - } - - public IQueryable Query(string sql, object parameters = null, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - return CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - } - - public async Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.SingleOrDefaultAsync(cancellationToken); - } - - public async Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.SingleOrDefaultAsync(cancellationToken); - } - - public async Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.ToListAsync(cancellationToken); - } - - public async Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.ToListAsync(cancellationToken); - } - - public IQueryable Query(string sql, IDictionary parameters, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - return CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - } - - public async Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.SingleOrDefaultAsync(cancellationToken); - } - - public async Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.SingleOrDefaultAsync(cancellationToken); - } - - public async Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.ToListAsync(cancellationToken); - } - - public async Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) - { - var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); - var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); - return await queryable.ToListAsync(cancellationToken); - } - - public async Task> AddAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - return await CosmonautClient.CreateDocumentAsync(DatabaseName, CollectionName, entity, - GetRequestOptions(requestOptions, entity), cancellationToken); - } - - public async Task> AddRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) - { - return await ExecuteMultiOperationAsync(entities, x => AddAsync(x, requestOptions?.Invoke(x), cancellationToken)); - } - - public async Task> RemoveAsync( - Expression> predicate, - FeedOptions feedOptions = null, - Func requestOptions = null, - CancellationToken cancellationToken = default) - { - var entitiesToRemove = await Query(GetFeedOptionsForQuery(feedOptions)).Where(predicate).ToListAsync(cancellationToken); - return await RemoveRangeAsync(entitiesToRemove, requestOptions, cancellationToken); - } - - public async Task> RemoveAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - entity.ValidateEntityForCosmosDb(); - var documentId = entity.GetDocumentId(); - return await CosmonautClient.DeleteDocumentAsync(DatabaseName, CollectionName, documentId, - GetRequestOptions(requestOptions, entity), cancellationToken).ExecuteCosmosCommand(entity); - } - - public async Task> RemoveRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) - { - return await ExecuteMultiOperationAsync(entities, x => RemoveAsync(x, requestOptions?.Invoke(x), cancellationToken)); - } - - public async Task> UpdateAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - entity.ValidateEntityForCosmosDb(); - requestOptions = GetRequestOptions(requestOptions, entity); - var document = entity.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); - return await CosmonautClient.UpdateDocumentAsync(DatabaseName, CollectionName, document, - requestOptions, cancellationToken).ExecuteCosmosCommand(entity); - } - - public async Task> UpdateRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) - { - return await ExecuteMultiOperationAsync(entities, x => UpdateAsync(x, requestOptions?.Invoke(x), cancellationToken)); - } - - public async Task> UpsertAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - requestOptions = GetRequestOptions(requestOptions, entity); - var document = entity.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); - return await CosmonautClient.UpsertDocumentAsync(DatabaseName, CollectionName, document, - requestOptions, cancellationToken).ExecuteCosmosCommand(entity); - } - - public async Task> UpsertRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) - { - return await ExecuteMultiOperationAsync(entities, x => UpsertAsync(x, requestOptions?.Invoke(x), cancellationToken)); - } - - public async Task> RemoveByIdAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - var response = await CosmonautClient.DeleteDocumentAsync(DatabaseName, CollectionName, id, - GetRequestOptions(id, requestOptions), cancellationToken); - return new CosmosResponse(response); - } - - public async Task> RemoveByIdAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default) - { - var requestOptions = partitionKeyValue != null - ? new RequestOptions { PartitionKey = new PartitionKey(partitionKeyValue) } - : null; - - return await RemoveByIdAsync(id, requestOptions, cancellationToken); - } - - public async Task FindAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) - { - return await CosmonautClient.GetDocumentAsync(DatabaseName, CollectionName, id, - GetRequestOptions(id, requestOptions), cancellationToken); - } - - public async Task FindAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default) - { - var requestOptions = partitionKeyValue != null - ? new RequestOptions { PartitionKey = new PartitionKey(partitionKeyValue) } - : null; - return await FindAsync(id, requestOptions, cancellationToken); - } - - public async Task EnsureInfrastructureProvisionedAsync() - { - var databaseCreated = - await _databaseCreator.EnsureCreatedAsync(DatabaseName, Settings.DefaultDatabaseThroughput); - var collectionCreated = await _collectionCreator.EnsureCreatedAsync(DatabaseName, CollectionName, - Settings.DefaultCollectionThroughput, Settings.JsonSerializerSettings, Settings.IndexingPolicy, Settings.OnDatabaseThroughput, Settings.UniqueKeyPolicy); - - return databaseCreated && collectionCreated; - } - - private void InitialiseCosmosStore(string overridenCollectionName) - { - IsShared = typeof(TEntity).UsesSharedCollection(); - CollectionName = GetCosmosStoreCollectionName(overridenCollectionName); - - if (Settings.ProvisionInfrastructureIfMissing) - { - EnsureInfrastructureProvisionedAsync().GetAwaiter().GetResult(); - } - - Settings.JsonSerializerSettings = CosmonautClient.DocumentClient.GetJsonSerializerSettingsFromClient(); - } - - private string GetCosmosStoreCollectionName(string overridenCollectionName) - { - var hasOverridenName = !string.IsNullOrEmpty(overridenCollectionName); - return IsShared - ? $"{Settings.CollectionPrefix ?? string.Empty}{(hasOverridenName ? overridenCollectionName : typeof(TEntity).GetSharedCollectionName())}" - : $"{Settings.CollectionPrefix ?? string.Empty}{(hasOverridenName ? overridenCollectionName : typeof(TEntity).GetCollectionName())}"; - } - - private async Task> ExecuteMultiOperationAsync(IEnumerable entities, - Func>> operationFunc) - { - var multipleResponse = new CosmosMultipleResponse(); - - var entitiesList = entities.ToList(); - if (!entitiesList.Any()) - return multipleResponse; - - var results = (await entitiesList.Select(operationFunc).WhenAllTasksAsync()).ToList(); - multipleResponse.SuccessfulEntities.AddRange(results.Where(x => x.IsSuccess)); - multipleResponse.FailedEntities.AddRange(results.Where(x => !x.IsSuccess)); - return multipleResponse; - } - - private RequestOptions GetRequestOptions(RequestOptions requestOptions, TEntity entity) - { - var partitionKeyValue = entity.GetPartitionKeyValueForEntity(); - if (requestOptions == null) - { - return partitionKeyValue != null ? new RequestOptions - { - PartitionKey = partitionKeyValue - } : null; - } - - requestOptions.PartitionKey = partitionKeyValue; - return requestOptions; - } - - private RequestOptions GetRequestOptions(string id, RequestOptions requestOptions) - { - var partitionKeyDefinition = typeof(TEntity).GetPartitionKeyDefinitionForEntity(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); - var partitionKeyIsId = partitionKeyDefinition?.Paths?.SingleOrDefault()?.Equals($"/{CosmosConstants.CosmosId}") ?? false; - if (requestOptions == null && partitionKeyIsId) - { - return new RequestOptions - { - PartitionKey = new PartitionKey(id) - }; - } - - if (requestOptions != null && partitionKeyIsId) - requestOptions.PartitionKey = new PartitionKey(id); - - return requestOptions; - } - - private FeedOptions GetFeedOptionsForQuery(FeedOptions feedOptions) - { - var shouldEnablePartitionQuery = (typeof(TEntity).HasPartitionKey() && feedOptions?.PartitionKey == null) - || (feedOptions != null && feedOptions.EnableCrossPartitionQuery); - - if (feedOptions == null) - { - return new FeedOptions - { - EnableCrossPartitionQuery = shouldEnablePartitionQuery - }; - } - - feedOptions.EnableCrossPartitionQuery = shouldEnablePartitionQuery; - return feedOptions; - } +// public bool IsShared { get; internal set; } +// +// public string CollectionName { get; private set; } +// +// public string DatabaseName { get; } +// +// public CosmosStoreSettings Settings { get; } +// +// public ICosmonautClient CosmonautClient { get; } +// +// private readonly IDatabaseCreator _databaseCreator; +// private readonly ICollectionCreator _collectionCreator; +// +// public CosmosStore(CosmosStoreSettings settings) : this(settings, string.Empty) +// { +// } +// +// public CosmosStore(CosmosStoreSettings settings, string overriddenCollectionName) +// { +// Settings = settings ?? throw new ArgumentNullException(nameof(settings)); +// DatabaseName = settings.DatabaseName; +// var documentClient = DocumentClientFactory.CreateDocumentClient(settings); +// CosmonautClient = new CosmonautClient(documentClient, Settings.InfiniteRetries); +// if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName)); +// _collectionCreator = new CosmosCollectionCreator(CosmonautClient); +// _databaseCreator = new CosmosDatabaseCreator(CosmonautClient); +// InitialiseCosmosStore(overriddenCollectionName); +// } +// +// public CosmosStore(ICosmonautClient cosmonautClient, +// string databaseName) : this(cosmonautClient, databaseName, string.Empty, +// new CosmosDatabaseCreator(cosmonautClient), +// new CosmosCollectionCreator(cosmonautClient)) +// { +// } +// +// public CosmosStore(ICosmonautClient cosmonautClient, +// string databaseName, +// string overriddenCollectionName) : this(cosmonautClient, +// databaseName, +// overriddenCollectionName, +// new CosmosDatabaseCreator(cosmonautClient), +// new CosmosCollectionCreator(cosmonautClient)) +// { +// } +// +// internal CosmosStore(ICosmonautClient cosmonautClient, +// string databaseName, +// string overriddenCollectionName, +// IDatabaseCreator databaseCreator = null, +// ICollectionCreator collectionCreator = null) +// { +// DatabaseName = databaseName; +// CosmonautClient = cosmonautClient ?? throw new ArgumentNullException(nameof(cosmonautClient)); +// Settings = new CosmosStoreSettings(databaseName, cosmonautClient.DocumentClient.ServiceEndpoint.ToString(), string.Empty, cosmonautClient.DocumentClient.ConnectionPolicy); +// if (Settings.InfiniteRetries) +// CosmonautClient.DocumentClient.SetupInfiniteRetries(); +// if (string.IsNullOrEmpty(Settings.DatabaseName)) throw new ArgumentNullException(nameof(Settings.DatabaseName)); +// _collectionCreator = collectionCreator ?? new CosmosCollectionCreator(CosmonautClient); +// _databaseCreator = databaseCreator ?? new CosmosDatabaseCreator(CosmonautClient); +// InitialiseCosmosStore(overriddenCollectionName); +// } +// +// public IQueryable Query(FeedOptions feedOptions = null) +// { +// var queryable = +// CosmonautClient.Query(DatabaseName, CollectionName, GetFeedOptionsForQuery(feedOptions)); +// +// return IsShared ? queryable.Where(ExpressionExtensions.SharedCollectionExpression()) : queryable; +// } +// +// public IQueryable Query(string sql, object parameters = null, FeedOptions feedOptions = null, +// CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// return CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// } +// +// public async Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.SingleOrDefaultAsync(cancellationToken); +// } +// +// public async Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.SingleOrDefaultAsync(cancellationToken); +// } +// +// public async Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.ToListAsync(cancellationToken); +// } +// +// public async Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.ToListAsync(cancellationToken); +// } +// +// public IQueryable Query(string sql, IDictionary parameters, FeedOptions feedOptions = null, +// CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// return CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// } +// +// public async Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.SingleOrDefaultAsync(cancellationToken); +// } +// +// public async Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.SingleOrDefaultAsync(cancellationToken); +// } +// +// public async Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.ToListAsync(cancellationToken); +// } +// +// public async Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default) +// { +// var collectionSharingFriendlySql = sql.EnsureQueryIsCollectionSharingFriendly(); +// var queryable = CosmonautClient.Query(DatabaseName, CollectionName, collectionSharingFriendlySql, parameters, GetFeedOptionsForQuery(feedOptions)); +// return await queryable.ToListAsync(cancellationToken); +// } +// +// public async Task> AddAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await CosmonautClient.CreateDocumentAsync(DatabaseName, CollectionName, entity, +// GetRequestOptions(requestOptions, entity), cancellationToken); +// } +// +// public async Task> AddRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await ExecuteMultiOperationAsync(entities, x => AddAsync(x, requestOptions?.Invoke(x), cancellationToken)); +// } +// +// public async Task> RemoveAsync( +// Expression> predicate, +// FeedOptions feedOptions = null, +// Func requestOptions = null, +// CancellationToken cancellationToken = default) +// { +// var entitiesToRemove = await Query(GetFeedOptionsForQuery(feedOptions)).Where(predicate).ToListAsync(cancellationToken); +// return await RemoveRangeAsync(entitiesToRemove, requestOptions, cancellationToken); +// } +// +// public async Task> RemoveAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// entity.ValidateEntityForCosmosDb(); +// var documentId = entity.GetDocumentId(); +// return await CosmonautClient.DeleteDocumentAsync(DatabaseName, CollectionName, documentId, +// GetRequestOptions(requestOptions, entity), cancellationToken).ExecuteCosmosCommand(entity); +// } +// +// public async Task> RemoveRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await ExecuteMultiOperationAsync(entities, x => RemoveAsync(x, requestOptions?.Invoke(x), cancellationToken)); +// } +// +// public async Task> UpdateAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// entity.ValidateEntityForCosmosDb(); +// requestOptions = GetRequestOptions(requestOptions, entity); +// var document = entity.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); +// return await CosmonautClient.UpdateDocumentAsync(DatabaseName, CollectionName, document, +// requestOptions, cancellationToken).ExecuteCosmosCommand(entity); +// } +// +// public async Task> UpdateRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await ExecuteMultiOperationAsync(entities, x => UpdateAsync(x, requestOptions?.Invoke(x), cancellationToken)); +// } +// +// public async Task> UpsertAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// requestOptions = GetRequestOptions(requestOptions, entity); +// var document = entity.ToCosmonautDocument(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); +// return await CosmonautClient.UpsertDocumentAsync(DatabaseName, CollectionName, document, +// requestOptions, cancellationToken).ExecuteCosmosCommand(entity); +// } +// +// public async Task> UpsertRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await ExecuteMultiOperationAsync(entities, x => UpsertAsync(x, requestOptions?.Invoke(x), cancellationToken)); +// } +// +// public async Task> RemoveByIdAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// var response = await CosmonautClient.DeleteDocumentAsync(DatabaseName, CollectionName, id, +// GetRequestOptions(id, requestOptions), cancellationToken); +// return new CosmosResponse(response); +// } +// +// public async Task> RemoveByIdAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default) +// { +// var requestOptions = partitionKeyValue != null +// ? new RequestOptions { PartitionKey = new PartitionKey(partitionKeyValue) } +// : null; +// +// return await RemoveByIdAsync(id, requestOptions, cancellationToken); +// } +// +// public async Task FindAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) +// { +// return await CosmonautClient.GetDocumentAsync(DatabaseName, CollectionName, id, +// GetRequestOptions(id, requestOptions), cancellationToken); +// } +// +// public async Task FindAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default) +// { +// var requestOptions = partitionKeyValue != null +// ? new RequestOptions { PartitionKey = new PartitionKey(partitionKeyValue) } +// : null; +// return await FindAsync(id, requestOptions, cancellationToken); +// } +// +// public async Task EnsureInfrastructureProvisionedAsync() +// { +// var databaseCreated = +// await _databaseCreator.EnsureCreatedAsync(DatabaseName, Settings.DefaultDatabaseThroughput); +// var collectionCreated = await _collectionCreator.EnsureCreatedAsync(DatabaseName, CollectionName, +// Settings.DefaultCollectionThroughput, Settings.JsonSerializerSettings, Settings.IndexingPolicy, Settings.OnDatabaseThroughput, Settings.UniqueKeyPolicy); +// +// return databaseCreated && collectionCreated; +// } +// +// private void InitialiseCosmosStore(string overridenCollectionName) +// { +// IsShared = typeof(TEntity).UsesSharedCollection(); +// CollectionName = GetCosmosStoreCollectionName(overridenCollectionName); +// +// if (Settings.ProvisionInfrastructureIfMissing) +// { +// EnsureInfrastructureProvisionedAsync().GetAwaiter().GetResult(); +// } +// +// Settings.JsonSerializerSettings = CosmonautClient.DocumentClient.GetJsonSerializerSettingsFromClient(); +// } +// +// private string GetCosmosStoreCollectionName(string overridenCollectionName) +// { +// var hasOverridenName = !string.IsNullOrEmpty(overridenCollectionName); +// return IsShared +// ? $"{Settings.CollectionPrefix ?? string.Empty}{(hasOverridenName ? overridenCollectionName : typeof(TEntity).GetSharedCollectionName())}" +// : $"{Settings.CollectionPrefix ?? string.Empty}{(hasOverridenName ? overridenCollectionName : typeof(TEntity).GetCollectionName())}"; +// } +// +// private async Task> ExecuteMultiOperationAsync(IEnumerable entities, +// Func>> operationFunc) +// { +// var multipleResponse = new CosmosMultipleResponse(); +// +// var entitiesList = entities.ToList(); +// if (!entitiesList.Any()) +// return multipleResponse; +// +// var results = (await entitiesList.Select(operationFunc).WhenAllTasksAsync()).ToList(); +// multipleResponse.SuccessfulEntities.AddRange(results.Where(x => x.IsSuccess)); +// multipleResponse.FailedEntities.AddRange(results.Where(x => !x.IsSuccess)); +// return multipleResponse; +// } +// +// private RequestOptions GetRequestOptions(RequestOptions requestOptions, TEntity entity) +// { +// var partitionKeyValue = entity.GetPartitionKeyValueForEntity(); +// if (requestOptions == null) +// { +// return partitionKeyValue != null ? new RequestOptions +// { +// PartitionKey = partitionKeyValue +// } : null; +// } +// +// requestOptions.PartitionKey = partitionKeyValue; +// return requestOptions; +// } +// +// private RequestOptions GetRequestOptions(string id, RequestOptions requestOptions) +// { +// var partitionKeyDefinition = typeof(TEntity).GetPartitionKeyDefinitionForEntity(requestOptions?.JsonSerializerSettings ?? Settings.JsonSerializerSettings); +// var partitionKeyIsId = partitionKeyDefinition?.Paths?.SingleOrDefault()?.Equals($"/{CosmosConstants.CosmosId}") ?? false; +// if (requestOptions == null && partitionKeyIsId) +// { +// return new RequestOptions +// { +// PartitionKey = new PartitionKey(id) +// }; +// } +// +// if (requestOptions != null && partitionKeyIsId) +// requestOptions.PartitionKey = new PartitionKey(id); +// +// return requestOptions; +// } +// +// private FeedOptions GetFeedOptionsForQuery(FeedOptions feedOptions) +// { +// var shouldEnablePartitionQuery = (typeof(TEntity).HasPartitionKey() && feedOptions?.PartitionKey == null) +// || (feedOptions != null && feedOptions.EnableCrossPartitionQuery); +// +// if (feedOptions == null) +// { +// return new FeedOptions +// { +// EnableCrossPartitionQuery = shouldEnablePartitionQuery +// }; +// } +// +// feedOptions.EnableCrossPartitionQuery = shouldEnablePartitionQuery; +// return feedOptions; +// } } } \ No newline at end of file diff --git a/src/Cosmonaut/CosmosStoreSettings.cs b/src/Cosmonaut/CosmosStoreSettings.cs index e6178d1..0e1ca12 100644 --- a/src/Cosmonaut/CosmosStoreSettings.cs +++ b/src/Cosmonaut/CosmosStoreSettings.cs @@ -1,7 +1,6 @@ using System; using Cosmonaut.Configuration; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Cosmos; using Newtonsoft.Json; namespace Cosmonaut @@ -14,7 +13,7 @@ public class CosmosStoreSettings public Uri EndpointUrl { get; } - public ConnectionPolicy ConnectionPolicy { get; set; } + public ConnectionMode ConnectionMode { get; set; } public ConsistencyLevel? ConsistencyLevel { get; set; } = null; @@ -28,7 +27,7 @@ public class CosmosStoreSettings public ThroughputBehaviour OnDatabaseThroughput { get; set; } = ThroughputBehaviour.UseDatabaseThroughput; - public JsonSerializerSettings JsonSerializerSettings { get; set; } + public CosmosSerializer CosmosSerializer { get; set; } public bool InfiniteRetries { get; set; } = true; @@ -58,13 +57,13 @@ public CosmosStoreSettings( string databaseName, string endpointUrl, string authKey, - ConnectionPolicy connectionPolicy = null, + ConnectionMode connectionMode = ConnectionMode.Direct, IndexingPolicy indexingPolicy = null, int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput) : this(databaseName, new Uri(endpointUrl), authKey, - connectionPolicy, + connectionMode, indexingPolicy, defaultCollectionThroughput) { @@ -74,14 +73,14 @@ public CosmosStoreSettings( string databaseName, Uri endpointUrl, string authKey, - ConnectionPolicy connectionPolicy = null, + ConnectionMode connectionMode = ConnectionMode.Direct, IndexingPolicy indexingPolicy = null, int defaultCollectionThroughput = CosmosConstants.MinimumCosmosThroughput) { DatabaseName = databaseName ?? throw new ArgumentNullException(nameof(databaseName)); EndpointUrl = endpointUrl ?? throw new ArgumentNullException(nameof(endpointUrl)); AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey)); - ConnectionPolicy = connectionPolicy; + ConnectionMode = connectionMode; DefaultCollectionThroughput = defaultCollectionThroughput; IndexingPolicy = indexingPolicy ?? CosmosConstants.DefaultIndexingPolicy; diff --git a/src/Cosmonaut/Diagnostics/CosmosEventCall.cs b/src/Cosmonaut/Diagnostics/CosmosEventCall.cs index 4a2261d..3dc136f 100644 --- a/src/Cosmonaut/Diagnostics/CosmosEventCall.cs +++ b/src/Cosmonaut/Diagnostics/CosmosEventCall.cs @@ -5,8 +5,7 @@ using System.Net; using System.Reflection; using System.Threading.Tasks; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Cosmos; using Newtonsoft.Json; namespace Cosmonaut.Diagnostics @@ -78,8 +77,8 @@ internal async Task> InvokeAsync(Func> InvokeAsync(Func> InvokeAsync(Func>> eventCall) + internal async Task> InvokeAsync(Func>> eventCall) { if (!CosmosEventSource.EventSource.IsEnabled()) { @@ -105,7 +104,7 @@ internal async Task> InvokeAsync(Func> InvokeAsync(Func> InvokeAsync(Func>> eventCall) where TEntity : Resource, new() + internal async Task> InvokeAsync(Func>> eventCall) { if (!CosmosEventSource.EventSource.IsEnabled()) { @@ -132,33 +131,7 @@ internal async Task> InvokeAsync(Func> InvokeAsync(Func>> eventCall) - { - if (!CosmosEventSource.EventSource.IsEnabled()) - { - return await eventCall(); - } - - var timer = new Stopwatch(); - try - { - SetPreExecutionEventMetadata(eventCall); - timer.Start(); - var result = await eventCall(); - timer.Stop(); - AddEventMetadataFromHeaders(result.ResponseHeaders); + AddEventMetadataFromHeaders(result.Headers); TrackSuccess(timer, HttpStatusCode.OK.ToString("D")); return result; } @@ -171,13 +144,13 @@ internal async Task> InvokeAsync(Func< } } - private void LogQueryMetricsIfPresent(FeedResponse result) - { - if (result.QueryMetrics == null) - return; - - EventMetadata.Properties[nameof(result.QueryMetrics)] = JsonConvert.SerializeObject(result.QueryMetrics); - } +// private void LogQueryMetricsIfPresent(FeedResponse result) +// { +// if (result.QueryMetrics == null) +// return; +// +// EventMetadata.Properties[nameof(result.QueryMetrics)] = JsonConvert.SerializeObject(result.QueryMetrics); +// } private void SetPreExecutionEventMetadata(Func> eventCall) { @@ -228,17 +201,17 @@ private void TrackSuccess(Stopwatch timer, string resultCode) private void AddEventMetadataFromException(Exception ex) { - if (!(ex is DocumentClientException documentClientException)) + if (!(ex is CosmosException cosmosException)) { EventMetadata.ResultCode = HttpStatusCode.InternalServerError.ToString("D"); return; } - AddEventMetadataFromHeaders(documentClientException.ResponseHeaders); - EventMetadata.ResultCode = documentClientException.StatusCode?.ToString("D") ?? HttpStatusCode.InternalServerError.ToString("D"); + AddEventMetadataFromHeaders(cosmosException.Headers); + EventMetadata.ResultCode = cosmosException.StatusCode.ToString("D"); } - private void AddEventMetadataFromHeaders(NameValueCollection headers) + private void AddEventMetadataFromHeaders(Headers headers) { if (headers == null) return; diff --git a/src/Cosmonaut/Diagnostics/CosmosEventExtensions.cs b/src/Cosmonaut/Diagnostics/CosmosEventExtensions.cs index 98a5c25..425d2aa 100644 --- a/src/Cosmonaut/Diagnostics/CosmosEventExtensions.cs +++ b/src/Cosmonaut/Diagnostics/CosmosEventExtensions.cs @@ -3,8 +3,7 @@ using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Diagnostics { @@ -21,31 +20,9 @@ internal static Task InvokeCosmosCallAsync( return CreateCosmosEventCall(invoker, data, properties, target, name).InvokeAsync(eventCall); } - internal static Task> InvokeCosmosOperationAsync( + internal static Task> InvokeCosmosOperationAsync( this object invoker, - Func>> eventCall, - string data, - Dictionary properties = null, - string target = null, - [CallerMemberName]string name = null) where TResource : Resource, new() - { - return CreateCosmosEventCall(invoker, data, properties, target, name).InvokeAsync(eventCall); - } - - internal static Task> InvokeCosmosOperationAsync( - this object invoker, - Func>> eventCall, - string data, - Dictionary properties = null, - string target = null, - [CallerMemberName]string name = null) - { - return CreateCosmosEventCall(invoker, data, properties, target, name).InvokeAsync(eventCall); - } - - internal static Task> InvokeCosmosOperationAsync( - this object invoker, - Func>> eventCall, + Func>> eventCall, string data, Dictionary properties = null, string target = null, diff --git a/src/Cosmonaut/Exceptions/CosmosCollectionThroughputUpdateException.cs b/src/Cosmonaut/Exceptions/CosmosCollectionThroughputUpdateException.cs index 41e7ff0..3acb8f9 100644 --- a/src/Cosmonaut/Exceptions/CosmosCollectionThroughputUpdateException.cs +++ b/src/Cosmonaut/Exceptions/CosmosCollectionThroughputUpdateException.cs @@ -1,11 +1,11 @@ using System; -using Microsoft.Azure.Documents; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Exceptions { public class CosmosCollectionThroughputUpdateException : Exception { - public CosmosCollectionThroughputUpdateException(DocumentCollection collection) : base($"Failed to update hroughput of collection {collection.Id}") + public CosmosCollectionThroughputUpdateException(Container collection) : base($"Failed to update hroughput of collection {collection.Id}") { } diff --git a/src/Cosmonaut/Extensions/CosmonautHelpers.cs b/src/Cosmonaut/Extensions/CosmonautHelpers.cs index 5a5ee1d..9306d15 100644 --- a/src/Cosmonaut/Extensions/CosmonautHelpers.cs +++ b/src/Cosmonaut/Extensions/CosmonautHelpers.cs @@ -1,4 +1,4 @@ -using Microsoft.Azure.Documents; +using Cosmonaut.Internal; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -6,37 +6,28 @@ namespace Cosmonaut.Extensions { public static class CosmonautHelpers { - public static Document ToCosmonautDocument(this TEntity obj, JsonSerializerSettings settings) where TEntity : class + public static CosmosDocument ToCosmonautDocument(this TEntity obj, JsonSerializerSettings settings) where TEntity : class { obj.ValidateEntityForCosmosDb(); var document = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj, settings), settings); - using (JsonReader reader = new JTokenReader(document)) - { - var actualDocument = new Document(); - actualDocument.LoadFrom(reader); - actualDocument.Id = obj.GetDocumentId(); - RemoveDuplicateIds(ref actualDocument); - - if (typeof(TEntity).UsesSharedCollection()) - actualDocument.SetPropertyValue(nameof(ISharedCosmosEntity.CosmosEntityName), $"{typeof(TEntity).GetSharedCollectionEntityName()}"); - - return actualDocument; - } +// var actualDocument = new CosmosDocument(document) {Id = obj.GetDocumentId()}; +// +// RemoveDuplicateIds(ref actualDocument); +// +// if (typeof(TEntity).UsesSharedCollection()) +// actualDocument.SetPropertyValue(nameof(ISharedCosmosEntity.CosmosEntityName), $"{typeof(TEntity).GetSharedCollectionEntityName()}"); +// +// return actualDocument; + return new CosmosDocument(null); } - internal static PartitionKeyDefinition GetPartitionKeyDefinition(string partitionKeyName) + internal static string GetPartitionKeyDefinition(string partitionKeyName) { - return new PartitionKeyDefinition - { - Paths = - { - $"/{partitionKeyName}" - } - }; + return $"/{partitionKeyName}"; } - internal static void RemoveDuplicateIds(ref Document actualDocument) + internal static void RemoveDuplicateIds(ref CosmosDocument actualDocument) { actualDocument.SetPropertyValue("Id", null); actualDocument.SetPropertyValue("ID", null); diff --git a/src/Cosmonaut/Extensions/CosmosClientExtensions.cs b/src/Cosmonaut/Extensions/CosmosClientExtensions.cs new file mode 100644 index 0000000..5e3af0b --- /dev/null +++ b/src/Cosmonaut/Extensions/CosmosClientExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Azure.Cosmos; + +namespace Cosmonaut.Extensions +{ + public static class CosmosClientExtensions + { + public static void SetupInfiniteRetries(this CosmosClient cosmosClient) + { + cosmosClient.ClientOptions.MaxRetryAttemptsOnRateLimitedRequests = int.MaxValue; + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Extensions/CosmosOperationExtensions.cs b/src/Cosmonaut/Extensions/CosmosOperationExtensions.cs deleted file mode 100644 index ae48515..0000000 --- a/src/Cosmonaut/Extensions/CosmosOperationExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Threading.Tasks; -using Cosmonaut.Response; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; - -namespace Cosmonaut.Extensions -{ - internal static class CosmosOperationExtensions - { - internal static async Task ExecuteCosmosQuery(this Task> operationTask) where TResult : Resource, new() - { - try - { - var response = await operationTask; - return response?.Resource; - } - catch (DocumentClientException exception) - { - var cosmosResponse = exception.ToCosmosResponse(); - - if (cosmosResponse.CosmosOperationStatus == CosmosOperationStatus.ResourceNotFound) - return null; - - throw; - } - } - - internal static async Task ExecuteCosmosQuery(this Task> operationTask) where TResult : class - { - try - { - var response = await operationTask; - return response?.Document; - } - catch (DocumentClientException exception) - { - var cosmosResponse = exception.ToCosmosResponse(); - - if (cosmosResponse.CosmosOperationStatus == CosmosOperationStatus.ResourceNotFound) - return null; - - throw; - } - } - - internal static async Task> ExecuteCosmosCommand(this Task> operationTask) where TResult : Resource, new() - { - try - { - var response = await operationTask; - return response; - } - catch (DocumentClientException exception) - { - var cosmosResponse = exception.ToCosmosResponse(); - - if (cosmosResponse.CosmosOperationStatus == CosmosOperationStatus.ResourceNotFound) - return null; - - throw; - } - } - - internal static async Task> ExecuteCosmosCommand(this Task> operationTask, TEntity entity) where TEntity : class - { - try - { - var response = await operationTask; - return response == null ? new CosmosResponse(entity, null, CosmosOperationStatus.ResourceNotFound) : new CosmosResponse(entity, response); - } - catch (DocumentClientException exception) - { - var cosmosResponse = exception.ToCosmosResponse(entity); - - switch (cosmosResponse.CosmosOperationStatus) - { - case CosmosOperationStatus.ResourceNotFound: - case CosmosOperationStatus.PreconditionFailed: - case CosmosOperationStatus.Conflict: - return cosmosResponse; - default: - throw; - } - } - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Extensions/CosmosResultExtensions.cs b/src/Cosmonaut/Extensions/CosmosResultExtensions.cs index 568881c..f953cbe 100644 --- a/src/Cosmonaut/Extensions/CosmosResultExtensions.cs +++ b/src/Cosmonaut/Extensions/CosmosResultExtensions.cs @@ -7,185 +7,184 @@ using Cosmonaut.Diagnostics; using Cosmonaut.Internal; using Cosmonaut.Response; -using Microsoft.Azure.Documents.Client; -using Microsoft.Azure.Documents.Linq; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Extensions { public static class CosmosResultExtensions { public static async Task> ToListAsync( - this IQueryable queryable, + this FeedIterator iterator, CancellationToken cancellationToken = default) { - return await GetListFromQueryable(queryable, cancellationToken); + //return await GetListFromQueryable(queryable, cancellationToken); + return await GetResultsFromQueryToList(iterator, cancellationToken); } - public static async Task> ToPagedListAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return await GetPagedListFromQueryable(queryable, cancellationToken); - } +// public static async Task> ToPagedListAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return await GetPagedListFromQueryable(queryable, cancellationToken); +// } - public static async Task CountAsync( + /*public static async Task CountAsync( this IQueryable queryable, CancellationToken cancellationToken = default) { return await queryable.InvokeCosmosCallAsync(() => DocumentQueryable.CountAsync(queryable, cancellationToken), queryable.ToString(), target: GetAltLocationFromQueryable(queryable)); - } - - public static async Task CountAsync( - this IQueryable queryable, - Expression> predicate, - CancellationToken cancellationToken = default) - { - var finalQueryable = queryable.Where(predicate); - return await CountAsync(finalQueryable, cancellationToken); - } - - public static async Task FirstOrDefaultAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).FirstOrDefault(); - } - - public static async Task FirstOrDefaultAsync( - this IQueryable queryable, - Expression> predicate, - CancellationToken cancellationToken = default) - { - var finalQueryable = queryable.Where(predicate); - return await finalQueryable.FirstOrDefaultAsync(cancellationToken); - } - - public static async Task FirstAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).First(); - } - - public static async Task FirstAsync( - this IQueryable queryable, - Expression> predicate, - CancellationToken cancellationToken = default) - { - var finalQueryable = queryable.Where(predicate); - return await finalQueryable.FirstAsync(cancellationToken); - } - - public static async Task SingleOrDefaultAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).SingleOrDefault(); - } - - public static async Task SingleOrDefaultAsync( - this IQueryable queryable, - Expression> predicate, - CancellationToken cancellationToken = default) - { - var finalQueryable = queryable.Where(predicate); - return await finalQueryable.SingleOrDefaultAsync(cancellationToken); - } - - public static async Task SingleAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).Single(); - } - - public static async Task SingleAsync( - this IQueryable queryable, - Expression> predicate, - CancellationToken cancellationToken = default) + }*/ + +// public static async Task CountAsync( +// this IQueryable queryable, +// Expression> predicate, +// CancellationToken cancellationToken = default) +// { +// var finalQueryable = queryable.Where(predicate); +// return await CountAsync(finalQueryable, cancellationToken); +// } + +// public static async Task FirstOrDefaultAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).FirstOrDefault(); +// } + +// public static async Task FirstOrDefaultAsync( +// this IQueryable queryable, +// Expression> predicate, +// CancellationToken cancellationToken = default) +// { +// var finalQueryable = queryable.Where(predicate); +// return await finalQueryable.FirstOrDefaultAsync(cancellationToken); +// } +// +// public static async Task FirstAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).First(); +// } +// +// public static async Task FirstAsync( +// this IQueryable queryable, +// Expression> predicate, +// CancellationToken cancellationToken = default) +// { +// var finalQueryable = queryable.Where(predicate); +// return await finalQueryable.FirstAsync(cancellationToken); +// } +// +// public static async Task SingleOrDefaultAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).SingleOrDefault(); +// } +// +// public static async Task SingleOrDefaultAsync( +// this IQueryable queryable, +// Expression> predicate, +// CancellationToken cancellationToken = default) +// { +// var finalQueryable = queryable.Where(predicate); +// return await finalQueryable.SingleOrDefaultAsync(cancellationToken); +// } +// +// public static async Task SingleAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return (await GetSingleOrFirstFromQueryable(queryable, cancellationToken)).Single(); +// } +// +// public static async Task SingleAsync( +// this IQueryable queryable, +// Expression> predicate, +// CancellationToken cancellationToken = default) +// { +// var finalQueryable = queryable.Where(predicate); +// return await finalQueryable.SingleAsync(cancellationToken); +// } + +// public static async Task MaxAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// Microsoft.Azure.Cosmos.Linq.CosmosLinqExtensions.ToQueryDefinition() +// return await queryable.InvokeCosmosCallAsync(() => DocumentQueryable.MaxAsync(queryable, cancellationToken), queryable.ToString(), target: GetAltLocationFromQueryable(queryable)); +// } +// +// public static async Task MinAsync( +// this IQueryable queryable, +// CancellationToken cancellationToken = default) +// { +// return await queryable.InvokeCosmosCallAsync(() => DocumentQueryable.MinAsync(queryable, cancellationToken), queryable.ToString(), target: GetAltLocationFromQueryable(queryable)); +// } + +// private static async Task> GetListFromQueryable(FeedIterator iterator, +// CancellationToken cancellationToken) +// { +// var feedOptions = iterator.GetFeedOptionsForQueryable(); +// if (feedOptions?.RequestContinuation == null) +// { +// return await GetResultsFromQueryToList(iterator, cancellationToken); +// } +// +// return await GetPaginatedResultsFromQueryable(iterator, cancellationToken, feedOptions); +// } +// +// private static async Task> GetSingleOrFirstFromQueryable(IQueryable queryable, +// CancellationToken cancellationToken) +// { +// SetFeedOptionsForSingleOperation(ref queryable, out var feedOptions); +// +// if (feedOptions?.RequestContinuation == null) +// { +// return await GetResultsFromQueryForSingleOrFirst(queryable, cancellationToken); +// } +// +// return await GetPaginatedResultsFromQueryable(queryable, cancellationToken, feedOptions); +// } +// +// private static void SetFeedOptionsForSingleOperation(ref IQueryable queryable, out FeedOptions feedOptions) +// { +// feedOptions = queryable.GetFeedOptionsForQueryable() ?? new FeedOptions(); +// feedOptions.MaxItemCount = 1; +// queryable.SetFeedOptionsForQueryable(feedOptions); +// } + +// private static async Task> GetPagedListFromQueryable(IQueryable queryable, +// CancellationToken cancellationToken) +// { +// var feedOptions = queryable.GetFeedOptionsForQueryable(); +// if (feedOptions?.RequestContinuation == null) +// return new CosmosPagedResults(await GetListFromQueryable(queryable, cancellationToken), feedOptions?.MaxItemCount ?? 0, +// string.Empty, queryable); +// +// return await GetPaginatedResultsFromQueryable(queryable, cancellationToken, feedOptions); +// } + + private static async Task> GetResultsFromQueryToList(FeedIterator iterator, CancellationToken cancellationToken) { - var finalQueryable = queryable.Where(predicate); - return await finalQueryable.SingleAsync(cancellationToken); - } - - public static async Task MaxAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return await queryable.InvokeCosmosCallAsync(() => DocumentQueryable.MaxAsync(queryable, cancellationToken), queryable.ToString(), target: GetAltLocationFromQueryable(queryable)); - } - - public static async Task MinAsync( - this IQueryable queryable, - CancellationToken cancellationToken = default) - { - return await queryable.InvokeCosmosCallAsync(() => DocumentQueryable.MinAsync(queryable, cancellationToken), queryable.ToString(), target: GetAltLocationFromQueryable(queryable)); - } - - private static async Task> GetListFromQueryable(IQueryable queryable, - CancellationToken cancellationToken) - { - var feedOptions = queryable.GetFeedOptionsForQueryable(); - if (feedOptions?.RequestContinuation == null) - { - return await GetResultsFromQueryToList(queryable, cancellationToken); - } - - return await GetPaginatedResultsFromQueryable(queryable, cancellationToken, feedOptions); - } - - private static async Task> GetSingleOrFirstFromQueryable(IQueryable queryable, - CancellationToken cancellationToken) - { - SetFeedOptionsForSingleOperation(ref queryable, out var feedOptions); - - if (feedOptions?.RequestContinuation == null) - { - return await GetResultsFromQueryForSingleOrFirst(queryable, cancellationToken); - } - - return await GetPaginatedResultsFromQueryable(queryable, cancellationToken, feedOptions); - } - - private static void SetFeedOptionsForSingleOperation(ref IQueryable queryable, out FeedOptions feedOptions) - { - feedOptions = queryable.GetFeedOptionsForQueryable() ?? new FeedOptions(); - feedOptions.MaxItemCount = 1; - queryable.SetFeedOptionsForQueryable(feedOptions); - } - - private static async Task> GetPagedListFromQueryable(IQueryable queryable, - CancellationToken cancellationToken) - { - var feedOptions = queryable.GetFeedOptionsForQueryable(); - if (feedOptions?.RequestContinuation == null) - return new CosmosPagedResults(await GetListFromQueryable(queryable, cancellationToken), feedOptions?.MaxItemCount ?? 0, - string.Empty, queryable); - - return await GetPaginatedResultsFromQueryable(queryable, cancellationToken, feedOptions); - } - - private static async Task> GetResultsFromQueryToList(IQueryable queryable, CancellationToken cancellationToken) - { - var query = queryable.AsDocumentQuery(); var results = new List(); - while (query.HasMoreResults) + while (iterator.HasMoreResults) { - var items = await query.InvokeExecuteNextAsync(() => query.ExecuteNextAsync(cancellationToken), - query.ToString(), target: GetAltLocationFromQueryable(queryable)); + var items = await iterator.InvokeExecuteNextAsync(() => iterator.ReadNextAsync(cancellationToken), + iterator.ToString(), target: string.Empty /*target: GetAltLocationFromQueryable(queryable)*/); results.AddRange(items); } return results; } - private static async Task> GetResultsFromQueryForSingleOrFirst(IQueryable queryable, CancellationToken cancellationToken) + private static async Task> GetResultsFromQueryForSingleOrFirst(FeedIterator iterator, CancellationToken cancellationToken) { - var query = queryable.AsDocumentQuery(); var results = new List(); - while (query.HasMoreResults) + while (iterator.HasMoreResults) { - var items = await query.InvokeExecuteNextAsync(() => query.ExecuteNextAsync(cancellationToken), - query.ToString(), target: GetAltLocationFromQueryable(queryable)); + var items = await iterator.InvokeExecuteNextAsync(() => iterator.ReadNextAsync(cancellationToken), + iterator.ToString(), target: GetAltLocationFromQueryable(iterator)); results.AddRange(items); if (results.Any()) return results; @@ -193,20 +192,19 @@ private static async Task> GetResultsFromQueryForSingleOrFirst(IQuery return results; } - private static async Task> GetSkipTakePagedResultsFromQueryToList(IQueryable queryable, int pageNumber, int pageSize, CancellationToken cancellationToken) + private static async Task> GetSkipTakePagedResultsFromQueryToList(FeedIterator iterator, int pageNumber, int pageSize, CancellationToken cancellationToken) { - var query = queryable.AsDocumentQuery(); var results = new List(); var documentsSkipped = 0; var nextPageToken = string.Empty; - while (query.HasMoreResults) + while (iterator.HasMoreResults) { if (results.Count == pageSize) break; - var items = await query.InvokeExecuteNextAsync(() => query.ExecuteNextAsync(cancellationToken), - query.ToString(), target: GetAltLocationFromQueryable(queryable)); - nextPageToken = items.ResponseContinuation; + var items = await iterator.InvokeExecuteNextAsync(() => iterator.ReadNextAsync(cancellationToken), + iterator.ToString(), target: GetAltLocationFromQueryable(iterator)); + nextPageToken = items.ContinuationToken; foreach (var item in items) { @@ -222,22 +220,21 @@ private static async Task> GetSkipTakePagedResultsFromQuer break; } } - return new CosmosPagedResults(results, pageSize, nextPageToken, queryable); + return new CosmosPagedResults(results, pageSize, nextPageToken, iterator); } - private static async Task> GetTokenPagedResultsFromQueryToList(IQueryable queryable, int pageSize, CancellationToken cancellationToken) + private static async Task> GetTokenPagedResultsFromQueryToList(FeedIterator iterator, int pageSize, CancellationToken cancellationToken) { - var query = queryable.AsDocumentQuery(); var results = new List(); var nextPageToken = string.Empty; - while (query.HasMoreResults) + while (iterator.HasMoreResults) { if (results.Count == pageSize) break; - var items = await query.InvokeExecuteNextAsync(() => query.ExecuteNextAsync(cancellationToken), - query.ToString(), target: GetAltLocationFromQueryable(queryable)); - nextPageToken = items.ResponseContinuation; + var items = await iterator.InvokeExecuteNextAsync(() => iterator.ReadNextAsync(cancellationToken), + iterator.ToString(), target: GetAltLocationFromQueryable(iterator)); + nextPageToken = items.ContinuationToken; foreach (var item in items) { @@ -247,28 +244,28 @@ private static async Task> GetTokenPagedResultsFromQueryTo break; } } - return new CosmosPagedResults(results, pageSize, nextPageToken, queryable); - } - - private static async Task> GetPaginatedResultsFromQueryable(IQueryable queryable, CancellationToken cancellationToken, - FeedOptions feedOptions) - { - var usesSkipTakePagination = - feedOptions.RequestContinuation.StartsWith(nameof(PaginationExtensions.WithPagination)); - - if (!usesSkipTakePagination) - return await GetTokenPagedResultsFromQueryToList(queryable, feedOptions.MaxItemCount ?? 0, - cancellationToken); - - var pageNumber = int.Parse(feedOptions.RequestContinuation.Replace( - $"{nameof(PaginationExtensions.WithPagination)}/", string.Empty)); - feedOptions.RequestContinuation = null; - queryable.SetFeedOptionsForQueryable(feedOptions); - return await GetSkipTakePagedResultsFromQueryToList(queryable, pageNumber, feedOptions.MaxItemCount ?? 0, - cancellationToken); + return new CosmosPagedResults(results, pageSize, nextPageToken, iterator); } - private static string GetAltLocationFromQueryable(IQueryable queryable) +// private static async Task> GetPaginatedResultsFromQueryable(IQueryable queryable, CancellationToken cancellationToken, +// FeedOptions feedOptions) +// { +// var usesSkipTakePagination = +// feedOptions.RequestContinuationToken.StartsWith(nameof(PaginationExtensions.WithPagination)); +// +// if (!usesSkipTakePagination) +// return await GetTokenPagedResultsFromQueryToList(queryable, feedOptions.MaxItemCount ?? 0, +// cancellationToken); +// +// var pageNumber = int.Parse(feedOptions.RequestContinuationToken.Replace( +// $"{nameof(PaginationExtensions.WithPagination)}/", string.Empty)); +// feedOptions.RequestContinuationToken = null; +// queryable.SetFeedOptionsForQueryable(feedOptions); +// return await GetSkipTakePagedResultsFromQueryToList(queryable, pageNumber, feedOptions.MaxItemCount ?? 0, +// cancellationToken); +// } + + private static string GetAltLocationFromQueryable(FeedIterator queryable) { if (!CosmosEventSource.EventSource.IsEnabled()) return null; @@ -276,7 +273,7 @@ private static string GetAltLocationFromQueryable(IQueryable queryable) if (!queryable.GetType().Name.Equals("DocumentQuery`1")) return null; - return InternalTypeCache.Instance.DocumentFeedOrDbLinkFieldInfo?.GetValue(queryable.Provider)?.ToString(); + return string.Empty;//InternalTypeCache.Instance.DocumentFeedOrDbLinkFieldInfo?.GetValue(queryable.Provider)?.ToString(); } } } \ No newline at end of file diff --git a/src/Cosmonaut/Extensions/CosmosSqlQueryExtensions.cs b/src/Cosmonaut/Extensions/CosmosSqlQueryExtensions.cs index fb14b80..49d50b9 100644 --- a/src/Cosmonaut/Extensions/CosmosSqlQueryExtensions.cs +++ b/src/Cosmonaut/Extensions/CosmosSqlQueryExtensions.cs @@ -4,7 +4,6 @@ using System.Text.RegularExpressions; using Cosmonaut.Exceptions; using Cosmonaut.Internal; -using Microsoft.Azure.Documents; namespace Cosmonaut.Extensions { @@ -46,40 +45,40 @@ internal static string EnsureQueryIsCollectionSharingFriendly(this stri return GetQueryWithExistingWhereClauseInjectedWithSharedCollection(sql, identifier, cosmosEntityNameValue); } - internal static SqlParameterCollection ConvertToSqlParameterCollection(this object obj) - { - var sqlParameterCollection = new SqlParameterCollection(); - - if (obj == null) - return sqlParameterCollection; - - foreach (var propertyInfo in InternalTypeCache.Instance.GetPropertiesFromCache(obj.GetType())) - { - var propertyName = propertyInfo.Name.StartsWith("@") ? propertyInfo.Name : $"@{propertyInfo.Name}"; - var propertyValue = propertyInfo.GetValue(obj); - var sqlparameter = new SqlParameter(propertyName, propertyValue); - sqlParameterCollection.Add(sqlparameter); - } - - return sqlParameterCollection; - } - - internal static SqlParameterCollection ConvertDictionaryToSqlParameterCollection(this IDictionary dictionary) - { - var sqlParameterCollection = new SqlParameterCollection(); - - if (dictionary == null) - return sqlParameterCollection; - - foreach (var pair in dictionary) - { - var key = pair.Key.StartsWith("@") ? pair.Key : $"@{pair.Key}"; - var sqlparameter = new SqlParameter(key, pair.Value); - sqlParameterCollection.Add(sqlparameter); - } - - return sqlParameterCollection; - } +// internal static SqlParameterCollection ConvertToSqlParameterCollection(this object obj) +// { +// var sqlParameterCollection = new SqlParameterCollection(); +// +// if (obj == null) +// return sqlParameterCollection; +// +// foreach (var propertyInfo in InternalTypeCache.Instance.GetPropertiesFromCache(obj.GetType())) +// { +// var propertyName = propertyInfo.Name.StartsWith("@") ? propertyInfo.Name : $"@{propertyInfo.Name}"; +// var propertyValue = propertyInfo.GetValue(obj); +// var sqlparameter = new SqlParameter(propertyName, propertyValue); +// sqlParameterCollection.Add(sqlparameter); +// } +// +// return sqlParameterCollection; +// } +// +// internal static SqlParameterCollection ConvertDictionaryToSqlParameterCollection(this IDictionary dictionary) +// { +// var sqlParameterCollection = new SqlParameterCollection(); +// +// if (dictionary == null) +// return sqlParameterCollection; +// +// foreach (var pair in dictionary) +// { +// var key = pair.Key.StartsWith("@") ? pair.Key : $"@{pair.Key}"; +// var sqlparameter = new SqlParameter(key, pair.Value); +// sqlParameterCollection.Add(sqlparameter); +// } +// +// return sqlParameterCollection; +// } private static string GetQueryWithExistingWhereClauseInjectedWithSharedCollection(string sql, string identifier, string cosmosEntityNameValue) diff --git a/src/Cosmonaut/Extensions/DocumentClientExtensions.cs b/src/Cosmonaut/Extensions/DocumentClientExtensions.cs deleted file mode 100644 index b7e5a6d..0000000 --- a/src/Cosmonaut/Extensions/DocumentClientExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; -using Newtonsoft.Json; - -namespace Cosmonaut.Extensions -{ - public static class DocumentClientExtensions - { - public static void SetupInfiniteRetries(this IDocumentClient documentClient) - { - if (documentClient.ConnectionPolicy == null) - return; - documentClient.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = int.MaxValue; - } - - internal static JsonSerializerSettings GetJsonSerializerSettingsFromClient(this IDocumentClient documentClient) - { - try - { - return (JsonSerializerSettings) typeof(DocumentClient).GetTypeInfo() - .GetField("serializerSettings", BindingFlags.NonPublic | BindingFlags.Instance) - .GetValue(documentClient); - } - catch - { - return null; - } - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Extensions/DocumentEntityExtensions.cs b/src/Cosmonaut/Extensions/DocumentEntityExtensions.cs index 897be7d..b64d1f0 100644 --- a/src/Cosmonaut/Extensions/DocumentEntityExtensions.cs +++ b/src/Cosmonaut/Extensions/DocumentEntityExtensions.cs @@ -5,7 +5,7 @@ using Cosmonaut.Attributes; using Cosmonaut.Exceptions; using Cosmonaut.Internal; -using Microsoft.Azure.Documents; +using Microsoft.Azure.Cosmos; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -13,7 +13,7 @@ namespace Cosmonaut.Extensions { public static class DocumentEntityExtensions { - internal static PartitionKeyDefinition GetPartitionKeyDefinitionForEntity(this Type type, JsonSerializerSettings serializerSettings) + internal static string GetPartitionKeyDefinitionForEntity(this Type type, JsonSerializerSettings serializerSettings) { var partitionKeyProperties = InternalTypeCache.Instance.GetPropertiesFromCache(type) .Where(x => x.GetCustomAttribute() != null).ToList(); @@ -44,16 +44,16 @@ internal static PartitionKeyDefinition GetPartitionKeyDefinitionForEntity(this T return CosmonautHelpers.GetPartitionKeyDefinition(partitionKeyProperty.Name); } - private static bool IsCosmosIdThePartitionKey(JsonPropertyAttribute porentialJsonPropertyAttribute, PropertyInfo partitionKeyProperty) + private static bool IsCosmosIdThePartitionKey(JsonPropertyAttribute potentialJsonPropertyAttribute, PropertyInfo partitionKeyProperty) { - return porentialJsonPropertyAttribute.HasJsonPropertyAttributeId() + return potentialJsonPropertyAttribute.HasJsonPropertyAttributeId() || partitionKeyProperty.Name.Equals(CosmosConstants.CosmosId, StringComparison.OrdinalIgnoreCase); } internal static PartitionKey GetPartitionKeyValueForEntity(this TEntity entity) where TEntity : class { var partitionKeyValue = entity.GetPartitionKeyValueAsStringForEntity(); - return !string.IsNullOrEmpty(partitionKeyValue) ? new PartitionKey(partitionKeyValue) : null; + return !string.IsNullOrEmpty(partitionKeyValue) ? new PartitionKey(partitionKeyValue) : PartitionKey.None; } internal static string GetPartitionKeyValueAsStringForEntity(this TEntity entity) where TEntity : class diff --git a/src/Cosmonaut/Extensions/ExceptionHandlingExtensions.cs b/src/Cosmonaut/Extensions/ExceptionHandlingExtensions.cs deleted file mode 100644 index b31521d..0000000 --- a/src/Cosmonaut/Extensions/ExceptionHandlingExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Net; -using Cosmonaut.Response; -using Microsoft.Azure.Documents; - -namespace Cosmonaut.Extensions -{ - public static class ExceptionHandlingExtensions - { - internal static CosmosResponse DocumentClientExceptionToCosmosResponse(DocumentClientException exception, TEntity entity) where TEntity : class - { - switch (exception.StatusCode) - { - case HttpStatusCode.NotFound: - return new CosmosResponse(entity, exception, CosmosOperationStatus.ResourceNotFound); - case (HttpStatusCode) CosmosConstants.TooManyRequestsStatusCode: - return new CosmosResponse(entity, exception, CosmosOperationStatus.RequestRateIsLarge); - case HttpStatusCode.PreconditionFailed: - return new CosmosResponse(entity, exception, CosmosOperationStatus.PreconditionFailed); - case HttpStatusCode.Conflict: - return new CosmosResponse(entity, exception, CosmosOperationStatus.Conflict); - } - - throw exception; - } - - internal static CosmosResponse ToCosmosResponse(this DocumentClientException exception) where TEntity : class - { - return ToCosmosResponse(exception, null); - } - - internal static CosmosResponse ToCosmosResponse(this DocumentClientException exception, TEntity entity) where TEntity : class - { - return DocumentClientExceptionToCosmosResponse(exception, entity); - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Extensions/PaginationExtensions.cs b/src/Cosmonaut/Extensions/PaginationExtensions.cs index de57cfb..19d8ec9 100644 --- a/src/Cosmonaut/Extensions/PaginationExtensions.cs +++ b/src/Cosmonaut/Extensions/PaginationExtensions.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Reflection; using Cosmonaut.Internal; -using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Extensions { @@ -16,7 +16,7 @@ public static class PaginationExtensions /// Read more at https://github.com/Elfocrash/Cosmonaut /// /// A specific page of the results that your query matches. - public static IQueryable WithPagination(this IQueryable queryable, int pageNumber, int pageSize) + public static FeedIterator WithPagination(this FeedIterator iterator, int pageNumber, int pageSize) { if (pageNumber <= 0) { @@ -28,7 +28,7 @@ public static IQueryable WithPagination(this IQueryable queryable, int throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size must be a positive number."); } - return GetQueryableWithPaginationSettings(queryable, $"{nameof(WithPagination)}/{pageNumber}", pageSize); + return GetQueryableWithPaginationSettings(iterator, $"{nameof(WithPagination)}/{pageNumber}", pageSize); } /// @@ -36,11 +36,11 @@ public static IQueryable WithPagination(this IQueryable queryable, int /// though all the documents to get to the page you want. Read more at https://github.com/Elfocrash/Cosmonaut /// /// /// - /// The DocumentQueryable for the operation + /// The DocumentQueryable for the operation /// When null or empty string, the first page of items will be returned /// The size of the page we are expecting /// A specific page of the results that your query matches. - public static IQueryable WithPagination(this IQueryable queryable, string continuationToken, int pageSize) + public static FeedIterator WithPagination(this FeedIterator iterator, string continuationToken, int pageSize) { if (pageSize <= 0) { @@ -48,38 +48,40 @@ public static IQueryable WithPagination(this IQueryable queryable, stri } if (continuationToken == null) - return GetQueryableWithPaginationSettings(queryable, $"{nameof(WithPagination)}/{1}", pageSize); + return GetQueryableWithPaginationSettings(iterator, $"{nameof(WithPagination)}/{1}", pageSize); - return GetQueryableWithPaginationSettings(queryable, continuationToken, pageSize); + return GetQueryableWithPaginationSettings(iterator, continuationToken, pageSize); } - private static IQueryable GetQueryableWithPaginationSettings(IQueryable queryable, string continuationInfo, int pageSize) - { - if (!queryable.GetType().Name.Equals(DocumentQueryTypeName)) - return queryable; - - var feedOptions = queryable.GetFeedOptionsForQueryable() ?? new FeedOptions(); - feedOptions.MaxItemCount = pageSize; - feedOptions.RequestContinuation = continuationInfo; - queryable.SetFeedOptionsForQueryable(feedOptions); - return queryable; + private static FeedIterator GetQueryableWithPaginationSettings(FeedIterator iterator, string continuationInfo, int pageSize) + { +// if (!iterator.GetType().Name.Equals(DocumentQueryTypeName)) +// return iterator; +// +// var feedOptions = iterator.GetFeedOptionsForQueryable() ?? new QueryRequestOptions(); +// feedOptions.MaxItemCount = pageSize; +// feedOptions.RequestContinuation = continuationInfo; + //iterator.SetFeedOptionsForQueryable(feedOptions); + return iterator; } - internal static FeedOptions GetFeedOptionsForQueryable(this IQueryable queryable) + internal static RequestOptions GetFeedOptionsForQueryable(this IQueryable queryable) { if (!queryable.GetType().Name.Equals(DocumentQueryTypeName)) return null; - return (FeedOptions) InternalTypeCache.Instance.FeedOptionsFieldInfo.GetValue(queryable.Provider); + //TODO see if i need this + return (RequestOptions) InternalTypeCache.Instance.FeedOptionsFieldInfo.GetValue(queryable.Provider); } - internal static void SetFeedOptionsForQueryable(this IQueryable queryable, FeedOptions feedOptions) + internal static void SetFeedOptionsForQueryable(this IQueryable queryable, RequestOptions requestOptions) { if (!queryable.GetType().Name.Equals(DocumentQueryTypeName)) return; - InternalTypeCache.Instance.GetFieldInfoFromCache(queryable.GetType(), "feedOptions", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(queryable, feedOptions); - InternalTypeCache.Instance.FeedOptionsFieldInfo.SetValue(queryable.Provider, feedOptions); + //InternalTypeCache.Instance.GetFieldInfoFromCache(queryable.GetType(), "feedOptions", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(queryable, feedOptions); + //InternalTypeCache.Instance.FeedOptionsFieldInfo.SetValue(queryable.Provider, feedOptions); + //TODO see if i need this } } } \ No newline at end of file diff --git a/src/Cosmonaut/Factories/CosmonautClientFactory.cs b/src/Cosmonaut/Factories/CosmonautClientFactory.cs index a4c088e..19c659e 100644 --- a/src/Cosmonaut/Factories/CosmonautClientFactory.cs +++ b/src/Cosmonaut/Factories/CosmonautClientFactory.cs @@ -4,7 +4,7 @@ public class CosmonautClientFactory { public static ICosmonautClient CreateCosmonautClient(CosmosStoreSettings settings) { - return new CosmonautClient(DocumentClientFactory.CreateDocumentClient(settings)); + return new CosmonautClient(CosmosClientFactory.CreateDocumentClient(settings)); } } } \ No newline at end of file diff --git a/src/Cosmonaut/Factories/CosmosClientFactory.cs b/src/Cosmonaut/Factories/CosmosClientFactory.cs new file mode 100644 index 0000000..acc9f87 --- /dev/null +++ b/src/Cosmonaut/Factories/CosmosClientFactory.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.Azure.Cosmos; +using Newtonsoft.Json; + +namespace Cosmonaut.Factories +{ + public class CosmosClientFactory + { + public static CosmosClient CreateDocumentClient(CosmosStoreSettings settings) + { + return new CosmosClient(settings.EndpointUrl.ToString(), settings.AuthKey, new CosmosClientOptions + { + Serializer = settings.CosmosSerializer, + ConnectionMode = settings.ConnectionMode, + ConsistencyLevel = settings.ConsistencyLevel + }); + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Factories/DocumentClientFactory.cs b/src/Cosmonaut/Factories/DocumentClientFactory.cs deleted file mode 100644 index 2ea6f73..0000000 --- a/src/Cosmonaut/Factories/DocumentClientFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; -using Newtonsoft.Json; - -namespace Cosmonaut.Factories -{ - public class DocumentClientFactory - { - public static IDocumentClient CreateDocumentClient(CosmosStoreSettings settings) - { - return new DocumentClient(settings.EndpointUrl, settings.AuthKey, settings.JsonSerializerSettings, settings.ConnectionPolicy ?? ConnectionPolicy.Default, settings.ConsistencyLevel); - } - - internal static IDocumentClient CreateDocumentClient(Uri endpoint, string authKeyOrResourceToken, ConnectionPolicy connectionPolicy = null, ConsistencyLevel? desiredConsistencyLevel = null) - { - return new DocumentClient(endpoint, authKeyOrResourceToken, connectionPolicy ?? ConnectionPolicy.Default, desiredConsistencyLevel); - } - - internal static IDocumentClient CreateDocumentClient(Uri endpoint, string authKeyOrResourceToken, JsonSerializerSettings jsonSerializerSettings, ConnectionPolicy connectionPolicy = null, ConsistencyLevel? desiredConsistencyLevel = null) - { - return new DocumentClient(endpoint, authKeyOrResourceToken, jsonSerializerSettings, connectionPolicy ?? ConnectionPolicy.Default, desiredConsistencyLevel); - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/ICosmonautClient.cs b/src/Cosmonaut/ICosmonautClient.cs index f1bfaa8..5e77e64 100644 --- a/src/Cosmonaut/ICosmonautClient.cs +++ b/src/Cosmonaut/ICosmonautClient.cs @@ -4,120 +4,118 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using Cosmonaut.Response; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; +using Microsoft.Azure.Cosmos; namespace Cosmonaut { public interface ICosmonautClient { - IDocumentClient DocumentClient { get; } + CosmosClient CosmosClient { get; } - Task GetDatabaseAsync(string databaseId, RequestOptions requestOptions = null); + Task GetDatabaseAsync(string databaseId, RequestOptions requestOptions = null); - Task> QueryDatabasesAsync(Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task> QueryCollectionsAsync(string databaseId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task> QueryDocumentsAsync(string databaseId, string collectionId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task> QueryDocumentsAsync(string databaseId, string collectionId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task> QueryDocumentsAsync(string databaseId, string collectionId, - string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task> QueryDocumentsAsync(string databaseId, string collectionId, - string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task GetDocumentAsync(string databaseId, string collectionId, string documentId, + Task> QueryDatabasesAsync(Expression> predicate = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task GetDocumentAsync(string databaseId, string collectionId, string documentId, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; - - Task GetCollectionAsync(string databaseId, string collectionId, - RequestOptions requestOptions = null); + Task> QueryContainersAsync(string databaseId, + Expression> predicate = null, ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task GetOfferForCollectionAsync(string databaseId, string collectionId, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default); + Task> QueryItemsAsync(string databaseId, string collectionId, + Expression> predicate = null, ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task GetOfferV2ForCollectionAsync(string databaseId, string collectionId, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// Task> QueryDocumentsAsync(string databaseId, string collectionId, +// Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - Task GetOfferForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default); + Task> QueryItemsAsync(string databaseId, string collectionId, + string sql, object parameters = null, ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task GetOfferV2ForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, - CancellationToken cancellationToken = default); + Task> QueryItemsAsync(string databaseId, string collectionId, + string sql, IDictionary parameters, ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task> QueryOffersAsync(Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// Task GetDocumentAsync(string databaseId, string collectionId, string documentId, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task> QueryOffersV2Async(Expression> predicate = null, - FeedOptions feedOptions = null, CancellationToken cancellationToken = default); + Task GetItemAsync(string databaseId, string collectionId, string documentId, + ContainerRequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; - Task> UpdateOfferAsync(Offer offer); + Task GetContainerAsync(string databaseId, string containerId, + ContainerRequestOptions requestOptions = null); - Task> QueryStoredProceduresAsync(string databaseId, string collectionId, - Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - Task GetStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - RequestOptions requestOptions = null); - - IQueryable Query(string databaseId, string collectionId, FeedOptions feedOptions = null); + IQueryable Query(string databaseId, string collectionId, ItemRequestOptions requestOptions = null); IQueryable Query(string databaseId, string collectionId, string sql, object parameters = null, - FeedOptions feedOptions = null); + ItemRequestOptions requestOptions = null); - IQueryable Query(string databaseId, string collectionId, string sql, - IDictionary parameters, FeedOptions feedOptions = null); +// IQueryable Query(string databaseId, string collectionId, string sql, +// IDictionary parameters, FeedOptions feedOptions = null); - Task> CreateCollectionAsync(string databaseId, DocumentCollection collection, - RequestOptions requestOptions = null); - - Task> CreateDatabaseAsync(Database database, RequestOptions requestOptions = null); - - Task> CreateDocumentAsync(string databaseId, string collectionId, - Document document, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - Task> CreateDocumentAsync(string databaseId, string collectionId, T document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; - - Task> DeleteDocumentAsync(string databaseId, string collectionId, string documentId, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - Task> UpdateDocumentAsync(string databaseId, string collectionId, Document document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - Task> UpdateDocumentAsync(string databaseId, string collectionId, T document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; - - Task> UpsertDocumentAsync(string databaseId, string collectionId, Document document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - Task> UpsertDocumentAsync(string databaseId, string collectionId, T document, - RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; - - Task> DeleteDatabaseAsync(string databaseId, RequestOptions options = null); - - Task> DeleteCollectionAsync(string databaseId, string collectionId, - RequestOptions requestOptions = null); - - Task> UpdateCollectionAsync(string databaseId, string collectionId, DocumentCollection documentCollection, - RequestOptions requestOptions = null); - - Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - params object[] procedureParams); - - Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - RequestOptions requestOptions, params object[] procedureParams); - - Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, - RequestOptions requestOptions, CancellationToken cancellationToken, params object[] procedureParams); +// Task> CreateCollectionAsync(string databaseId, DocumentCollection collection, +// RequestOptions requestOptions = null); +// +// Task> CreateDatabaseAsync(Database database, RequestOptions requestOptions = null); +// +// Task> CreateDocumentAsync(string databaseId, string collectionId, +// Document document, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// Task> CreateDocumentAsync(string databaseId, string collectionId, T document, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; +// +// Task> DeleteDocumentAsync(string databaseId, string collectionId, string documentId, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// Task> UpdateDocumentAsync(string databaseId, string collectionId, Document document, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// Task> UpdateDocumentAsync(string databaseId, string collectionId, T document, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; +// +// Task> UpsertDocumentAsync(string databaseId, string collectionId, Document document, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// Task> UpsertDocumentAsync(string databaseId, string collectionId, T document, +// RequestOptions requestOptions = null, CancellationToken cancellationToken = default) where T : class; +// +// Task> DeleteDatabaseAsync(string databaseId, RequestOptions options = null); +// +// Task> DeleteCollectionAsync(string databaseId, string collectionId, +// RequestOptions requestOptions = null); +// +// Task> UpdateCollectionAsync(string databaseId, string collectionId, DocumentCollection documentCollection, +// RequestOptions requestOptions = null); +// +// Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, +// params object[] procedureParams); +// +// Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, +// RequestOptions requestOptions, params object[] procedureParams); +// +// Task> ExecuteStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, +// RequestOptions requestOptions, CancellationToken cancellationToken, params object[] procedureParams); +// +// Task GetOfferForCollectionAsync(string databaseId, string collectionId, +// FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// Task GetOfferV2ForCollectionAsync(string databaseId, string collectionId, +// FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// Task GetOfferForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, +// CancellationToken cancellationToken = default); +// +// Task GetOfferV2ForDatabaseAsync(string databaseId, FeedOptions feedOptions = null, +// CancellationToken cancellationToken = default); +// +// Task> QueryOffersAsync(Expression> predicate = null, +// FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// Task> QueryOffersV2Async(Expression> predicate = null, +// FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// Task> UpdateOfferAsync(Offer offer); +// +// Task> QueryStoredProceduresAsync(string databaseId, string collectionId, +// Expression> predicate = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// Task GetStoredProcedureAsync(string databaseId, string collectionId, string storedProcedureId, +// RequestOptions requestOptions = null); } } \ No newline at end of file diff --git a/src/Cosmonaut/ICosmosStore.cs b/src/Cosmonaut/ICosmosStore.cs index 17393a6..d85e910 100644 --- a/src/Cosmonaut/ICosmosStore.cs +++ b/src/Cosmonaut/ICosmosStore.cs @@ -1,387 +1,377 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using Cosmonaut.Exceptions; -using Cosmonaut.Response; -using Microsoft.Azure.Documents.Client; - -namespace Cosmonaut +namespace Cosmonaut { public interface ICosmosStore where TEntity : class { - /// - /// Entry point to the usage of LINQ in order to query the collection. It is highly recommended to get the results with the .ToListAsync method - /// because it is using the internal paginated retrieval to prevent locking. - /// - /// The feed options for this operation. - IQueryable Query(FeedOptions feedOptions = null); - - /// - /// Returns an IQueryable that matches the expression provided. You can use ToListAsync to enumerate it or add WithPagination for - /// pagination support. - /// - /// The sql query for this operation. - /// The sql parameters to replace if any - /// The feed options for this operation. - /// The CancellationToken for this operation. - IQueryable Query(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a single item that matches the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace if any - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a single item of any type that matches the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace if any - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a collection of items that match the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace if any - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a collection of items of any type that match the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace if any - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns an IQueryable that matches the expression provided. You can use ToListAsync to enumerate it or add WithPagination for - /// pagination support. - /// - /// The sql query for this operation. - /// The sql parameters to replace as a dictionary - /// The feed options for this operation. - /// The CancellationToken for this operation. - IQueryable Query(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a single item that matches the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace as a dictionary - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a single item of any type that matches the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace as a dictionary - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a collection of items that match the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace as a dictionary - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns a collection of items of any type that match the expression provided. - /// - /// The sql query for this operation. - /// The sql parameters to replace as a dictionary - /// The feed options for this operation. - /// The CancellationToken for this operation. - Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); - - /// - /// Adds the given entity in the cosmos db store. - /// - /// The type of the entity. - /// The entity to add. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Add operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - Task> AddAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Adds the given entities in the cosmos db store. - /// - /// The type of the entities. - /// The entities to add. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous AddRange operation. The task result contains the - /// for the entities. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - Task> AddRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Updates the given entity in the cosmos db store. - /// - /// The type of the entity. - /// The entity to update. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Update operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - Task> UpdateAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Updates the given entities in the cosmos db store. - /// - /// The type of the entities. - /// The entities to update. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Update operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - Task> UpdateRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Adds if absent or updates if present the given entity in the cosmos db store. - /// - /// The type of the entity. - /// The entity to upsert. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Upsert operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - Task> UpsertAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Adds if absent or updates if present the given entities in the cosmos db store. - /// - /// The type of the entities. - /// The entities to upsert. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Upsert operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - //Task> UpsertRangeAsync(IEnumerable entities, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - Task> UpsertRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Removed all the entities matching the given criteria. - /// - /// The type of the entities. - /// The entities to remove. - /// The feed options for this operation. - /// The request options for this operation. - /// The cancellation token for this operation. - /// - /// A task that represents the asynchronous Remove operation. The task result contains the - /// for the entities. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - Task> RemoveAsync(Expression> predicate, FeedOptions feedOptions = null, Func requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Removes the given entity from the cosmos db store. - /// - /// The type of the entity. - /// The entity to remove. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous Remove operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - Task> RemoveAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Removes the given entities from the cosmos db store. - /// - /// The type of the entities. - /// The entities to remove. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous RemoveRange operation. The task result contains the - /// for the entities. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong - /// at the individual entity level. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity does not have an Id specified. - /// - /// - /// An error is encountered while processing the entity. - /// This is because the given entity has more that one Ids specified for it. - /// - Task> RemoveRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Removes the entity with the specified Id from the cosmos db store. - /// - /// The type of the entity. - /// The id of the entity attempting to remove. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous RemoveById operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong. - /// - Task> RemoveByIdAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Removes the entity with the specified Id from the cosmos db store. - /// - /// The type of the entity. - /// The id of the entity attempting to remove. - /// The partition key value. - /// The CancellationToken for this operation. - /// - /// A task that represents the asynchronous RemoveById operation. The task result contains the - /// for the entity. The response provides access to - /// various response information such as whether it was successful or what (if anything) went wrong. - /// - Task> RemoveByIdAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default); - - /// - /// Returns an entity by document/entity id from the cosmos db store. If the collection is partitioned you will need to provide the - /// partition key value in the . - /// - /// The id of the document/entity. - /// The request options for this operation. - /// The CancellationToken for this operation. - /// The entity that matches the id and partition key. Returns null if the entity is not found. - Task FindAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); - - /// - /// Returns an entity by document/entity id and partition key value from the cosmos db store. - /// - /// The id of the document/entity. - /// The partition key value. - /// The CancellationToken for this operation. - /// The entity that matches the id and partition key. Returns null if the entity is not found. - Task FindAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default); - - /// - /// Ensures that the database and collection needed for this CosmosStore is provisioned. If any of the two resources are missing, they will be created automatically. - /// - /// True if both the database and the collection exists - Task EnsureInfrastructureProvisionedAsync(); - - /// - /// The settings that were used to initialise this CosmosStore - /// - CosmosStoreSettings Settings { get; } - - /// - /// Indicates whether this is a shared CosmosStore - /// - bool IsShared { get; } - - /// - /// The name of the collection that this CosmosStore is targeting - /// - string CollectionName { get; } - - /// - /// The name of the database that this CosmosStore is targeting - /// - string DatabaseName { get; } - - ICosmonautClient CosmonautClient { get; } +// /// +// /// Entry point to the usage of LINQ in order to query the collection. It is highly recommended to get the results with the .ToListAsync method +// /// because it is using the internal paginated retrieval to prevent locking. +// /// +// /// The feed options for this operation. +// IQueryable Query(FeedOptions feedOptions = null); +// +// /// +// /// Returns an IQueryable that matches the expression provided. You can use ToListAsync to enumerate it or add WithPagination for +// /// pagination support. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace if any +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// IQueryable Query(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a single item that matches the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace if any +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a single item of any type that matches the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace if any +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task QuerySingleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a collection of items that match the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace if any +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a collection of items of any type that match the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace if any +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task> QueryMultipleAsync(string sql, object parameters = null, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns an IQueryable that matches the expression provided. You can use ToListAsync to enumerate it or add WithPagination for +// /// pagination support. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace as a dictionary +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// IQueryable Query(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a single item that matches the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace as a dictionary +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a single item of any type that matches the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace as a dictionary +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task QuerySingleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a collection of items that match the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace as a dictionary +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns a collection of items of any type that match the expression provided. +// /// +// /// The sql query for this operation. +// /// The sql parameters to replace as a dictionary +// /// The feed options for this operation. +// /// The CancellationToken for this operation. +// Task> QueryMultipleAsync(string sql, IDictionary parameters, FeedOptions feedOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Adds the given entity in the cosmos db store. +// /// +// /// The type of the entity. +// /// The entity to add. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Add operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// Task> AddAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Adds the given entities in the cosmos db store. +// /// +// /// The type of the entities. +// /// The entities to add. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous AddRange operation. The task result contains the +// /// for the entities. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// Task> AddRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Updates the given entity in the cosmos db store. +// /// +// /// The type of the entity. +// /// The entity to update. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Update operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// Task> UpdateAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Updates the given entities in the cosmos db store. +// /// +// /// The type of the entities. +// /// The entities to update. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Update operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// Task> UpdateRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Adds if absent or updates if present the given entity in the cosmos db store. +// /// +// /// The type of the entity. +// /// The entity to upsert. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Upsert operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// Task> UpsertAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Adds if absent or updates if present the given entities in the cosmos db store. +// /// +// /// The type of the entities. +// /// The entities to upsert. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Upsert operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// //Task> UpsertRangeAsync(IEnumerable entities, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// Task> UpsertRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Removed all the entities matching the given criteria. +// /// +// /// The type of the entities. +// /// The entities to remove. +// /// The feed options for this operation. +// /// The request options for this operation. +// /// The cancellation token for this operation. +// /// +// /// A task that represents the asynchronous Remove operation. The task result contains the +// /// for the entities. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// Task> RemoveAsync(Expression> predicate, FeedOptions feedOptions = null, Func requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Removes the given entity from the cosmos db store. +// /// +// /// The type of the entity. +// /// The entity to remove. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous Remove operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// Task> RemoveAsync(TEntity entity, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Removes the given entities from the cosmos db store. +// /// +// /// The type of the entities. +// /// The entities to remove. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous RemoveRange operation. The task result contains the +// /// for the entities. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong +// /// at the individual entity level. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity does not have an Id specified. +// /// +// /// +// /// An error is encountered while processing the entity. +// /// This is because the given entity has more that one Ids specified for it. +// /// +// Task> RemoveRangeAsync(IEnumerable entities, Func requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Removes the entity with the specified Id from the cosmos db store. +// /// +// /// The type of the entity. +// /// The id of the entity attempting to remove. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous RemoveById operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong. +// /// +// Task> RemoveByIdAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Removes the entity with the specified Id from the cosmos db store. +// /// +// /// The type of the entity. +// /// The id of the entity attempting to remove. +// /// The partition key value. +// /// The CancellationToken for this operation. +// /// +// /// A task that represents the asynchronous RemoveById operation. The task result contains the +// /// for the entity. The response provides access to +// /// various response information such as whether it was successful or what (if anything) went wrong. +// /// +// Task> RemoveByIdAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default); +// +// /// +// /// Returns an entity by document/entity id from the cosmos db store. If the collection is partitioned you will need to provide the +// /// partition key value in the . +// /// +// /// The id of the document/entity. +// /// The request options for this operation. +// /// The CancellationToken for this operation. +// /// The entity that matches the id and partition key. Returns null if the entity is not found. +// Task FindAsync(string id, RequestOptions requestOptions = null, CancellationToken cancellationToken = default); +// +// /// +// /// Returns an entity by document/entity id and partition key value from the cosmos db store. +// /// +// /// The id of the document/entity. +// /// The partition key value. +// /// The CancellationToken for this operation. +// /// The entity that matches the id and partition key. Returns null if the entity is not found. +// Task FindAsync(string id, object partitionKeyValue, CancellationToken cancellationToken = default); +// +// /// +// /// Ensures that the database and collection needed for this CosmosStore is provisioned. If any of the two resources are missing, they will be created automatically. +// /// +// /// True if both the database and the collection exists +// Task EnsureInfrastructureProvisionedAsync(); +// +// /// +// /// The settings that were used to initialise this CosmosStore +// /// +// CosmosStoreSettings Settings { get; } +// +// /// +// /// Indicates whether this is a shared CosmosStore +// /// +// bool IsShared { get; } +// +// /// +// /// The name of the collection that this CosmosStore is targeting +// /// +// string CollectionName { get; } +// +// /// +// /// The name of the database that this CosmosStore is targeting +// /// +// string DatabaseName { get; } +// +// ICosmonautClient CosmonautClient { get; } } } \ No newline at end of file diff --git a/src/Cosmonaut/Internal/CosmosDocument.cs b/src/Cosmonaut/Internal/CosmosDocument.cs new file mode 100644 index 0000000..f3bff0a --- /dev/null +++ b/src/Cosmonaut/Internal/CosmosDocument.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json.Linq; + +namespace Cosmonaut.Internal +{ + public class CosmosDocument : CosmosResource + { + public CosmosDocument(JObject json) : base(json) + { + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Internal/CosmosResource.cs b/src/Cosmonaut/Internal/CosmosResource.cs new file mode 100644 index 0000000..333f7ed --- /dev/null +++ b/src/Cosmonaut/Internal/CosmosResource.cs @@ -0,0 +1,68 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Cosmonaut.Internal +{ + public class CosmosResource : CosmosSerializable + { + private static DateTime UnixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + public CosmosResource() + { + } + + public CosmosResource(JObject json) : base(json) + { + } + + [JsonProperty("id")] + public string Id + { + get => GetValue("id"); + set => SetValue("id", value); + } + + [JsonProperty(PropertyName = "_rid")] + public virtual string ResourceId + { + get => GetValue("_rid"); + set => SetValue("_rid", (object) value); + } + + [JsonProperty(PropertyName = "_self")] + public string SelfLink + { + get => GetValue("_self"); + set => SetValue("_self", (object) value); + } + + [JsonIgnore] + public string AltLink { get; set; } + + [JsonProperty(PropertyName = "_ts")] + [JsonConverter(typeof (UnixDateTimeConverter))] + public virtual DateTime Timestamp + { + get => UnixStartTime.AddSeconds(GetValue("_ts")); + internal set => SetValue("_ts", (object) (ulong) (value - UnixStartTime).TotalSeconds); + } + + [JsonProperty(PropertyName = "_etag")] + public string ETag + { + get => GetValue("_etag"); + set => SetValue("_etag", (object) value); + } + + public void SetPropertyValue(string propertyName, object propertyValue) + { + SetValue(propertyName, propertyValue); + } + + public T GetPropertyValue(string propertyName) + { + return GetValue(propertyName); + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Internal/CosmosSerializable.cs b/src/Cosmonaut/Internal/CosmosSerializable.cs new file mode 100644 index 0000000..500861a --- /dev/null +++ b/src/Cosmonaut/Internal/CosmosSerializable.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Cosmonaut.Internal +{ + public class CosmosSerializable + { + private JObject _propertyBag; + + protected CosmosSerializable() + { + _propertyBag = new JObject(); + } + + protected CosmosSerializable(JObject json) + { + _propertyBag = new JObject(json); + } + + public override string ToString() + { + return _propertyBag.ToString(); + } + + internal T GetValue(string propertyName) + { + var jtoken = _propertyBag?[propertyName]; + if (jtoken == null) return default; + + if (typeof (T).IsEnum && jtoken.Type == JTokenType.String) + return jtoken.ToObject(JsonSerializer.CreateDefault()); + //if (this.SerializerSettings != null) + // return jtoken.ToObject(JsonSerializer.Create(this.SerializerSettings)); + return jtoken.ToObject(); + } + + /// + /// Get the value associated with the specified property name. + /// + /// + /// + /// + /// + internal T GetValue(string propertyName, T defaultValue) + { + var jtoken = _propertyBag?[propertyName]; + if (jtoken == null) return defaultValue; + + if (typeof(T).IsEnum && jtoken.Type == JTokenType.String) + return jtoken.ToObject(JsonSerializer.CreateDefault()); + //if (this.SerializerSettings != null) + // return jtoken.ToObject(JsonSerializer.Create(this.SerializerSettings)); + return jtoken.ToObject(); + } + + internal void SetValue(string name, object value) + { + if (_propertyBag == null) + _propertyBag = new JObject(); + if (value != null) + _propertyBag[name] = JToken.FromObject(value); + else + _propertyBag.Remove(name); + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Internal/UnixDateTimeConverter.cs b/src/Cosmonaut/Internal/UnixDateTimeConverter.cs new file mode 100644 index 0000000..2b35d9e --- /dev/null +++ b/src/Cosmonaut/Internal/UnixDateTimeConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Cosmonaut.Internal +{ + public class UnixDateTimeConverter : DateTimeConverterBase + { + private static DateTime _unixStartTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (!(value is DateTime)) + throw new ArgumentException("Invalid datetime", nameof (value)); + var totalSeconds = (long) ((DateTime) value - _unixStartTime).TotalSeconds; + writer.WriteValue(totalSeconds); + } + + public override object ReadJson( + JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.Integer) + throw new Exception("Invalid reader"); + double num; + try + { + num = Convert.ToDouble(reader.Value, (IFormatProvider) CultureInfo.InvariantCulture); + } + catch + { + throw new Exception("Invalid reader double value"); + } + return _unixStartTime.AddSeconds(num); + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Response/CosmosMultipleResponse.cs b/src/Cosmonaut/Response/CosmosMultipleResponse.cs index ff85c9a..f03efcb 100644 --- a/src/Cosmonaut/Response/CosmosMultipleResponse.cs +++ b/src/Cosmonaut/Response/CosmosMultipleResponse.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Response { @@ -7,16 +8,16 @@ public class CosmosMultipleResponse where TEntity : class { public bool IsSuccess => !FailedEntities.Any(); - public List> FailedEntities { get; } = new List>(); + public List> FailedEntities { get; } = new List>(); - public List> SuccessfulEntities { get; } = new List>(); + public List> SuccessfulEntities { get; } = new List>(); - internal void AddResponse(CosmosResponse response) + internal void AddResponse(ItemResponse response) { if (response == null) return; - if (response.IsSuccess) + if ((int)response.StatusCode >= 200 && (int)response.StatusCode < 400) //TODO check this { SuccessfulEntities.Add(response); return; diff --git a/src/Cosmonaut/Response/CosmosPagedResults.cs b/src/Cosmonaut/Response/CosmosPagedResults.cs index 6dface4..3fc878e 100644 --- a/src/Cosmonaut/Response/CosmosPagedResults.cs +++ b/src/Cosmonaut/Response/CosmosPagedResults.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading.Tasks; using Cosmonaut.Extensions; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Response { @@ -14,17 +15,17 @@ internal CosmosPagedResults(List results, int pageSize, string nextPageToken) PageSize = pageSize; } - internal CosmosPagedResults(List results, int pageSize, string nextPageToken, IQueryable queryable) + internal CosmosPagedResults(List results, int pageSize, string nextPageToken, FeedIterator iterator) { Results = results; NextPageToken = nextPageToken; - Queryable = queryable; + Iterator = iterator; PageSize = pageSize; } internal readonly int PageSize; - internal readonly IQueryable Queryable; + internal readonly FeedIterator Iterator; public List Results { get; } @@ -32,19 +33,19 @@ internal CosmosPagedResults(List results, int pageSize, string nextPageToken, public bool HasNextPage => !string.IsNullOrEmpty(NextPageToken); - public async Task> GetNextPageAsync() - { - if(Queryable == null) - return new CosmosPagedResults(new List(), PageSize, string.Empty); - - if(!HasNextPage) - return new CosmosPagedResults(new List(), PageSize, string.Empty); - - if(PageSize <= 0) - return new CosmosPagedResults(new List(), PageSize, string.Empty); - - return await Queryable.WithPagination(NextPageToken, PageSize).ToPagedListAsync(); - } +// public async Task> GetNextPageAsync() +// { +// if(Iterator == null) +// return new CosmosPagedResults(new List(), PageSize, string.Empty); +// +// if(!HasNextPage) +// return new CosmosPagedResults(new List(), PageSize, string.Empty); +// +// if(PageSize <= 0) +// return new CosmosPagedResults(new List(), PageSize, string.Empty); +// +// return await Iterator.WithPagination(NextPageToken, PageSize).ToPagedListAsync(); +// } public static implicit operator List(CosmosPagedResults results) { diff --git a/src/Cosmonaut/Response/CosmosResponse.cs b/src/Cosmonaut/Response/CosmosResponse.cs deleted file mode 100644 index c7716ab..0000000 --- a/src/Cosmonaut/Response/CosmosResponse.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; -using Newtonsoft.Json; - -namespace Cosmonaut.Response -{ - public class CosmosResponse where TEntity : class - { - public bool IsSuccess => CosmosOperationStatus == CosmosOperationStatus.Success; - - public CosmosOperationStatus CosmosOperationStatus { get; } = CosmosOperationStatus.Success; - - public ResourceResponse ResourceResponse { get; } - - public TEntity Entity { get; } - - public Exception Exception { get; } - - public CosmosResponse(ResourceResponse resourceResponse) - { - ResourceResponse = resourceResponse; - } - - public CosmosResponse(TEntity entity, ResourceResponse resourceResponse) - { - ResourceResponse = resourceResponse; - Entity = entity; - } - - public CosmosResponse(TEntity entity, Exception exception, CosmosOperationStatus statusType) - { - CosmosOperationStatus = statusType; - Entity = entity; - Exception = exception; - } - - public static implicit operator TEntity(CosmosResponse response) - { - if (response?.Entity != null) - return response.Entity; - - if (!string.IsNullOrEmpty(response?.ResourceResponse?.Resource?.ToString())) - return JsonConvert.DeserializeObject(response.ResourceResponse.Resource.ToString()); - - return null; - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Storage/CosmosCollectionCreator.cs b/src/Cosmonaut/Storage/CosmosCollectionCreator.cs deleted file mode 100644 index b1cfb95..0000000 --- a/src/Cosmonaut/Storage/CosmosCollectionCreator.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading.Tasks; -using Cosmonaut.Configuration; -using Cosmonaut.Extensions; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; -using Newtonsoft.Json; - -namespace Cosmonaut.Storage -{ - internal class CosmosCollectionCreator : ICollectionCreator - { - private readonly ICosmonautClient _cosmonautClient; - - public CosmosCollectionCreator(ICosmonautClient cosmonautClient) - { - _cosmonautClient = cosmonautClient; - } - - public CosmosCollectionCreator(IDocumentClient documentClient) - { - _cosmonautClient = new CosmonautClient(documentClient); - } - - public async Task EnsureCreatedAsync( - string databaseId, - string collectionId, - int collectionThroughput, - JsonSerializerSettings partitionKeySerializer, - IndexingPolicy indexingPolicy = null, - ThroughputBehaviour onDatabaseBehaviour = ThroughputBehaviour.UseDatabaseThroughput, - UniqueKeyPolicy uniqueKeyPolicy = null) where TEntity : class - { - var collectionResource = await _cosmonautClient.GetCollectionAsync(databaseId, collectionId); - var databaseHasOffer = (await _cosmonautClient.GetOfferV2ForDatabaseAsync(databaseId)) != null; - - if (collectionResource != null) - return true; - - var newCollection = new DocumentCollection - { - Id = collectionId, - IndexingPolicy = indexingPolicy ?? CosmosConstants.DefaultIndexingPolicy, - UniqueKeyPolicy = uniqueKeyPolicy ?? CosmosConstants.DefaultUniqueKeyPolicy - }; - - SetPartitionKeyDefinitionForCollection(typeof(TEntity), newCollection, partitionKeySerializer); - - var finalCollectionThroughput = databaseHasOffer ? onDatabaseBehaviour == ThroughputBehaviour.DedicateCollectionThroughput ? (int?)collectionThroughput : null : collectionThroughput; - - newCollection = await _cosmonautClient.CreateCollectionAsync(databaseId, newCollection, new RequestOptions - { - OfferThroughput = finalCollectionThroughput - }); - - return newCollection != null; - } - - private static void SetPartitionKeyDefinitionForCollection(Type entityType, DocumentCollection collection, JsonSerializerSettings serializerSettings) - { - var partitionKey = entityType.GetPartitionKeyDefinitionForEntity(serializerSettings); - - if (partitionKey != null) - collection.PartitionKey = partitionKey; - } - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Storage/CosmosContainerCreator.cs b/src/Cosmonaut/Storage/CosmosContainerCreator.cs new file mode 100644 index 0000000..482e9c9 --- /dev/null +++ b/src/Cosmonaut/Storage/CosmosContainerCreator.cs @@ -0,0 +1,57 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using Cosmonaut.Configuration; +using Cosmonaut.Extensions; +using Microsoft.Azure.Cosmos; +using Newtonsoft.Json; + +namespace Cosmonaut.Storage +{ + internal class CosmosContainerCreator : IContainerCreator + { + private readonly ICosmonautClient _cosmonautClient; + + public CosmosContainerCreator(ICosmonautClient cosmonautClient) + { + _cosmonautClient = cosmonautClient; + } + + public CosmosContainerCreator(CosmosClient documentClient) + { + _cosmonautClient = new CosmonautClient(documentClient); + } + + public async Task EnsureCreatedAsync( + string databaseId, + string containerId, + int containerThroughput, + JsonSerializerSettings partitionKeySerializer, + IndexingPolicy indexingPolicy = null, + ThroughputBehaviour onDatabaseBehaviour = ThroughputBehaviour.UseDatabaseThroughput, + UniqueKeyPolicy uniqueKeyPolicy = null) where TEntity : class + { + var containerResponse = await _cosmonautClient.GetContainerAsync(databaseId, containerId); + var databaseHasOffer = await _cosmonautClient.CosmosClient.GetDatabase(databaseId).ReadThroughputAsync() != null; + + if (containerResponse.StatusCode != HttpStatusCode.NotFound) + return true; + + var partitionKeyDef = typeof(TEntity).GetPartitionKeyDefinitionForEntity(partitionKeySerializer); + + var containerProperties = new ContainerProperties + { + Id = containerId, + IndexingPolicy = indexingPolicy ?? CosmosConstants.DefaultIndexingPolicy, + UniqueKeyPolicy = uniqueKeyPolicy ?? CosmosConstants.DefaultUniqueKeyPolicy, + PartitionKeyPath = partitionKeyDef + }; + + var finalCollectionThroughput = databaseHasOffer ? onDatabaseBehaviour == ThroughputBehaviour.DedicateCollectionThroughput ? (int?)containerThroughput : null : containerThroughput; + + var response = await _cosmonautClient.CosmosClient.GetDatabase(databaseId).CreateContainerAsync(containerProperties, finalCollectionThroughput); + + return response != null; // TODO check for status code + } + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Storage/CosmosDatabaseCreator.cs b/src/Cosmonaut/Storage/CosmosDatabaseCreator.cs index 5476527..fc53f7f 100644 --- a/src/Cosmonaut/Storage/CosmosDatabaseCreator.cs +++ b/src/Cosmonaut/Storage/CosmosDatabaseCreator.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; +using System.Net; +using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; namespace Cosmonaut.Storage { @@ -13,7 +13,7 @@ public CosmosDatabaseCreator(ICosmonautClient cosmonautClient) _cosmonautClient = cosmonautClient; } - public CosmosDatabaseCreator(IDocumentClient documentClient) + public CosmosDatabaseCreator(CosmosClient documentClient) { _cosmonautClient = new CosmonautClient(documentClient); } @@ -22,15 +22,10 @@ public async Task EnsureCreatedAsync(string databaseId, int? databaseThrou { var database = await _cosmonautClient.GetDatabaseAsync(databaseId); - if (database != null) return false; + if (database.StatusCode == HttpStatusCode.NotFound) return false; - var newDatabase = new Database {Id = databaseId}; - - database = await _cosmonautClient.CreateDatabaseAsync(newDatabase, new RequestOptions - { - OfferThroughput = databaseThroughput - }); - return database != null; + database = await _cosmonautClient.CosmosClient.CreateDatabaseAsync(databaseId, databaseThroughput); + return database != null; //TODO check this } } } \ No newline at end of file diff --git a/src/Cosmonaut/Storage/ICollectionCreator.cs b/src/Cosmonaut/Storage/ICollectionCreator.cs deleted file mode 100644 index 9bf395d..0000000 --- a/src/Cosmonaut/Storage/ICollectionCreator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; -using Cosmonaut.Configuration; -using Microsoft.Azure.Documents; -using Newtonsoft.Json; - -namespace Cosmonaut.Storage -{ - public interface ICollectionCreator - { - Task EnsureCreatedAsync(string databaseId, string collectionId, int collectionThroughput, JsonSerializerSettings partitionKeySerializer, IndexingPolicy indexingPolicy = null, ThroughputBehaviour onDatabaseBehaviour = ThroughputBehaviour.UseDatabaseThroughput, UniqueKeyPolicy uniqueKeyPolicy = null) where TEntity : class; - } -} \ No newline at end of file diff --git a/src/Cosmonaut/Storage/IContainerCreator.cs b/src/Cosmonaut/Storage/IContainerCreator.cs new file mode 100644 index 0000000..3d3f65d --- /dev/null +++ b/src/Cosmonaut/Storage/IContainerCreator.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Cosmonaut.Configuration; +using Microsoft.Azure.Cosmos; +using Newtonsoft.Json; + +namespace Cosmonaut.Storage +{ + public interface IContainerCreator + { + Task EnsureCreatedAsync(string databaseId, string containerId, int containerThroughput, JsonSerializerSettings partitionKeySerializer, IndexingPolicy indexingPolicy = null, ThroughputBehaviour onDatabaseBehaviour = ThroughputBehaviour.UseDatabaseThroughput, UniqueKeyPolicy uniqueKeyPolicy = null) where TEntity : class; + } +} \ No newline at end of file diff --git a/src/Cosmonaut/Testing/TestingExtensions.cs b/src/Cosmonaut/Testing/TestingExtensions.cs deleted file mode 100644 index 9e7d1e9..0000000 --- a/src/Cosmonaut/Testing/TestingExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using Cosmonaut.Internal; -using Microsoft.Azure.Documents; -using Microsoft.Azure.Documents.Client; - -namespace Cosmonaut.Testing -{ - public static class TestingExtensions - { - public static ResourceResponse ToResourceResponse(this T resource, HttpStatusCode statusCode, IDictionary responseHeaders = null) where T : Resource, new() - { - var resourceResponse = new ResourceResponse(resource); - - var flags = BindingFlags.NonPublic | BindingFlags.Instance; - - var headers = new NameValueCollection { { "x-ms-request-charge", "0" } }; - - if (responseHeaders != null) - { - foreach (var responseHeader in responseHeaders) - { - headers[responseHeader.Key] = responseHeader.Value; - } - } - - var headersDictionaryInstance = Activator.CreateInstance(InternalTypeCache.Instance.DictionaryNameValueCollectionType, headers); - - var arguments = new[] { Stream.Null, headersDictionaryInstance, statusCode, null }; - - var documentServiceResponse = InternalTypeCache.Instance.DocumentServiceResponseCtorInfo.Invoke(arguments); - - var responseField = typeof(ResourceResponse).GetTypeInfo().GetField("response", flags); - - responseField?.SetValue(resourceResponse, documentServiceResponse); - - return resourceResponse; - } - - public static FeedResponse ToFeedResponse(this IQueryable resource, IDictionary responseHeaders = null) - { - var headers = new NameValueCollection - { - { "x-ms-request-charge", "0" }, - { "x-ms-activity-id", Guid.NewGuid().ToString() } - }; - - if (responseHeaders != null) - { - foreach (var responseHeader in responseHeaders) - { - headers[responseHeader.Key] = responseHeader.Value; - } - } - - var headersDictionaryInstance = Activator.CreateInstance(InternalTypeCache.Instance.DictionaryNameValueCollectionType, headers); - - var arguments = new[] { resource, resource.Count(), headersDictionaryInstance, false, null, null, null, 0 }; - - var feedResponse = InternalTypeCache.Instance.FeedResponseCtorInfo().Invoke(arguments); - - return (FeedResponse)feedResponse; - } - } -} \ No newline at end of file