Skip to content

Commit

Permalink
test: fix the livedocs models to support serialization: you cannot ha…
Browse files Browse the repository at this point in the history
…ve nullable, required properties
  • Loading branch information
rbeauchamp committed Aug 20, 2024
1 parent 48bd6aa commit 17cf17a
Show file tree
Hide file tree
Showing 8 changed files with 549 additions and 11 deletions.
3 changes: 2 additions & 1 deletion example/LiveDocs.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

var builder = DistributedApplication.CreateBuilder(args);

var redis = builder.AddRedis("redis");
var redis = builder.AddRedis("redis", 6379)
.WithEndpoint(port: 6380, targetPort: 6379, name: "redis-endpoint");

// Add SQL Server
var password = builder.AddParameter("sqlpassword", secret: true);
Expand Down
18 changes: 18 additions & 0 deletions example/LiveDocs.GraphQLApi/Data/LiveDocsDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using LiveDocs.GraphQLApi.Models.Entities;
using LiveDocs.GraphQLApi.Models.Shared;
using Microsoft.EntityFrameworkCore;

namespace LiveDocs.GraphQLApi.Data;
Expand All @@ -8,6 +9,7 @@ public class LiveDocsDbContext(DbContextOptions options) : DbContext(options)
public DbSet<User> Users => Set<User>();
public DbSet<Workspace> Workspaces => Set<Workspace>();
public DbSet<LiveDoc> LiveDocs => Set<LiveDoc>();
public DbSet<Hero> Heroes => Set<Hero>();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Expand All @@ -17,6 +19,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
ConfigureUserEntity(modelBuilder);
ConfigureWorkspaceEntity(modelBuilder);
ConfigureLiveDocEntity(modelBuilder);
ConfigureHeroEntity(modelBuilder);
}

private static void ConfigureHeroEntity(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Hero>(entity =>
{
entity.ToTable("Heroes");
entity.Property(e => e.Id)
.ValueGeneratedNever();
entity.Property(e => e.UpdatedAt)
.IsRequired()
.HasColumnType("datetimeoffset(7)");
});
}

private static void ConfigureUserEntity(ModelBuilder modelBuilder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public string FullName
[JwtFormat(AllowNull = true)]
[Trim]
[MaxLength(2000)]
public required string? JwtAccessToken { get; init; }
public string? JwtAccessToken { get; init; }

/// <summary>
/// The client-assigned identifier of the workspace to which the user belongs.
Expand Down
37 changes: 37 additions & 0 deletions example/LiveDocs.GraphQLApi/Models/Shared/Hero.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using HotChocolate;
using LiveDocs.GraphQLApi.Validations;
using RxDBDotNet.Documents;

namespace LiveDocs.GraphQLApi.Models.Shared;

[GraphQLName("Hero")]
public class Hero : IReplicatedDocument
{
/// <inheritdoc />
[Required]
[NotDefault]
public required Guid Id { get; init; }

/// <inheritdoc />
[Required]
public required bool IsDeleted { get; set; }

/// <inheritdoc />
[NotMapped]
public List<string>? Topics { get; init; }

/// <inheritdoc />
[Required]
[NotDefault]
public required DateTimeOffset UpdatedAt { get; set; }

[Required]
[MaxLength(100)]
public required string Name { get; set; }

[Required]
[MaxLength(30)]
public required string Color { get; set; }
}
93 changes: 93 additions & 0 deletions example/LiveDocs.GraphQLApi/Services/HeroService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using LiveDocs.GraphQLApi.Data;
using LiveDocs.GraphQLApi.Models.Shared;
using Microsoft.EntityFrameworkCore;
using RxDBDotNet.Repositories;
using RxDBDotNet.Services;

namespace LiveDocs.GraphQLApi.Services;

public class HeroService : IDocumentService<Hero>
{
private readonly LiveDocsDbContext _dbContext;
private readonly IEventPublisher _eventPublisher;
private readonly List<Hero> _pendingEvents = [];

public HeroService(LiveDocsDbContext dbContext, IEventPublisher eventPublisher)
{
_dbContext = dbContext;
_eventPublisher = eventPublisher;
}

public IQueryable<Hero> GetQueryableDocuments()
{
return _dbContext.Heroes.AsNoTracking();
}

public Task<List<Hero>> ExecuteQueryAsync(IQueryable<Hero> query, CancellationToken cancellationToken)
{
return query.ToListAsync(cancellationToken);
}

public Task<Hero?> GetDocumentByIdAsync(Guid id, CancellationToken cancellationToken)
{
return _dbContext.Heroes.Where(d => d.Id == id).SingleOrDefaultAsync(cancellationToken);
}

public async Task<Hero> CreateDocumentAsync(Hero document, CancellationToken cancellationToken)
{
await _dbContext.Set<Hero>()
.AddAsync(document, cancellationToken);

_pendingEvents.Add(document);

return document;
}

public async Task<Hero> UpdateDocumentAsync(Hero document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);

var heroToUpdate = await GetDocumentByIdAsync(document.Id, cancellationToken)
?? throw new InvalidOperationException($"Entity with a ReplicateDocumentId of {document.Id} not found for update.");

heroToUpdate.Name = document.Name;
heroToUpdate.Color = document.Color;
heroToUpdate.UpdatedAt = DateTimeOffset.Now;

_pendingEvents.Add(heroToUpdate);

return heroToUpdate;
}

public async Task<Hero> MarkAsDeletedAsync(Hero document, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);

var heroToDelete = await GetDocumentByIdAsync(document.Id, cancellationToken)
?? throw new InvalidOperationException($"Entity with a ReplicateDocumentId of {document.Id} not found for update.");

heroToDelete.IsDeleted = true;
heroToDelete.UpdatedAt = DateTimeOffset.Now;

_pendingEvents.Add(heroToDelete);

return heroToDelete;
}

