Skip to content

Commit

Permalink
reset branch
Browse files Browse the repository at this point in the history
revert back due to large file committed without LFS
  • Loading branch information
psyphore committed Jul 14, 2024
1 parent 61c96cd commit ec9efd8
Show file tree
Hide file tree
Showing 23 changed files with 330 additions and 115 deletions.
35 changes: 19 additions & 16 deletions documents/load-service-providers.cypher
Original file line number Diff line number Diff line change
Expand Up @@ -90,25 +90,28 @@ CREATE
(Product9)<-[:HAS_PRICE{ created: timestamp(), createdBy: 'system' }]-(M9),
(Product10)<-[:HAS_PRICE{ created: timestamp(), createdBy: 'system' }]-(M10)

CREATE
(Res:Address{ line1:'55 Acacia Road', line2:'1 Carlswald Meadows Estate', line3: null, suburb:'Blue Hills AH', city:'Midrand', zip:'1685', country:'ZA', created: timestamp()})
CREATE (Res:Address{ line1:'55 Acacia Road', line2:'1 Carlswald Meadows Estate', line3: null, suburb:'Blue Hills AH', city:'Midrand', zip:'1685', country:'ZA', created: timestamp()})
CREATE (Customer:Lead{ id: apoc.create.uuid(), active: true, firstName:'John', lastName:'Wick', dateOfBirth: datetime('1971-01-12T00:00:00'), created: timestamp()})
CREATE (CustomerContact:Contact{ id: apoc.create.uuid(), active: true, number:'0718890001', email: 'john.wick@continetal.hightable.org', created: timestamp()})

CREATE
(Customer:Lead{ id: apoc.create.uuid(), active: true, firstName:'John', lastName:'Wick', dateOfBirth: datetime('1971-01-12T00:00:00'), created: timestamp()})
CREATE (Customer)-[:RESIDES_AT{ since: datetime('2010-11-04T08:00:00'), created: timestamp(), createdBy: 'system' }]->(Res)
CREATE (Customer)-[:HAS_CONTACT{ created: timestamp(), createdBy: 'system' }]->(CustomerContact)

CREATE
(CustomerContact:Contact{ id: apoc.create.uuid(), active: true, number:'0718890001', email: 'john.wick@continetal.hightable.org', created: timestamp()})
// https://neo4j.com/docs/cypher-manual/current/indexes/search-performance-indexes/managing-indexes/#create-text-index
CREATE TEXT INDEX product_text_index_id FOR (n:Product) ON (n.id)
CREATE TEXT INDEX product_text_index_name FOR (n:Product) ON (n.name)
CREATE TEXT INDEX product_text_index_tags FOR (n:Product) ON (n.tags)
CREATE TEXT INDEX money_text_index_currency FOR (n:Money) ON (n.currency)
CREATE RANGE INDEX money_range_index_amount FOR (n:Money) ON (n.amount)
CREATE TEXT INDEX lead_text_index_id FOR (n:Lead) ON (n.id)
CREATE TEXT INDEX lead_text_index_names FOR (n:Lead) ON (n.names)

CREATE
(Customer)-[:RESIDES_AT{ since: datetime('2010-11-04T08:00:00'), created: timestamp(), createdBy: 'system' }]->(Res)
// https://neo4j.com/docs/cypher-manual/current/indexes/search-performance-indexes/managing-indexes/#create-a-composite-range-index-for-nodes
CREATE INDEX product_composite_index FOR (n:Product) ON (n.id, n.name, n.tags)
CREATE INDEX address_composite_index FOR (n:Address) ON (n.line1, n.line2, n.line3, n.suburb, n.city, n.zip, n.country)

CREATE
(Customer)-[:HAS_CONTACT{ created: timestamp(), createdBy: 'system' }]->(CustomerContact)

