diff --git a/.harness/pipeline.yaml b/.harness/pipeline.yaml index 83dee830..37fe51f9 100644 --- a/.harness/pipeline.yaml +++ b/.harness/pipeline.yaml @@ -75,7 +75,7 @@ pipeline: $env:PATH = "C:\dotnet;C:\dotnet\tools;$env:PATH" dotnet-sonarscanner begin ` /k:"dotnet-client" ` - /d:sonar.host.url="https://sonar.harness.io/" ` + /d:sonar.host.url="https://sonar.harness.io" ` /d:sonar.token="<+secrets.getValue("sonarqube-token")>" ` /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" ` /d:sonar.coverage.exclusions="**/*Tests*/**,**/bin/**,**/obj/**" ` @@ -268,7 +268,7 @@ pipeline: -CommitSha "<+codebase.commitSha>" ` -SonarProjectKey "dotnet-client" ` -SonarToken "<+secrets.getValue('sonarqube-token')>" ` - -SonarUrl "https://sonar.harness.io/" + -SonarUrl "https://sonar.harness.io" - parallel: - step: type: Run diff --git a/Splitio.Redis/Services/Cache/Classes/RedisImpressionsCache.cs b/Splitio.Redis/Services/Cache/Classes/RedisImpressionsCache.cs index 28339d4a..da0cf8d7 100644 --- a/Splitio.Redis/Services/Cache/Classes/RedisImpressionsCache.cs +++ b/Splitio.Redis/Services/Cache/Classes/RedisImpressionsCache.cs @@ -80,11 +80,8 @@ public async Task RecordImpressionsCountAsync(Dictionary impression private RedisValue[] GetImpressions(IList items) { - var impressions = items.Select(item => JsonConvertWrapper.SerializeObject(new - { - m = new { s = SdkVersion, i = MachineIp, n = MachineName }, - i = new { k = item.keyName, b = item.bucketingKey, f = item.feature, t = item.treatment, r = item.label, c = item.changeNumber, m = item.time, pt = item.previousTime } - })); + var impressions = items + .Select(item => JsonConvertWrapper.SerializeObject(new KeyImpressionDto(item, SdkVersion, MachineIp, MachineName))); return impressions .Select(i => (RedisValue)i) diff --git a/Splitio.Redis/Splitio.Redis.csproj b/Splitio.Redis/Splitio.Redis.csproj index 23a3ee68..b1790d52 100644 --- a/Splitio.Redis/Splitio.Redis.csproj +++ b/Splitio.Redis/Splitio.Redis.csproj @@ -7,7 +7,7 @@ false false false - 7.12.0 + 7.12.1-rc.1 true SplitioRedis.snk Apache-2.0 diff --git a/src/Splitio/Domain/KeyImpression.cs b/src/Splitio/Domain/KeyImpression.cs index a64c16dd..23bb2b48 100644 --- a/src/Splitio/Domain/KeyImpression.cs +++ b/src/Splitio/Domain/KeyImpression.cs @@ -6,7 +6,7 @@ public class KeyImpression { public KeyImpression() { } - public KeyImpression(string matchingKey, string feature, string treatment, long time, long? changeNumber, string label, string bucketingKey, bool impressionsDisabled, long? previousTime = null, bool optimized = false) + public KeyImpression(string matchingKey, string feature, string treatment, long time, long? changeNumber, string label, string bucketingKey, bool impressionsDisabled, long? previousTime = null, bool optimized = false, string properties = null) { this.feature = feature; keyName = matchingKey; @@ -18,6 +18,7 @@ public KeyImpression(string matchingKey, string feature, string treatment, long this.previousTime = previousTime; ImpressionsDisabled = impressionsDisabled; this.optimized = optimized; + this.properties = properties; } [JsonIgnore] diff --git a/src/Splitio/Domain/KeyImpressionDTO.cs b/src/Splitio/Domain/KeyImpressionDTO.cs new file mode 100644 index 00000000..32c6f592 --- /dev/null +++ b/src/Splitio/Domain/KeyImpressionDTO.cs @@ -0,0 +1,71 @@ +using Newtonsoft.Json; + +namespace Splitio.Domain +{ + public class KeyImpressionMetadataDto + { + [JsonProperty("s")] + private readonly string _sdkVersion; + [JsonProperty("i")] + private readonly string _machineIp; + [JsonProperty("n")] + private readonly string _machineName; + + public KeyImpressionMetadataDto(string sdkVersion, string machineIp, string machineName) + { + _sdkVersion = sdkVersion; + _machineIp = machineIp; + _machineName = machineName; + } + } + + public class KeyImpressionItemDto + { + [JsonProperty("f")] + private readonly string _feature; + [JsonProperty("k")] + private readonly string _keyName; + [JsonProperty("t")] + private readonly string _treatment; + [JsonProperty("m")] + private readonly long _time; + [JsonProperty("c")] + private readonly long? _changeNumber; + [JsonProperty("r")] + private readonly string _label; + [JsonProperty("b")] + private readonly string _bucketingKey; + [JsonProperty("pt")] + private readonly long? _previousTime; + + [JsonProperty(PropertyName = "properties", NullValueHandling = NullValueHandling.Ignore)] + private readonly string _properties; + + public KeyImpressionItemDto(KeyImpression impression) + { + _feature = impression.feature; + _keyName = impression.keyName; + _treatment = impression.treatment; + _time = impression.time; + _changeNumber = impression.changeNumber; + _label = impression.label; + _bucketingKey = impression.bucketingKey; + _previousTime = impression.previousTime; + _properties = impression.properties; + } + } + + public class KeyImpressionDto + { + [JsonProperty("m")] + private readonly KeyImpressionMetadataDto _metadata; + [JsonProperty("i")] + private readonly KeyImpressionItemDto _item; + + public KeyImpressionDto(KeyImpression impression, string sdkVersion, string machineIp, string machineName) + { + _metadata = new KeyImpressionMetadataDto(sdkVersion, machineIp, machineName); + _item = new KeyImpressionItemDto(impression); + } + } +} \ No newline at end of file diff --git a/src/Splitio/Services/Impressions/Classes/ImpressionsSdkApiClient.cs b/src/Splitio/Services/Impressions/Classes/ImpressionsSdkApiClient.cs index 9c75c3d2..b5bef285 100644 --- a/src/Splitio/Services/Impressions/Classes/ImpressionsSdkApiClient.cs +++ b/src/Splitio/Services/Impressions/Classes/ImpressionsSdkApiClient.cs @@ -92,6 +92,7 @@ public static string ConvertToJson(List impressions) properties = x.properties }) }); + return JsonConvertWrapper.SerializeObjectIgnoreNullValue(impressionsPerFeature); } diff --git a/src/Splitio/Splitio.csproj b/src/Splitio/Splitio.csproj index f7d09149..47f9bcae 100644 --- a/src/Splitio/Splitio.csproj +++ b/src/Splitio/Splitio.csproj @@ -7,7 +7,7 @@ false false false - 7.12.0 + 7.12.1-rc.1 Splitio true Splitio.snk diff --git a/tests/Splitio-tests/Integration Tests/Impressions/RedisImpressionsCacheTests.cs b/tests/Splitio-tests/Integration Tests/Impressions/RedisImpressionsCacheTests.cs index 522b2a91..7ef3094c 100644 --- a/tests/Splitio-tests/Integration Tests/Impressions/RedisImpressionsCacheTests.cs +++ b/tests/Splitio-tests/Integration Tests/Impressions/RedisImpressionsCacheTests.cs @@ -1,10 +1,12 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Splitio.Domain; using Splitio.Redis.Services.Cache.Classes; using Splitio.Redis.Services.Cache.Interfaces; using Splitio.Redis.Services.Domain; using Splitio.Telemetry.Domain; using Splitio.Tests.Common.Resources; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace Splitio_Tests.Integration_Tests.Impressions @@ -94,36 +96,61 @@ await impressionsCache.RecordUniqueKeysAsync(new List Clean(); } - private static RedisAdapterForTests GetRedisClusterAdapter() + [TestMethod] + public async Task RecordImpressionWithProperties() + { + // Arrange. + var cache = GetRedisClusterImpressionsCache("Prop-Test:"); + var impressions = new List + { + new KeyImpression("matching-key", "feature-1", "treatment", 34534546, 3333444, "label", "bucketing-key", false), + new KeyImpression("matching-key", "feature-1", "treatment", 34534550, 3333444, "label", "bucketing-key", false, 34534546), + new KeyImpression("matching-key", "feature-2", "treatment", 34534546, 3333444, "label", "bucketing-key", false, properties: "{\"prop\":\"val\"}"), + }; + + // Act. + await _impressionsCache.AddAsync(impressions); + var result = await _redisAdapter.ListRangeAsync("test-mtks:.SPLITIO.impressions"); + + // Assert. + Assert.AreEqual(3, result.Count()); + Assert.AreEqual("{\"m\":{\"s\":\"version\",\"i\":\"ip\",\"n\":\"mm\"},\"i\":{\"f\":\"feature-1\",\"k\":\"matching-key\",\"t\":\"treatment\",\"m\":34534546,\"c\":3333444,\"r\":\"label\",\"b\":\"bucketing-key\",\"pt\":null}}", result[0].ToString()); + Assert.AreEqual("{\"m\":{\"s\":\"version\",\"i\":\"ip\",\"n\":\"mm\"},\"i\":{\"f\":\"feature-1\",\"k\":\"matching-key\",\"t\":\"treatment\",\"m\":34534550,\"c\":3333444,\"r\":\"label\",\"b\":\"bucketing-key\",\"pt\":34534546}}", result[1].ToString()); + Assert.AreEqual("{\"m\":{\"s\":\"version\",\"i\":\"ip\",\"n\":\"mm\"},\"i\":{\"f\":\"feature-2\",\"k\":\"matching-key\",\"t\":\"treatment\",\"m\":34534546,\"c\":3333444,\"r\":\"label\",\"b\":\"bucketing-key\",\"pt\":null,\"properties\":\"{\\\"prop\\\":\\\"val\\\"}\"}}", result[2].ToString()); + + Clean(); + } + + private static RedisAdapterForTests GetRedisClusterAdapter(string prefix = null) { var config = new RedisConfig { - ClusterNodes = new Splitio.Domain.ClusterNodes( new List() { "localhost:6379" }, "{SPLITIO}"), + ClusterNodes = new ClusterNodes( new List() { "localhost:6379" }, "{SPLITIO}"), RedisPassword = "", RedisDatabase = 0, RedisConnectTimeout = 1000, RedisConnectRetry = 5, RedisSyncTimeout = 1000, PoolSize = 1, - RedisUserPrefix = RedisPrefix + RedisUserPrefix = prefix ?? RedisPrefix }; var pool = new ConnectionPoolManager(config); return new RedisAdapterForTests(config, pool); } - private static RedisImpressionsCache GetRedisClusterImpressionsCache() + private static RedisImpressionsCache GetRedisClusterImpressionsCache(string prefix = null) { var config = new RedisConfig { - ClusterNodes = new Splitio.Domain.ClusterNodes(new List() { "localhost:6379" }, "{SPLITIO}"), + ClusterNodes = new ClusterNodes(new List() { "localhost:6379" }, "{SPLITIO}"), RedisPassword = "", RedisDatabase = 0, RedisConnectTimeout = 1000, RedisConnectRetry = 5, RedisSyncTimeout = 1000, PoolSize = 1, - RedisUserPrefix = RedisPrefix, + RedisUserPrefix = prefix ?? RedisPrefix, SdkMachineIP = "ip", SdkVersion = "version", SdkMachineName = "mm" diff --git a/tests/Splitio-tests/Unit Tests/Impressions/RedisImpressionsCacheTests.cs b/tests/Splitio-tests/Unit Tests/Impressions/RedisImpressionsCacheTests.cs index de5abd43..96cf6ee1 100644 --- a/tests/Splitio-tests/Unit Tests/Impressions/RedisImpressionsCacheTests.cs +++ b/tests/Splitio-tests/Unit Tests/Impressions/RedisImpressionsCacheTests.cs @@ -1,9 +1,11 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Splitio.Domain; using Splitio.Redis.Services.Cache.Classes; using Splitio.Redis.Services.Cache.Interfaces; using Splitio.Redis.Services.Domain; using Splitio.Telemetry.Domain; +using StackExchange.Redis; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -91,5 +93,25 @@ await _cache.RecordUniqueKeysAsync(new List _redisAdapter.Verify(mock => mock.ListRightPushAsync(key, expected2), Times.Once); _redisAdapter.Verify(mock => mock.KeyExpireAsync(key, new TimeSpan(0, 0, 3600)), Times.Never); } + + [TestMethod] + public async Task CorrectFormatStoreImpressions() + { + // Arrange. + var impressions = new List + { + new KeyImpression("matching-key", "feature-1", "treatment", 34534546, 3333444, "label", "bucketing-key", false), + new KeyImpression("matching-key", "feature-1", "treatment", 34534550, 3333444, "label", "bucketing-key", false, 34534546), + new KeyImpression("matching-key", "feature-2", "treatment", 34534546, 3333444, "label", "bucketing-key", false, properties: "{\"prop\":\"val\"}"), + }; + + // Act. + var result = await _cache.AddAsync(impressions); + + // Assert. + _redisAdapter.Verify(mock => mock.ListRightPushAsync("test-pre:.SPLITIO.impressions", It.IsAny()), Times.Once); + + //IRedisAdapterProducer.ListRightPushAsync("test-pre:.SPLITIO.impressions", [{"m":{"s":"version","i":"ip","n":"mm"},"i":{"f":"feature-1","k":"matching-key","t":"treatment","m":34534546,"c":3333444,"r":"label","b":"bucketing-key","pt":null}}, {"m":{"s":"version","i":"ip","n":"mm"},"i":{"f":"feature-1","k":"matching-key","t":"treatment","m":34534550,"c":3333444,"r":"label","b":"bucketing-key","pt":34534546}}, {"m":{"s":"version","i":"ip","n":"mm"},"i":{"f":"feature-2","k":"matching-key","t":"treatment","m":34534546,"c":3333444,"r":"label","b":"bucketing-key","pt":null,"properties":"{\"prop\":\"val\"}"}}]) + } } }