public bool AreDocumentsEqual(Hero document1, Hero document2)
{
return true;
}

public async Task SaveChangesAsync(CancellationToken cancellationToken)
{
await _dbContext.SaveChangesAsync(cancellationToken);

foreach (var doc in _pendingEvents)
{
await _eventPublisher.PublishDocumentChangedEventAsync(doc, cancellationToken);
}

_pendingEvents.Clear();
}
}
8 changes: 6 additions & 2 deletions example/LiveDocs.GraphQLApi/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using LiveDocs.GraphQLApi.Data;
using LiveDocs.GraphQLApi.Infrastructure;
using LiveDocs.GraphQLApi.Models.Replication;
using LiveDocs.GraphQLApi.Models.Shared;
using LiveDocs.GraphQLApi.Services;
using LiveDocs.ServiceDefaults;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -31,6 +32,7 @@ public virtual void ConfigureServices(

// Add services to the container
services.AddProblemDetails()
.AddScoped<IDocumentService<Hero>, HeroService>()
.AddScoped<IDocumentService<ReplicatedUser>, UserService>()
.AddScoped<IDocumentService<ReplicatedWorkspace>, WorkspaceService>()
.AddScoped<IDocumentService<ReplicatedLiveDoc>, LiveDocService>();
Expand All @@ -42,7 +44,9 @@ public virtual void ConfigureServices(
{
options.AddDefaultPolicy(corsPolicyBuilder =>
{
corsPolicyBuilder.WithOrigins("http://localhost:3000")
corsPolicyBuilder.WithOrigins(
"http://localhost:3000",
"http://127.0.0.1:8888")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
Expand Down Expand Up @@ -71,10 +75,10 @@ public static void ConfigureDefaultGraphQLServer(IServiceCollection services)
// has already added their own root query type.
.AddQueryType<Query>()
.AddReplicationServer()
.RegisterService<IDocumentService<ReplicatedWorkspace>>()
.AddReplicatedDocument<ReplicatedUser>()
.AddReplicatedDocument<ReplicatedWorkspace>()
.AddReplicatedDocument<ReplicatedLiveDoc>()
.AddReplicatedDocument<Hero>()
.AddRedisSubscriptions(provider => provider.GetRequiredService<IConnectionMultiplexer>())
.AddSubscriptionDiagnostics();
}
Expand Down
6 changes: 3 additions & 3 deletions example/livedocs-client/src/lib/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const workspaceSchemaLiteral = {
type: 'boolean',
},
},
required: ['id', 'name', 'updatedAt', 'isDeleted'],
required: ['id', 'name', 'updatedAt'],
} as const;

// User Schema
Expand Down Expand Up @@ -66,7 +66,7 @@ const userSchemaLiteral = {
type: 'boolean',
},
},
required: ['id', 'firstName', 'lastName', 'email', 'workspaceId', 'updatedAt', 'isDeleted'],
required: ['id', 'firstName', 'lastName', 'email', 'workspaceId', 'updatedAt'],
} as const;

// LiveDoc Schema
Expand Down Expand Up @@ -101,7 +101,7 @@ const liveDocSchemaLiteral = {
type: 'boolean',
},
},
required: ['id', 'content', 'ownerId', 'workspaceId', 'updatedAt', 'isDeleted'],
required: ['id', 'content', 'ownerId', 'workspaceId', 'updatedAt'],
} as const;

// Create typed schemas
Expand Down
Loading

0 comments on commit 17cf17a

Please sign in to comment.