CREATE TEXT INDEX FOR (n:Product) ON EACH [n.id, n.name, n.tags]
CREATE TEXT INDEX FOR (n:Money) ON (n.currency)
CREATE RANGE INDEX FOR (n:Money) ON (n.amount)
CREATE TEXT INDEX FOR (n:Lead) ON EACH [n.id, n.names]
CREATE TEXT INDEX FOR (n:Address) ON EACH [n.line1, n.line2, n.line3, n.suburb, n.city, n.zip, n.country]
https://neo4j.com/docs/cypher-manual/current/constraints/syntax/#constraints-syntax-create-node-unique
CREATE CONSTRAINT product_unique_containt IF NOT EXISTS FOR (n:Product) REQUIRE n.name IS UNIQUE
CREATE CONSTRAINT product_null_containt IF NOT EXISTS FOR (n:Product) REQUIRE n.name IS NOT NULL

;
13 changes: 5 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ I will loosely follow the Domain Driven Design pattern.

stack:

- dotnet 6 (LTS)
- Neo4j-driver 5.5
- HotChocolate GraphQL 13.0.5
- RedisStack 6.2.6v2
- dotnet 8 (LTS)
- Neo4j-driver 5.22
- HotChocolate GraphQL 13.9.7
- RedisStack 6.2.6v14

## Project Structure

Expand Down Expand Up @@ -42,10 +42,7 @@ net-core-graphql
"Password": "thumbeza-tech-l3ad5",
"databaseName": "leads"
},
"Redis": {
"Host": "localhost",
"Port": 6379
},
"Redis": "redis://localhost:6379",
"Smtp": {
"Sender": "no-reply@leads.thumbezatech.co.za",
"SenderName": "Thumbeza Tech",
Expand Down
21 changes: 20 additions & 1 deletion src/ThumbezaTech.Leads.Application/Leads/CreateLeadCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using ThumbezaTech.Leads.Domain.LeadAggregate;
using FluentValidation;

using ThumbezaTech.Leads.Domain.LeadAggregate;

namespace ThumbezaTech.Leads.Application.Leads;
public sealed record CreateLeadCommand(Lead Lead) : ICommand<Result>;
Expand All @@ -15,3 +17,20 @@ public ValueTask<Result> Handle(CreateLeadCommand command, CancellationToken can
return _service.CreateALeadAsync(Guard.Against.Null(command.Lead, nameof(command.Lead)), cancellationToken);
}
}

internal sealed class CreateLeadCommandValidator: AbstractValidator<CreateLeadCommand>
{
public CreateLeadCommandValidator()
{
RuleFor(m => m.Lead).NotNull();
RuleFor(m => m.Lead.FirstName).NotEmpty().NotNull();
RuleFor(m => m.Lead.LastName).NotEmpty().NotNull();
RuleFor(m => m.Lead.Id).Empty().Null();
RuleFor(m => m.Lead.Active).Must(v => false);
RuleFor(m => m.Lead.Contacts)
.NotEmpty()
.NotNull()
.Must(v => v.All(i => !string.IsNullOrEmpty(i.Number)));
RuleFor(m => m.Lead.DateOfBirth).InclusiveBetween(DateTimeOffset.MinValue, DateTimeOffset.Now);
}
}
23 changes: 22 additions & 1 deletion src/ThumbezaTech.Leads.Application/Leads/UpdateLeadCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using ThumbezaTech.Leads.Domain.LeadAggregate;
using FluentValidation;

using ThumbezaTech.Leads.Domain.LeadAggregate;

namespace ThumbezaTech.Leads.Application.Leads;

Expand All @@ -25,3 +27,22 @@ public async ValueTask<Result> Handle(UpdateLeadCommand command, CancellationTok
return await _service.UpdateLeadAsync(matched.Value, cancellationToken);
}
}

