Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/Octokit.Webhooks/Converter/ChangesFieldValueChangeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace Octokit.Webhooks.Converter;

using Octokit.Webhooks.Models.ProjectsV2ItemEvent;

public class ChangesFieldValueChangeConverter : JsonConverter<ChangesFieldValueChangeBase>
{
public override ChangesFieldValueChangeBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader)
{
case { TokenType: JsonTokenType.StartObject }:
var changeObject = JsonSerializer.Deserialize<ChangesFieldValueChange>(ref reader, options);
return changeObject;
case { TokenType: JsonTokenType.String }:
return new ChangesFieldValueScalarChange { StringValue = reader.GetString() };
case { TokenType: JsonTokenType.Null }:
return null;
case { TokenType: JsonTokenType.Number }:
return new ChangesFieldValueScalarChange { NumericValue = reader.GetDecimal() };
default:
throw new JsonException($"Invalid JsonTokenType {reader.TokenType}");
}
}

public override void Write(Utf8JsonWriter writer, ChangesFieldValueChangeBase value, JsonSerializerOptions options)
{
switch (value)
{
case ChangesFieldValueScalarChange { StringValue: not null } scalarChange:
writer.WriteStringValue(scalarChange.StringValue);
break;
case ChangesFieldValueScalarChange { NumericValue: not null } scalarChange:
writer.WriteNumberValue(scalarChange.NumericValue.Value);
break;
case ChangesFieldValueScalarChange:
writer.WriteNullValue();
break;
case ChangesFieldValueChange change:
JsonSerializer.Serialize(writer, change, options);
break;
default:
writer.WriteNullValue();
break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public sealed record ChangesFieldValue
public long ProjectNumber { get; init; }

[JsonPropertyName("from")]
public ChangesFieldValueChange From { get; init; } = null!;
public ChangesFieldValueChangeBase From { get; init; } = null!;

[JsonPropertyName("to")]
public ChangesFieldValueChange To { get; init; } = null!;
public ChangesFieldValueChangeBase To { get; init; } = null!;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Octokit.Webhooks.Models.ProjectsV2ItemEvent;

[PublicAPI]
public sealed record ChangesFieldValueChange
public sealed record ChangesFieldValueChange : ChangesFieldValueChangeBase
{
[JsonPropertyName("id")]
public string Id { get; init; } = null!;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Octokit.Webhooks.Models.ProjectsV2ItemEvent;

[JsonConverter(typeof(ChangesFieldValueChangeConverter))]
public abstract record ChangesFieldValueChangeBase
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Octokit.Webhooks.Models.ProjectsV2ItemEvent;

[PublicAPI]
public sealed record ChangesFieldValueScalarChange : ChangesFieldValueChangeBase
{
public string? StringValue { get; init; }

public decimal? NumericValue { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace Octokit.Webhooks.Test.Converter;

using System.Text.Json;
using System.Text.Json.Serialization;
using AwesomeAssertions;
using Octokit.Webhooks.Converter;
using Octokit.Webhooks.Models.ProjectsV2ItemEvent;
using Xunit;

public class ChangesFieldValueChangeConverterTests(ITestOutputHelper output)
{
private readonly JsonSerializerOptions options = new()
{
WriteIndented = true,
Converters =
{
new ChangesFieldValueChangeConverter(),
},
};

[Fact]
public void Roundtrip()
{
var test = new TestObject
{
AsString = new ChangesFieldValueScalarChange { StringValue = "Hello world" },
AsNumber = new ChangesFieldValueScalarChange { NumericValue = 3.1415926m },
AsObject = new ChangesFieldValueChange
{
Color = "color",
Description = "description",
Id = "12345",
Name = "Name",
},
};

var serialized = JsonSerializer.Serialize(test, this.options);
output.WriteLine(serialized);

var deserialized = JsonSerializer.Deserialize<TestObject>(serialized, this.options);

deserialized.Should().NotBeNull();
deserialized.AsString.Should().NotBeNull().And.BeOfType<ChangesFieldValueScalarChange>();
deserialized.AsNumber.Should().NotBeNull().And.BeOfType<ChangesFieldValueScalarChange>();

(deserialized.AsString as ChangesFieldValueScalarChange)?.StringValue.Should().Be("Hello world");
(deserialized.AsString as ChangesFieldValueScalarChange)?.NumericValue.Should().BeNull();

(deserialized.AsNumber as ChangesFieldValueScalarChange)?.StringValue.Should().BeNull();
(deserialized.AsNumber as ChangesFieldValueScalarChange)?.NumericValue.Should().Be(3.1415926m);

deserialized.AsObject.Should().NotBeNull().And.BeOfType<ChangesFieldValueChange>();
var changeObject = deserialized.AsObject.As<ChangesFieldValueChange>();

changeObject.Color.Should().Be("color");
changeObject.Description.Should().Be("description");
changeObject.Id.Should().Be("12345");
changeObject.Name.Should().Be("Name");
}

internal sealed class TestObject
{
[JsonPropertyName("as_string")]
public ChangesFieldValueChangeBase AsString { get; init; } = null!;

[JsonPropertyName("as_number")]
public ChangesFieldValueChangeBase AsNumber { get; init; } = null!;

[JsonPropertyName("as_object")]
public ChangesFieldValueChangeBase AsObject { get; init; } = null!;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"action": "edited",
"projects_v2_item": {
"id": 5678510,
"node_id": "PVTI_lADOAWcTxs07G84AVqWu",
"project_node_id": "PVT_kwDOAWcTxs07Gw",
"content_node_id": "DI_lADOAWcTxs07G84AIjOy",
"content_type": "DraftIssue",
"creator": {
"login": "wolfy1339",
"id": 4595477,
"node_id": "MDQ6VXNlcjQ1OTU0Nzc=",
"avatar_url": "https://avatars.githubusercontent.com/u/4595477?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/wolfy1339",
"html_url": "https://github.com/wolfy1339",
"followers_url": "https://api.github.com/users/wolfy1339/followers",
"following_url": "https://api.github.com/users/wolfy1339/following{/other_user}",
"gists_url": "https://api.github.com/users/wolfy1339/gists{/gist_id}",
"starred_url": "https://api.github.com/users/wolfy1339/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/wolfy1339/subscriptions",
"organizations_url": "https://api.github.com/users/wolfy1339/orgs",
"repos_url": "https://api.github.com/users/wolfy1339/repos",
"events_url": "https://api.github.com/users/wolfy1339/events{/privacy}",
"received_events_url": "https://api.github.com/users/wolfy1339/received_events",
"type": "User",
"site_admin": false
},
"created_at": "2022-06-08T20:34:26Z",
"updated_at": "2022-06-08T20:34:26Z",
"archived_at": null
},
"changes": {
"field_value": {
"field_node_id": "PVTF_lADOAEzhac4AjFojzgbzsKM",
"field_type": "date",
"field_name": "Start Date",
"project_number": 233,
"from": null,
"to": "2025-08-01T00:00:00+00:00"
}
},
"organization": {
"login": "Octocoders",
"id": 38302899,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5",
"url": "https://api.github.com/orgs/Octocoders",
"repos_url": "https://api.github.com/orgs/Octocoders/repos",
"events_url": "https://api.github.com/orgs/Octocoders/events",
"hooks_url": "https://api.github.com/orgs/Octocoders/hooks",
"issues_url": "https://api.github.com/orgs/Octocoders/issues",
"members_url": "https://api.github.com/orgs/Octocoders/members{/member}",
"public_members_url": "https://api.github.com/orgs/Octocoders/public_members{/member}",
"avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4",
"description": ""
},
"sender": {
"login": "wolfy1339",
"id": 4595477,
"node_id": "MDQ6VXNlcjQ1OTU0Nzc=",
"avatar_url": "https://avatars.githubusercontent.com/u/4595477?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/wolfy1339",
"html_url": "https://github.com/wolfy1339",
"followers_url": "https://api.github.com/users/wolfy1339/followers",
"following_url": "https://api.github.com/users/wolfy1339/following{/other_user}",
"gists_url": "https://api.github.com/users/wolfy1339/gists{/gist_id}",
"starred_url": "https://api.github.com/users/wolfy1339/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/wolfy1339/subscriptions",
"organizations_url": "https://api.github.com/users/wolfy1339/orgs",
"repos_url": "https://api.github.com/users/wolfy1339/repos",
"events_url": "https://api.github.com/users/wolfy1339/events{/privacy}",
"received_events_url": "https://api.github.com/users/wolfy1339/received_events",
"type": "User",
"site_admin": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"action": "edited",
"projects_v2_item": {
"id": 5678510,
"node_id": "PVTI_lADOAWcTxs07G84AVqWu",
"project_node_id": "PVT_kwDOAWcTxs07Gw",
"content_node_id": "DI_lADOAWcTxs07G84AIjOy",
"content_type": "DraftIssue",
"creator": {
"login": "wolfy1339",
"id": 4595477,
"node_id": "MDQ6VXNlcjQ1OTU0Nzc=",
"avatar_url": "https://avatars.githubusercontent.com/u/4595477?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/wolfy1339",
"html_url": "https://github.com/wolfy1339",
"followers_url": "https://api.github.com/users/wolfy1339/followers",
"following_url": "https://api.github.com/users/wolfy1339/following{/other_user}",
"gists_url": "https://api.github.com/users/wolfy1339/gists{/gist_id}",
"starred_url": "https://api.github.com/users/wolfy1339/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/wolfy1339/subscriptions",
"organizations_url": "https://api.github.com/users/wolfy1339/orgs",
"repos_url": "https://api.github.com/users/wolfy1339/repos",
"events_url": "https://api.github.com/users/wolfy1339/events{/privacy}",
"received_events_url": "https://api.github.com/users/wolfy1339/received_events",
"type": "User",
"site_admin": false
},
"created_at": "2022-06-08T20:34:26Z",
"updated_at": "2022-06-08T20:34:26Z",
"archived_at": null
},
"changes": {
"field_value": {
"field_node_id": "PVTF_lADOAEzhac4AjFojzgyEIsU",
"field_type": "number",
"field_name": "tempNumber",
"project_number": 233,
"from": null,
"to": 123.456
}
},
"organization": {
"login": "Octocoders",
"id": 38302899,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5",
"url": "https://api.github.com/orgs/Octocoders",
"repos_url": "https://api.github.com/orgs/Octocoders/repos",
"events_url": "https://api.github.com/orgs/Octocoders/events",
"hooks_url": "https://api.github.com/orgs/Octocoders/hooks",
"issues_url": "https://api.github.com/orgs/Octocoders/issues",
"members_url": "https://api.github.com/orgs/Octocoders/members{/member}",
"public_members_url": "https://api.github.com/orgs/Octocoders/public_members{/member}",
"avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4",
"description": ""
},
"sender": {
"login": "wolfy1339",
"id": 4595477,
"node_id": "MDQ6VXNlcjQ1OTU0Nzc=",
"avatar_url": "https://avatars.githubusercontent.com/u/4595477?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/wolfy1339",
"html_url": "https://github.com/wolfy1339",
"followers_url": "https://api.github.com/users/wolfy1339/followers",
"following_url": "https://api.github.com/users/wolfy1339/following{/other_user}",
"gists_url": "https://api.github.com/users/wolfy1339/gists{/gist_id}",
"starred_url": "https://api.github.com/users/wolfy1339/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/wolfy1339/subscriptions",
"organizations_url": "https://api.github.com/users/wolfy1339/orgs",
"repos_url": "https://api.github.com/users/wolfy1339/repos",
"events_url": "https://api.github.com/users/wolfy1339/events{/privacy}",
"received_events_url": "https://api.github.com/users/wolfy1339/received_events",
"type": "User",
"site_admin": false
}
}
4 changes: 3 additions & 1 deletion test/Octokit.Webhooks.Test/WebhookEventProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ public class WebhookEventProcessorTests

[Theory]
[ClassData(typeof(WebhookEventProcessorTestsData))]
public void CanDeserialize(string @event, string payload, Type expectedType)
public void CanDeserialize(string @event, string testName, string payload, Type expectedType)
{
// Only used to make it easier to differentiate test cases for the same event without looking at whole payload
_ = testName;
var headers = new WebhookHeaders
{
Event = @event,
Expand Down
6 changes: 3 additions & 3 deletions test/Octokit.Webhooks.Test/WebhookEventProcessorTestsData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace Octokit.Webhooks.Test;
using Octokit.Webhooks.TestUtils;
using Xunit;

public class WebhookEventProcessorTestsData : IEnumerable<TheoryDataRow<string, string, Type>>
public class WebhookEventProcessorTestsData : IEnumerable<TheoryDataRow<string, string, string, Type>>
{
public IEnumerator<TheoryDataRow<string, string, Type>> GetEnumerator()
public IEnumerator<TheoryDataRow<string, string, string, Type>> GetEnumerator()
{
var resourcesDirectory = ResourceUtils.GetResources();
var files = Directory.GetFiles(resourcesDirectory, "*.json", SearchOption.AllDirectories);
Expand All @@ -20,7 +20,7 @@ public IEnumerator<TheoryDataRow<string, string, Type>> GetEnumerator()
var parts = relativeResource.Split(Path.DirectorySeparatorChar);
var expectedType = ClassUtils.GetEventTypeByName(parts[0].ToPascalCase());
var content = ResourceUtils.ReadResource(relativeResource);
yield return new TheoryDataRow<string, string, Type>(parts[0], content, expectedType);
yield return new TheoryDataRow<string, string, string, Type>(parts[0], parts[1], content, expectedType);
}
}

Expand Down
Loading