Skip to content

Commit 2267eda

Browse files
Fix duplicates moderation response
1 parent bc8c9bb commit 2267eda

File tree

3 files changed

+140
-5
lines changed

3 files changed

+140
-5
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using CloudinaryDotNet.Actions;
2+
using NUnit.Framework;
3+
4+
namespace CloudinaryDotNet.Tests.AdminApi
5+
{
6+
[TestFixture]
7+
public class GetResourceTest
8+
{
9+
private const string CommonJsonPrefix = @"
10+
{
11+
""asset_id"": ""8abd06560fc75b3bbe80299b988035b0"",
12+
""public_id"": ""REPLACE_PUBLIC_ID"",
13+
""format"": ""jpg"",
14+
""version"": 1739308959,
15+
""resource_type"": ""image"",
16+
""type"": ""upload"",
17+
""created_at"": ""2025-02-11T21:22:39Z"",
18+
""bytes"": 582249,
19+
""width"": 1000,
20+
""height"": 688,
21+
""url"": ""http://res.cloudinary.com/demo/image/upload/v1739308959/REPLACE_PUBLIC_ID.jpg"",
22+
""secure_url"": ""https://res.cloudinary.com/demo/image/upload/v1739308959/REPLACE_PUBLIC_ID.jpg"",
23+
""moderation"": [
24+
";
25+
26+
private const string CommonJsonSuffix = @"
27+
]
28+
}
29+
";
30+
31+
// "response" is an object containing "moderation_labels"
32+
private const string LabelsModerationBlock = @"
33+
{
34+
""response"": {
35+
""moderation_labels"": [
36+
{
37+
""confidence"": 94.9907455444336,
38+
""name"": ""Suggestive"",
39+
""parent_name"": """"
40+
},
41+
{
42+
""confidence"": 94.9907455444336,
43+
""name"": ""Female Swimwear Or Underwear"",
44+
""parent_name"": ""Suggestive""
45+
}
46+
]
47+
},
48+
""status"": ""rejected"",
49+
""kind"": ""aws_rek"",
50+
""updated_at"": ""2017-08-03T08:26:58Z""
51+
}
52+
";
53+
54+
// "response" is an array of objects (duplicates)
55+
private const string DuplicatesModerationBlock = @"
56+
{
57+
""kind"": ""duplicate"",
58+
""status"": ""rejected"",
59+
""response"": [
60+
{
61+
""public_id"": ""duplicate_id"",
62+
""confidence"": 1.0
63+
}
64+
],
65+
""updated_at"": ""2025-02-07T08:30:29Z""
66+
}
67+
";
68+
69+
[Test]
70+
public void TestGetResourceModerationResponse_WithLabels()
71+
{
72+
var responseData = CommonJsonPrefix
73+
.Replace("REPLACE_PUBLIC_ID", "mhor383ejnw0j6iokmlh")
74+
+ LabelsModerationBlock
75+
+ CommonJsonSuffix;
76+
77+
var localCloudinaryMock = new MockedCloudinary(responseData);
78+
var result = localCloudinaryMock.GetResource("mhor383ejnw0j6iokmlh");
79+
80+
Assert.NotNull(result);
81+
Assert.AreEqual("mhor383ejnw0j6iokmlh", result.PublicId);
82+
Assert.NotNull(result.Moderation);
83+
Assert.IsNotEmpty(result.Moderation);
84+
85+
var firstModeration = result.Moderation[0];
86+
Assert.AreEqual("aws_rek", firstModeration.Kind);
87+
Assert.AreEqual(ModerationStatus.Rejected, firstModeration.Status);
88+
Assert.NotNull(firstModeration.Response.ModerationLabels);
89+
Assert.IsNotEmpty(firstModeration.Response.ModerationLabels);
90+
Assert.AreEqual("Suggestive", firstModeration.Response.ModerationLabels[0].Name);
91+
Assert.AreEqual(94.9907455444336f, firstModeration.Response.ModerationLabels[0].Confidence);
92+
}
93+
94+
[Test]
95+
public void TestGetResourceModerationResponse_WithDuplicates()
96+
{
97+
var responseData = CommonJsonPrefix
98+
.Replace("REPLACE_PUBLIC_ID", "duplicate_id")
99+
+ DuplicatesModerationBlock
100+
+ CommonJsonSuffix;
101+
102+
var localCloudinaryMock = new MockedCloudinary(responseData);
103+
var result = localCloudinaryMock.GetResource("duplicate_id");
104+
105+
Assert.NotNull(result);
106+
Assert.AreEqual("duplicate_id", result.PublicId);
107+
Assert.NotNull(result.Moderation);
108+
Assert.IsNotEmpty(result.Moderation);
109+
110+
var firstModeration = result.Moderation[0];
111+
Assert.AreEqual("duplicate", firstModeration.Kind);
112+
Assert.AreEqual(ModerationStatus.Rejected, firstModeration.Status);
113+
Assert.NotNull(firstModeration.Response.ModerationLabels);
114+
Assert.IsNotEmpty(firstModeration.Response.ModerationLabels);
115+
Assert.AreEqual("duplicate_id", firstModeration.Response.ModerationLabels[0].PublicId);
116+
Assert.AreEqual(1.0f, firstModeration.Response.ModerationLabels[0].Confidence);
117+
}
118+
}
119+
}

CloudinaryDotNet/Actions/JsonConverter/ModerationResponseConverter.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ public override object ReadJson(
2727
object existingValue,
2828
JsonSerializer serializer)
2929
{
30-
return reader.TokenType == JsonToken.StartObject
31-
? serializer.Deserialize(reader, objectType)
32-
: null;
30+
switch (reader.TokenType)
31+
{
32+
case JsonToken.Null:
33+
return null;
34+
case JsonToken.StartArray:
35+
return new ModerationResponse { ModerationLabels = serializer.Deserialize<ModerationLabel[]>(reader) };
36+
case JsonToken.StartObject:
37+
return serializer.Deserialize<ModerationResponse>(reader);
38+
default:
39+
// If we hit something else, skip or return null
40+
reader.Skip();
41+
return null;
42+
}
3343
}
3444

3545
/// <summary>
@@ -50,4 +60,4 @@ public override void WriteJson(JsonWriter writer, object existingValue, JsonSeri
5060
throw new NotImplementedException("Unnecessary because of using just for Deserialization");
5161
}
5262
}
53-
}
63+
}

CloudinaryDotNet/Actions/ModerationLabel.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ public class ModerationLabel
1515
[DataMember(Name = "confidence")]
1616
public float Confidence;
1717

18+
/// <summary>
19+
/// Public ID of the duplicate asset.
20+
/// </summary>
21+
[DataMember(Name = "public_id")]
22+
public string PublicId;
23+
1824
/// <summary>
1925
/// Name of the offensive content category.
2026
/// </summary>
@@ -27,4 +33,4 @@ public class ModerationLabel
2733
[DataMember(Name = "parent_name")]
2834
public string ParentName;
2935
}
30-
}
36+
}

0 commit comments

Comments
 (0)