internal sealed class UpdateLeadCommandValidator : AbstractValidator<UpdateLeadCommand>
{
public UpdateLeadCommandValidator()
{
RuleFor(m => m.Lead).NotNull();
RuleFor(m => m.Lead.FirstName).NotEmpty().NotNull();
RuleFor(m => m.Lead.LastName).NotEmpty().NotNull();
RuleFor(m => m.Lead.Id).NotEmpty().NotNull();
RuleFor(m => m.Lead.Active).Must(i => true);

RuleFor(m => m.Lead.Contacts)
.NotEmpty()
.NotNull()
.Must(v => v.All(i => !string.IsNullOrEmpty(i.Number)));

RuleFor(m => m.Lead.DateOfBirth).InclusiveBetween(DateTimeOffset.MinValue, DateTimeOffset.Now);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<GraphQL Remove="Client\GetProductById.graphql" />
<GraphQL Remove="Client\GetProducts.graphql" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="StrawberryShake.Blazor" Version="13.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

using Neo4j.Driver;

using ThumbezaTech.Leads.SharedKernel;

namespace ThumbezaTech.Leads.Infrastructure.Data.Common;

// https://github.com/chintan196/DotnetCore.Neo4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public Repository(ILogger<Repository<T>> logger, IDriver driver, Neo4JConfigurat

public async ValueTask CreateIndicesAsync(string[] labels)
{
labels = !labels.Any() ? new[] { typeof(T).Name.ToString() } : labels;
labels = labels.Length == 0 ? [typeof(T).Name.ToString()] : labels;
using var session = GetSession(AccessMode.Write);
foreach (var query in labels.Select(l => string.Format("CREATE INDEX ON :{0}(id)", l)))
await session.ExecuteWriteAsync(r => r.RunAsync(query, null!));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public async ValueTask<Result<Lead>> GetLeadByIdAsync(string id, CancellationTok
};
var statement = Queries.Options[Queries.GetOne].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Lead>(statement, Label, Query);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.First())
: Result.NotFound();
}
Expand All @@ -31,7 +31,7 @@ public async ValueTask<Result<IEnumerable<Lead>>> GetLeadsAsync(CancellationToke
{
var statement = Queries.Options[Queries.GetAll].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Lead>(statement, $"{Label}s", null!);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.Distinct())
: Result.NotFound();
}
Expand Down Expand Up @@ -64,7 +64,7 @@ public async ValueTask<Result> CreateALeadAsync(Lead lead, CancellationToken can
{
await _publisher.Publish(item, cancellationToken);
}
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand All @@ -84,7 +84,7 @@ public async ValueTask<Result> UpdateLeadAsync(Lead lead, CancellationToken canc
{
await _publisher.Publish(item, cancellationToken);
}
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public async ValueTask<Result> CreateOrderAsync(Order order, CancellationToken c
};
var statement = Commands.Options[Commands.SaveOne].Trim();
var payload = await _data.ExecuteWriteTransactionAsync<string>(statement, input);
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand All @@ -32,7 +32,7 @@ public async ValueTask<Result<IEnumerable<Order>>> GetLeadOrdersAsync(string id,
};
var statement = Queries.Options[Queries.LeadOrders].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Order>(statement, $"{Label}s", Query);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.Distinct())
: Result.NotFound();
}
Expand All @@ -45,7 +45,7 @@ public async ValueTask<Result<Order>> GetOrderAsync(string id, CancellationToken
};
var statement = Queries.Options[Queries.LeadOrders].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Order>(statement, $"{Label}s", Query);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.First())
: Result.NotFound();
}
Expand All @@ -54,7 +54,7 @@ public async ValueTask<Result<IEnumerable<Order>>> GetOrdersAsync(CancellationTo
{
var statement = Queries.Options[Queries.LeadOrders].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Order>(statement, $"{Label}s", null!);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.Distinct())
: Result.NotFound();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public async ValueTask<Result<IEnumerable<Product>>> QueryProducts(string query,
};
var statement = Queries.Options[Queries.Search].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Product>(statement, $"{Label}s", Query);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.Distinct())
: Result.NotFound();
}
Expand All @@ -29,7 +29,7 @@ public async ValueTask<Result<IEnumerable<Product>>> GetProducts(CancellationTok
{
var statement = Queries.Options[Queries.GetAll].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Product>(statement, $"{Label}s", null!);
return payload.Any()
return payload.Count != 0
? Result.Success(payload.Distinct())
: Result.NotFound();
}
Expand All @@ -55,7 +55,7 @@ public async ValueTask<Result> AddProduct(Product product, CancellationToken can
};
var statement = Commands.Options[Commands.SaveOne].Trim();
var payload = await _data.ExecuteWriteTransactionAsync<string>(statement, input);
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand All @@ -68,7 +68,7 @@ public async ValueTask<Result> UpdateProduct(Product product, CancellationToken
};
var statement = Commands.Options[Commands.UpdateOne].Trim();
var payload = await _data.ExecuteWriteTransactionAsync<string>(statement, input);
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public async ValueTask<Result<List<Shipment>>> GetShipments()
{
var statement = Queries.Options[Queries.GetAll].Trim();
var payload = await _data.ExecuteReadTransactionAsync<Shipment>(statement, $"{Label}s", null!);
return payload.Any()
return payload.Count != 0
? Result.Success(payload)
: Result.NotFound();
}
Expand Down Expand Up @@ -53,7 +53,7 @@ public async ValueTask<Result> CreateShipment(Shipment shipment)
};
var statement = Commands.Options[Commands.SaveOne].Trim();
var payload = await _data.ExecuteWriteTransactionAsync<string>(statement, input);
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand All @@ -66,7 +66,7 @@ public async ValueTask<Result> UpdateShipment(Shipment shipment)
};
var statement = Commands.Options[Commands.UpdateOne].Trim();
var payload = await _data.ExecuteWriteTransactionAsync<string>(statement, input);
return payload.Any()
return payload.Length != 0
? Result.SuccessWithMessage(payload)
: Result.NotFound();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Neo4j.Driver" Version="5.7.0" />
<PackageReference Include="Redis.OM" Version="0.5.0" />
<PackageReference Include="Neo4j.Driver" />
<PackageReference Include="Redis.OM" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ardalis.Specification" Version="6.1.0" />
<PackageReference Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageReference Include="Ardalis.Result" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Mediator.Abstractions" Version="2.1.1" />
<PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Ardalis.Result" />
<PackageReference Include="Ardalis.SmartEnum" />
<PackageReference Include="Ardalis.Specification" />
<PackageReference Include="Mediator.Abstractions" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>

