From 8f2fc216bcc6a5863e7a4ec60a45de4b18bd79d1 Mon Sep 17 00:00:00 2001 From: Jordan Phillips Date: Sun, 19 May 2024 11:41:00 +1000 Subject: [PATCH] feat: raise event on encode status change --- .../interfaces/encode/chips/EncodeStatus.tsx | 24 +++++++++++++++++- .../Events/RaiseEncodeStatusChangedTopic.cs | 25 +++++++++++++++++++ .../Events/EncodeStatusChangedEvent.cs | 13 ++++++++++ .../src/Domain/Aggregates/Encodes/Encode.cs | 14 ++++++++++- .../EncodeStatusChangedSubscription.cs | 23 +++++++++++++++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/Service.Dashboard/src/Application.Components/Encodes/Events/RaiseEncodeStatusChangedTopic.cs create mode 100644 src/Service.Dashboard/src/Application.Contracts/Encodes/Events/EncodeStatusChangedEvent.cs create mode 100644 src/Service.Dashboard/src/HttpApi/Resolvers/Encodes/Subscriptions/EncodeStatusChangedSubscription.cs diff --git a/app/src/components/interfaces/encode/chips/EncodeStatus.tsx b/app/src/components/interfaces/encode/chips/EncodeStatus.tsx index d9a5a3d7..c9d865d0 100644 --- a/app/src/components/interfaces/encode/chips/EncodeStatus.tsx +++ b/app/src/components/interfaces/encode/chips/EncodeStatus.tsx @@ -4,10 +4,11 @@ import type { ChipProps } from '@giantnodes/react' import { Chip } from '@giantnodes/react' import dayjs from 'dayjs' import React from 'react' -import { graphql, useFragment } from 'react-relay' +import { graphql, useFragment, useSubscription } from 'react-relay' const FRAGMENT = graphql` fragment EncodeStatusFragment on Encode { + id status failed_at cancelled_at @@ -15,6 +16,14 @@ const FRAGMENT = graphql` } ` +const SUBSCRIPTION = graphql` + subscription EncodeStatusSubscription($where: EncodeFilterInput) { + encode_status_changed(where: $where) { + ...EncodeSpeedFragment + } + } +` + type EncodeStatusChipProps = { $key: EncodeStatusFragment$key } @@ -22,6 +31,19 @@ type EncodeStatusChipProps = { const EncodeStatus: React.FC = ({ $key }) => { const data = useFragment(FRAGMENT, $key) + useSubscription({ + subscription: SUBSCRIPTION, + variables: { + variables: { + where: { + id: { + eq: data.id, + }, + }, + }, + }, + }) + const color = React.useMemo(() => { switch (data.status) { case 'COMPLETED': diff --git a/src/Service.Dashboard/src/Application.Components/Encodes/Events/RaiseEncodeStatusChangedTopic.cs b/src/Service.Dashboard/src/Application.Components/Encodes/Events/RaiseEncodeStatusChangedTopic.cs new file mode 100644 index 00000000..65536a0f --- /dev/null +++ b/src/Service.Dashboard/src/Application.Components/Encodes/Events/RaiseEncodeStatusChangedTopic.cs @@ -0,0 +1,25 @@ +using Giantnodes.Service.Dashboard.Application.Contracts.Encodes.Events; +using Giantnodes.Service.Dashboard.Domain.Aggregates.Encodes.Repositories; +using HotChocolate.Subscriptions; +using MassTransit; + +namespace Giantnodes.Service.Dashboard.Application.Components.Encodes.Events; + +public class RaiseEncodeStatusChangedTopic : IConsumer +{ + private readonly IEncodeRepository _repository; + private readonly ITopicEventSender _sender; + + public RaiseEncodeStatusChangedTopic(IEncodeRepository repository, ITopicEventSender sender) + { + _repository = repository; + _sender = sender; + } + + public async Task Consume(ConsumeContext context) + { + var encode = await _repository.SingleAsync(x => x.Id == context.Message.EncodeId); + + await _sender.SendAsync(nameof(EncodeStatusChangedEvent), encode, context.CancellationToken); + } +} \ No newline at end of file diff --git a/src/Service.Dashboard/src/Application.Contracts/Encodes/Events/EncodeStatusChangedEvent.cs b/src/Service.Dashboard/src/Application.Contracts/Encodes/Events/EncodeStatusChangedEvent.cs new file mode 100644 index 00000000..d95f97f9 --- /dev/null +++ b/src/Service.Dashboard/src/Application.Contracts/Encodes/Events/EncodeStatusChangedEvent.cs @@ -0,0 +1,13 @@ +using Giantnodes.Infrastructure.Domain.Events; +using Giantnodes.Service.Dashboard.Domain.Shared.Enums; + +namespace Giantnodes.Service.Dashboard.Application.Contracts.Encodes.Events; + +public sealed record EncodeStatusChangedEvent : DomainEvent +{ + public required Guid EncodeId { get; init; } + + public required EncodeStatus FromStatus { get; init; } + + public required EncodeStatus ToStatus { get; init; } +} \ No newline at end of file diff --git a/src/Service.Dashboard/src/Domain/Aggregates/Encodes/Encode.cs b/src/Service.Dashboard/src/Domain/Aggregates/Encodes/Encode.cs index f9a764c8..16458cdb 100644 --- a/src/Service.Dashboard/src/Domain/Aggregates/Encodes/Encode.cs +++ b/src/Service.Dashboard/src/Domain/Aggregates/Encodes/Encode.cs @@ -16,6 +16,8 @@ public class Encode : AggregateRoot, ITimestampableEntity { private readonly List _snapshots = new(); + private EncodeStatus _status; + /// /// The file being encoded. /// @@ -34,7 +36,17 @@ public class Encode : AggregateRoot, ITimestampableEntity /// /// The current status of the encoding process. /// - public EncodeStatus Status { get; private set; } + public EncodeStatus Status + { + get => _status; + private set + { + if (Status != value) + DomainEvents.Add(new EncodeStatusChangedEvent { EncodeId = Id, FromStatus = Status, ToStatus = value }); + + _status = value; + } + } /// /// The machine performing the encoding. diff --git a/src/Service.Dashboard/src/HttpApi/Resolvers/Encodes/Subscriptions/EncodeStatusChangedSubscription.cs b/src/Service.Dashboard/src/HttpApi/Resolvers/Encodes/Subscriptions/EncodeStatusChangedSubscription.cs new file mode 100644 index 00000000..76f2632c --- /dev/null +++ b/src/Service.Dashboard/src/HttpApi/Resolvers/Encodes/Subscriptions/EncodeStatusChangedSubscription.cs @@ -0,0 +1,23 @@ +using Giantnodes.Service.Dashboard.Application.Contracts.Encodes.Events; +using Giantnodes.Service.Dashboard.Domain.Aggregates.Encodes; +using Giantnodes.Service.Dashboard.Persistence.DbContexts; +using Microsoft.EntityFrameworkCore; + +namespace Giantnodes.Service.Dashboard.HttpApi.Resolvers.Encodes.Subscriptions; + +[ExtendObjectType(OperationTypeNames.Subscription)] +public class EncodeStatusChangedSubscription +{ + [Subscribe] + [Topic(nameof(EncodeStatusChangedEvent))] + [UseSingleOrDefault] + [UseProjection] + [UseFiltering] + [UseSorting] + public IQueryable EncodeStatusChanged( + [Service] ApplicationDbContext database, + [EventMessage] Encode encode) + { + return database.Encodes.Where(x => x.Id == encode.Id).AsNoTracking(); + } +} \ No newline at end of file