Skip to content

Commit

Permalink
IHS-88 IHS-89 Change message format (#90)
Browse files Browse the repository at this point in the history
* change NewImage and CategorizedNewImage models

* pass original image name

* upd ci action
  • Loading branch information
adedw authored Oct 1, 2024
1 parent 8b8cc76 commit ca6d3ae
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 21 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/publish-containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:

env:
REGISTRY: ghcr.io
ORGANIZATION: baklanov-soft

jobs:
publish-containers:
Expand All @@ -20,9 +19,9 @@ jobs:
- linux/arm64/v8
include:
- dockerfile: ./src/ImageHosting.Storage.WebApi/Dockerfile
image: ${{ env.REGISTRY }}/${{ env.ORGANIZATION }}/image-hosting-storage-webapi
image: ghcr.io/baklanov-soft/image-hosting-storage-webapi
- dockerfile: ./src/ImageHosting.Storage.Tagger/Dockerfile
image: ${{ env.REGISTRY }}/${{ env.ORGANIZATION }}/image-hosting-storage-tagger
image: ghcr.io/baklanov-soft/image-hosting-storage-tagger

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"minio": "StartWithoutDebugging",
"postgres": "StartWithoutDebugging",
"resizer": "StartWithoutDebugging",
"image-tagger": "StartWithoutDebugging",
"image-tagger": "DoNotStart",
"recognizer": "StartWithoutDebugging",
"storage-webapi": "DoNotStart"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System.Text.Json.Serialization;
using ImageHosting.Storage.Domain.ValueTypes;

namespace ImageHosting.Storage.Domain.Messages;

public class CategorizedNewImage
{
[JsonPropertyName("imageId")] public required ImageId ImageId { get; init; }
[JsonPropertyName("image")] public required NewImage Image { get; init; }
[JsonPropertyName("categories")] public required Dictionary<string, double> Categories { get; init; }
}
10 changes: 4 additions & 6 deletions src/ImageHosting.Storage.Domain/Messages/NewImage.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using ImageHosting.Storage.Domain.ValueTypes;
using System.Text.Json.Serialization;

namespace ImageHosting.Storage.Domain.Messages;

public class NewImage
{
[JsonPropertyName("bucket")] public UserId Bucket { get; set; }
[JsonIgnore] public ImageId ImageId { get; set; }

[JsonPropertyName("prefix")] public string Prefix => ImageId.ToString();
[JsonPropertyName("image")] public string ImageName => "original.jpg";
public required UserId Bucket { get; init; }
public string Prefix => ImageId.ToString();
public required string Name { get; init; }
public required ImageId ImageId { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using ImageHosting.Storage.Domain.Messages;
using ImageHosting.Storage.Domain.ValueTypes;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImageHosting.Storage.Domain.Serialization;

public class NewImageJsonConverter : JsonConverter<NewImage>
{
public override NewImage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType is not JsonTokenType.StartObject)
throw new JsonException("Expected start of object");

UserId? bucket = null;
ImageId? prefix = null;
string? name = null;

while (reader.Read())
{
if (reader.TokenType is JsonTokenType.EndObject)
break;

if (reader.TokenType is JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read(); // Move to the value

switch (propertyName)
{
case "bucket":
bucket = JsonSerializer.Deserialize<UserId>(ref reader, options);
break;

case "prefix":
prefix = JsonSerializer.Deserialize<ImageId>(ref reader, options);
break;

case "name":
name = reader.GetString();
break;

default:
throw new JsonException($"Unexpected property {propertyName}");
}
}
}

if (!bucket.HasValue || name == null || !prefix.HasValue)
{
throw new JsonException("Missing required properties");
}

return new NewImage
{
Bucket = bucket.Value,
Name = name,
ImageId = prefix.Value,
};
}

public override void Write(Utf8JsonWriter writer, NewImage value, JsonSerializerOptions options)
{
writer.WriteStartObject();

writer.WritePropertyName("bucket");
JsonSerializer.Serialize(writer, value.Bucket, options);

writer.WritePropertyName("prefix");
writer.WriteStringValue(value.Prefix);

writer.WritePropertyName("name");
writer.WriteStringValue(value.Name);

writer.WriteEndObject();
}
}
2 changes: 1 addition & 1 deletion src/ImageHosting.Storage.Tagger/AssignTagsConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class AssignTagsConsumer(IAssignTagsService assignTagsService) : IConsume
public Task Consume(ConsumeContext<Batch<CategorizedNewImage>> context)
{
var messages = context.Message
.GroupBy(consumeContext => consumeContext.Message.ImageId,
.GroupBy(consumeContext => consumeContext.Message.Image.ImageId,
consumeContext => consumeContext.Message.Categories)
.ToDictionary(grouping => grouping.Key,
grouping => grouping.SelectMany(categories => categories)
Expand Down
10 changes: 9 additions & 1 deletion src/ImageHosting.Storage.Tagger/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CommunityToolkit.Diagnostics;
using Confluent.Kafka;
using ImageHosting.Storage.Domain.Messages;
using ImageHosting.Storage.Domain.Serialization;
using ImageHosting.Storage.Infrastructure.Extensions.DependencyInjection;
using ImageHosting.Storage.Tagger;
using ImageHosting.Storage.Tagger.Options;
Expand All @@ -15,7 +16,14 @@

builder.Services.AddMassTransit(massTransit =>
{
massTransit.UsingInMemory();
massTransit.UsingInMemory((context, configurator) =>
{
configurator.ConfigureJsonSerializerOptions(jsonSerializerOptions =>
{
jsonSerializerOptions.Converters.Add(new NewImageJsonConverter());
return jsonSerializerOptions;
});
});

massTransit.AddRider(rider =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public async Task<ImageUploadedDTO> UploadAsync(UserId userId, ImageId imageId,
fileUploadCommandFactory.CreateCommand(userId, imageId, formFile.Length, formFile.ContentType, stream);
var metadataUploadCommand =
metadataUploadCommandFactory.CreateCommand(userId, imageId, formFile.FileName, hidden, uploadedAt);
var publishNewMessageCommand = publishNewMessageCommandFactory.CreateCommand(userId, imageId);
var publishNewMessageCommand = publishNewMessageCommandFactory.CreateCommand(userId, imageId, formFile.FileName);

var commands = new RollbackCommands();
commands.Add(fileUploadCommand);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ namespace ImageHosting.Storage.WebApi.Features.Images.Services;

public interface IPublishNewMessageCommandFactory
{
IRollbackCommand CreateCommand(UserId userId, ImageId imageId);
IRollbackCommand CreateCommand(UserId userId, ImageId imageId, string imageName);
}

public class PublishNewMessageCommandFactory(INewImageProducer newImageProducer) : IPublishNewMessageCommandFactory
{
public IRollbackCommand CreateCommand(UserId userId, ImageId imageId)
public IRollbackCommand CreateCommand(UserId userId, ImageId imageId, string name)
{
return new PublishNewMessageCommand(newImageProducer, userId, imageId);
return new PublishNewMessageCommand(newImageProducer, userId, imageId, name);
}
}

public class PublishNewMessageCommand(INewImageProducer newImageProducer, UserId userId, ImageId imageId) : IRollbackCommand
public class PublishNewMessageCommand(INewImageProducer newImageProducer, UserId userId, ImageId imageId, string name) : IRollbackCommand
{
public Task ExecuteAsync(CancellationToken cancellationToken = default)
{
return newImageProducer.SendAsync(new NewImage { Bucket = userId, ImageId = imageId }, cancellationToken);
return newImageProducer.SendAsync(new NewImage { Bucket = userId, ImageId = imageId, Name = name }, cancellationToken);
}

public Task RollbackAsync(CancellationToken cancellationToken = default)
Expand Down
10 changes: 9 additions & 1 deletion src/ImageHosting.Storage.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Hellang.Middleware.ProblemDetails.Mvc;
using ImageHosting.Storage.Application.Services;
using ImageHosting.Storage.Domain.Messages;
using ImageHosting.Storage.Domain.Serialization;
using ImageHosting.Storage.Domain.ValueTypes;
using ImageHosting.Storage.Infrastructure.DbContexts;
using ImageHosting.Storage.Infrastructure.Extensions.DependencyInjection;
Expand Down Expand Up @@ -58,7 +59,14 @@

builder.Services.AddMassTransit(massTransit =>
{
massTransit.UsingInMemory();
massTransit.UsingInMemory((context, configurator) =>
{
configurator.ConfigureJsonSerializerOptions(jsonSerializerOptions =>
{
jsonSerializerOptions.Converters.Add(new NewImageJsonConverter());
return jsonSerializerOptions;
});
});

massTransit.AddRider(rider =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Reflection;
using Xunit.Abstractions;

namespace ImageHosting.Storage.UnitTests.Extensions;

public static class ObjectExtensions
{
public static void PrintProperties(this ITestOutputHelper outputHelper, object? obj)
{
if (obj == null)
{
outputHelper.WriteLine("null");
return;
}

var type = obj.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

foreach (var property in properties)
{
var value = property.GetValue(obj) ?? "null";
outputHelper.WriteLine($"{property.Name}: {value}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace ImageHosting.Storage.UnitTests.Serialization;

using ImageHosting.Storage.Domain.Messages;
using ImageHosting.Storage.Domain.Serialization;
using ImageHosting.Storage.Domain.ValueTypes;
using ImageHosting.Storage.UnitTests.Extensions;
using System.Text.Json;
using Xunit;
using Xunit.Abstractions;

public class NewImageJsonConverterTests
{
private readonly ITestOutputHelper _testOutputHelper;
private readonly JsonSerializerOptions _jsonSerializerOptions;

public NewImageJsonConverterTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
_jsonSerializerOptions = new JsonSerializerOptions
{
Converters = { new NewImageJsonConverter() },
WriteIndented = false
};
}

[Fact]
public void Serialize_NewImage_returns_correct_JSON()
{
var newImage = new NewImage
{
Bucket = new UserId(Guid.Empty),
ImageId = new ImageId(Guid.Empty),
Name = "image.jpg",
};

var json = JsonSerializer.Serialize(newImage, _jsonSerializerOptions);

_testOutputHelper.WriteLine("JSON string: {0}", json);
json.Should().Contain("\"bucket\":\"00000000-0000-0000-0000-000000000000\"");
json.Should().Contain("\"prefix\":\"00000000-0000-0000-0000-000000000000\"");
json.Should().Contain("\"name\":\"image.jpg\"");
json.Should().NotContain("imageId"); // Ensure ImageId is ignored
}

[Fact]
public void Deserialize_valid_JSON_returns_NewImage()
{
var json = @"{
""bucket"": ""00000000-0000-0000-0000-000000000000"",
""prefix"": ""00000000-0000-0000-0000-000000000000"",
""name"": ""image.jpg""
}";

var newImage = JsonSerializer.Deserialize<NewImage>(json, _jsonSerializerOptions);

_testOutputHelper.PrintProperties(newImage);
Assert.NotNull(newImage);
Assert.Equal(new UserId(Guid.Empty), newImage.Bucket);
Assert.Equal("image.jpg", newImage.Name);
Assert.Equal(new ImageId(Guid.Empty), newImage.ImageId);
}

[Fact]
public void Deserialize_invalid_JSON_throws_JSON_exception()
{
// Missing required prefix field
var invalidJson = @"{
""bucket"": ""00000000-0000-0000-0000-000000000000"",
""image"": ""image.jpg""
}";

_testOutputHelper.WriteLine("JSON string: {0}", invalidJson);
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<NewImage>(invalidJson, _jsonSerializerOptions));
}
}

0 comments on commit ca6d3ae

Please sign in to comment.