</Project>
16 changes: 12 additions & 4 deletions src/ThumbezaTech.Leads.Web.GraphQL/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ public static IServiceCollection AddGraphQL(this IServiceCollection services)
.AddSubscriptionType()
.AddRedisSubscriptions((sp) =>
{
var config = sp.GetRequiredService<IConfiguration>();
return ConnectionMultiplexer.Connect($"{config["Redis:Host"]}:{config["Redis:Port"]}");
var config = sp
.GetRequiredService<IConfiguration>()
.GetValue<string>("Redis");

return ConnectionMultiplexer.Connect(config!);
})
.AddTypeExtension<ProductSubscription>()
.AddTypeExtension<OrderSubscription>()
Expand All @@ -52,16 +55,21 @@ public static IApplicationBuilder UseGraphQLResolver(this IApplicationBuilder ap
{
app.UseEndpoints(endpoints =>
{
_ = endpoints.MapGraphQL().WithOptions(new HotChocolate.AspNetCore.GraphQLServerOptions
_ = endpoints.MapGraphQL()
.WithOptions(new HotChocolate.AspNetCore.GraphQLServerOptions
{
EnableSchemaRequests = !env.IsProduction(),
EnableSchemaRequests = env.IsDevelopment(),
EnableBatching = true,
EnableMultipartRequests = true,
EnforceMultipartRequestsPreflightHeader = true,
Tool =
{
Title = "ThumbezaTech.Leads",
Enable = env.IsDevelopment(),
DisableTelemetry = env.IsProduction(),
}
});

endpoints.MapGet("/", context =>
{
context.Response.Redirect("/graphql", true);
Expand Down
Loading

0 comments on commit ec9efd8

Please sign in to comment.