Skip to content

Commit

Permalink
Refactor on repository and add mutations
Browse files Browse the repository at this point in the history
  • Loading branch information
Diego Pereira committed Apr 3, 2021
1 parent a9cffbe commit 62eb2da
Show file tree
Hide file tree
Showing 19 changed files with 153 additions and 77 deletions.
2 changes: 1 addition & 1 deletion src/GraphQL.API.Tests/QueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async void Query_ReturnsProducts()
.AddScoped<ICategoryRepository, CategoryRepository>() // Should be mocked
.AddScoped<IProductRepository, ProductRepository>() // Should be mocked
.AddGraphQL()
.AddQueryType<Query>()
.AddQueryType<ProductQuery>()
.AddType<ProductType>()
.AddType<CategoryResolver>()
.BuildRequestExecutorAsync();
Expand Down
10 changes: 10 additions & 0 deletions src/GraphQL.API/Mutations/CategoryMutation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace GraphQL.API.Mutations
{
using HotChocolate.Types;

[ExtendObjectType(Name = "Mutation")]
public class CategoryMutation
{
// TODO: Create methods
}
}
18 changes: 18 additions & 0 deletions src/GraphQL.API/Mutations/ProductMutation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace GraphQL.API.Mutations
{
using GraphQL.Core.Entities;
using GraphQL.Core.Repositories;
using HotChocolate;
using HotChocolate.Types;
using System.Threading.Tasks;

[ExtendObjectType(Name = "Mutation")]
public class ProductMutation
{
public Task<Product> CreateProductAsync(Product product, [Service] IProductRepository productRepository) =>
productRepository.InsertAsync(product);

public Task<bool> RemoveProductAsync(string id, [Service] IProductRepository productRepository) =>
productRepository.RemoveAsync(id);
}
}
14 changes: 14 additions & 0 deletions src/GraphQL.API/Queries/CategoryQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace GraphQL.API.Queries
{
using HotChocolate.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

[ExtendObjectType(Name = "Query")]
public class CategoryQuery
{
// Create methods
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;

public class Query
public class ProductQuery
{
public Task<IEnumerable<Product>> GetProductsAsync([Service] IProductRepository productRepository) =>
productRepository.GetAllAsync();

public Task<Product> GetProductById(string id, [Service] IProductRepository productRepository) =>
public Task<Product> GetProductByIdAsync(string id, [Service] IProductRepository productRepository) =>
productRepository.GetByIdAsync(id);

// Add any queries you want here..
Expand Down
3 changes: 2 additions & 1 deletion src/GraphQL.API/Resolvers/CategoryResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[ExtendObjectType(Name = "Category")]
public class CategoryResolver
{
public Task<Category> GetCategoryAsync([Parent] Product product, [Service] ICategoryRepository categoryRepository) => categoryRepository.GetById(product.CategoryId);
public Task<Category> GetCategoryAsync([Parent] Product product, [Service] ICategoryRepository categoryRepository) =>
categoryRepository.GetByIdAsync(product.CategoryId);
}
}
9 changes: 8 additions & 1 deletion src/GraphQL.API/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace GraphQL.API
{
using GraphQL.API.Configurations;
using GraphQL.API.Mutations;
using GraphQL.API.Queries;
using GraphQL.API.Resolvers;
using GraphQL.API.Types;
Expand Down Expand Up @@ -30,13 +31,19 @@ public void ConfigureServices(IServiceCollection services)

// Repositories
services.AddSingleton<ICatalogContext, CatalogContext>();
services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddScoped<ICategoryRepository, CategoryRepository>();
services.AddScoped<IProductRepository, ProductRepository>();

// GraphQL
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddQueryType(d => d.Name("Query"))
.AddTypeExtension<ProductQuery>()
.AddTypeExtension<CategoryQuery>()
.AddMutationType(d => d.Name("Mutation"))
.AddTypeExtension<ProductMutation>()
.AddTypeExtension<CategoryMutation>()
.AddType<ProductType>()
.AddType<CategoryResolver>();
}
Expand Down
8 changes: 1 addition & 7 deletions src/GraphQL.Core/Entities/Category.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
namespace GraphQL.Core.Entities
{
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class Category
public class Category : EntityBase
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Description { get; set; }
}
}
12 changes: 12 additions & 0 deletions src/GraphQL.Core/Entities/EntityBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace GraphQL.Core.Entities
{
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class EntityBase
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
}
}
8 changes: 1 addition & 7 deletions src/GraphQL.Core/Entities/Product.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
namespace GraphQL.Core.Entities
{
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

public class Product
public class Product : EntityBase
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
Expand Down
14 changes: 14 additions & 0 deletions src/GraphQL.Core/Repositories/IBaseRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace GraphQL.Core.Repositories
{
using GraphQL.Core.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;

public interface IBaseRepository<T> where T : EntityBase
{
Task<IEnumerable<T>> GetAllAsync();
Task<T> GetByIdAsync(string id);
Task<T> InsertAsync(T entity);
Task<bool> RemoveAsync(string id);
}
}
5 changes: 2 additions & 3 deletions src/GraphQL.Core/Repositories/ICategoryRepository.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace GraphQL.Core.Repositories
{
using GraphQL.Core.Entities;
using System.Threading.Tasks;

public interface ICategoryRepository
public interface ICategoryRepository : IBaseRepository<Category>
{
Task<Category> GetById(string id);

}
}
7 changes: 2 additions & 5 deletions src/GraphQL.Core/Repositories/IProductRepository.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
namespace GraphQL.Core.Repositories
{
using GraphQL.Core.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;

public interface IProductRepository
public interface IProductRepository : IBaseRepository<Product>
{
Task<IEnumerable<Product>> GetAllAsync();
Task<Product> GetByIdAsync(string id);

}
}
16 changes: 7 additions & 9 deletions src/GraphQL.Infrastructure/Data/CatalogContext.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
namespace GraphQL.Infrastructure.Data
{
using GraphQL.Core.Entities;
using GraphQL.Infrastructure.Configurations;
using MongoDB.Driver;

public class CatalogContext : ICatalogContext
{
private const string ProductCollectionName = "Products";
private const string CategoryCollectionName = "Categories";
private readonly IMongoDatabase database;

public CatalogContext(MongoDbConfiguration mongoDbConfiguration)
{
var client = new MongoClient(mongoDbConfiguration.ConnectionString);
var database = client.GetDatabase(mongoDbConfiguration.Database);

this.Categories = database.GetCollection<Category>(CategoryCollectionName);
this.Products = database.GetCollection<Product>(ProductCollectionName);
this.database = client.GetDatabase(mongoDbConfiguration.Database);

CatalogContextSeed.SeedData(this.Categories, this.Products);
CatalogContextSeed.SeedData(this.database);
}

public IMongoCollection<Category> Categories { get; }
public IMongoCollection<Product> Products { get; }
public IMongoCollection<T> GetCollection<T>(string name)
{
return this.database.GetCollection<T>(name);
}
}
}
8 changes: 3 additions & 5 deletions src/GraphQL.Infrastructure/Data/CatalogContextSeed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

public class CatalogContextSeed
{
public static void SeedData(
IMongoCollection<Category> categoryCollection,
IMongoCollection<Product> productCollection)
public static void SeedData(IMongoDatabase database)
{
InsertCategories(categoryCollection);
InsertProducts(productCollection);
InsertCategories(database.GetCollection<Category>(nameof(Category)));
InsertProducts(database.GetCollection<Product>(nameof(Product)));
}

private static void InsertCategories(IMongoCollection<Category> categoryCollection)
Expand Down
4 changes: 1 addition & 3 deletions src/GraphQL.Infrastructure/Data/ICatalogContext.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
namespace GraphQL.Infrastructure.Data
{
using GraphQL.Core.Entities;
using MongoDB.Driver;

public interface ICatalogContext
{
IMongoCollection<Category> Categories { get; }
IMongoCollection<Product> Products { get; }
IMongoCollection<T> GetCollection<T>(string name);
}
}
51 changes: 51 additions & 0 deletions src/GraphQL.Infrastructure/Repositories/BaseRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace GraphQL.Infrastructure.Repositories
{
using GraphQL.Core.Entities;
using GraphQL.Core.Repositories;
using GraphQL.Infrastructure.Data;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class BaseRepository<T> : IBaseRepository<T> where T : EntityBase
{
private readonly IMongoCollection<T> collection;

public BaseRepository(ICatalogContext catalogContext)
{
if (catalogContext == null)
{
throw new ArgumentNullException(nameof(catalogContext));
}

this.collection = catalogContext.GetCollection<T>(typeof(T).Name);
}

public async Task<IEnumerable<T>> GetAllAsync()
{
return await this.collection.Find(_ => true).ToListAsync();
}

public async Task<T> GetByIdAsync(string id)
{
var filter = Builders<T>.Filter.Eq(_ => _.Id, id);

return await this.collection.Find(filter).FirstOrDefaultAsync();
}

public async Task<T> InsertAsync(T entity)
{
await this.collection.InsertOneAsync(entity);

return entity;
}

public async Task<bool> RemoveAsync(string id)
{
var result = await this.collection.DeleteOneAsync(Builders<T>.Filter.Eq(_ => _.Id, id));

return result.DeletedCount > 0;
}
}
}
15 changes: 2 additions & 13 deletions src/GraphQL.Infrastructure/Repositories/CategoryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,12 @@
using GraphQL.Core.Entities;
using GraphQL.Core.Repositories;
using GraphQL.Infrastructure.Data;
using MongoDB.Driver;
using System.Threading.Tasks;

public class CategoryRepository : ICategoryRepository
public class CategoryRepository : BaseRepository<Category>, ICategoryRepository
{
private readonly ICatalogContext catalogContext;

public CategoryRepository(ICatalogContext catalogContext)
{
this.catalogContext = catalogContext;
}

public async Task<Category> GetById(string id)
public CategoryRepository(ICatalogContext catalogContext) : base(catalogContext)
{
var filter = Builders<Category>.Filter.Eq(_ => _.Id, id);

return await this.catalogContext.Categories.Find(filter).FirstOrDefaultAsync();
}
}
}
22 changes: 2 additions & 20 deletions src/GraphQL.Infrastructure/Repositories/ProductRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,12 @@
using GraphQL.Core.Entities;
using GraphQL.Core.Repositories;
using GraphQL.Infrastructure.Data;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class ProductRepository : IProductRepository
public class ProductRepository : BaseRepository<Product>, IProductRepository
{
private readonly ICatalogContext catalogContext;

public ProductRepository(ICatalogContext catalogContext)
{
this.catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext));
}

public async Task<IEnumerable<Product>> GetAllAsync()
{
return await this.catalogContext.Products.Find(_ => true).ToListAsync();
}

public async Task<Product> GetByIdAsync(string id)
public ProductRepository(ICatalogContext catalogContext) : base(catalogContext)
{
var filter = Builders<Product>.Filter.Eq(_ => _.Id, id);

return await this.catalogContext.Products.Find(filter).FirstOrDefaultAsync();
}
}
}

0 comments on commit 62eb2da

Please sign in to comment.