From 831f88404a176a6176094bc3847af1bcc9cef0f4 Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 19:40:45 +0800 Subject: [PATCH 1/9] feat: return disabled variation when prerequisite not met --- .gitmodules | 4 +- .../Program.cs | 5 +- ...=> PrerequisitesDepthOverflowException.cs} | 4 +- src/FeatureProbe.Server.Sdk/Models/Toggle.cs | 36 ++++---- .../FPClientTest.cs | 6 ++ .../StreamingSynchronizerIT.cs | 3 +- .../ToggleTest.cs | 92 +++++++++++++++++++ .../resources/test | 1 + 8 files changed, 126 insertions(+), 25 deletions(-) rename src/FeatureProbe.Server.Sdk/Exceptions/{PrerequisitesDeepOverflowException.cs => PrerequisitesDepthOverflowException.cs} (82%) create mode 100644 tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs create mode 160000 tests/FeatureProbe.Server.Sdk.Tests/resources/test diff --git a/.gitmodules b/.gitmodules index f5888ba..7090e76 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -[submodule "tests/FeatureProbe.Server.Sdk.UT/resources/test"] - path = tests/FeatureProbe.Server.Sdk.UT/resources/test +[submodule "tests/FeatureProbe.Server.Sdk.Tests/resources/test"] + path = tests/FeatureProbe.Server.Sdk.Tests/resources/test url = git@github.com:FeatureProbe/server-sdk-specification.git diff --git a/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs b/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs index b8de735..b67089d 100644 --- a/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs +++ b/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs @@ -27,8 +27,9 @@ public static void Main() //// Must provide the server-side SDK Key for your project and environment .ServerSdkKey("server-8ed48815ef044428826787e9a238b9c6a479f98c") //// RemoteUrl is where you deploy the FeatureProbe server, by default, SDK will use its API for reporting events, synchronizing toggles, and so - .RemoteUrl("https://featureprobe.io/server") // FeatureProbe online demo - // .RemoteUrl("http://localhost:4009/server") // Default URL for local Docker installation, also the default value if unset + .RemoteUrl( + "http://localhost:4009/server") // Default URL for local Docker installation, also the default value if unset + // .RemoteUrl("https://featureprobe.io/server") // FeatureProbe online demo //// Below are three modes of synchronizing toggles, you can choose one of them .StreamingMode() // .PollingMode() diff --git a/src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDeepOverflowException.cs b/src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDepthOverflowException.cs similarity index 82% rename from src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDeepOverflowException.cs rename to src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDepthOverflowException.cs index c5a8347..27a5df1 100644 --- a/src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDeepOverflowException.cs +++ b/src/FeatureProbe.Server.Sdk/Exceptions/PrerequisitesDepthOverflowException.cs @@ -16,9 +16,9 @@ namespace FeatureProbe.Server.Sdk.Exceptions; -public class PrerequisitesDeepOverflowException : Exception +public class PrerequisitesDepthOverflowException : Exception { - public PrerequisitesDeepOverflowException(string? message) : base(message) + public PrerequisitesDepthOverflowException(string? message) : base(message) { } } diff --git a/src/FeatureProbe.Server.Sdk/Models/Toggle.cs b/src/FeatureProbe.Server.Sdk/Models/Toggle.cs index e4b8ebc..4828591 100644 --- a/src/FeatureProbe.Server.Sdk/Models/Toggle.cs +++ b/src/FeatureProbe.Server.Sdk/Models/Toggle.cs @@ -26,7 +26,7 @@ public class Toggle { [JsonPropertyName("key")] public string Key { get; init; } - [JsonPropertyName("enabled")] public bool Enabled { get; init; } + [JsonPropertyName("enabled")] public virtual bool Enabled { get; init; } [JsonPropertyName("trackAccessEvents")] public bool? TrackAccessEvents { get; init; } @@ -35,13 +35,13 @@ public class Toggle [JsonPropertyName("version")] public long Version { get; init; } - [JsonPropertyName("disabledServe")] public Serve DisabledServe { get; init; } + [JsonPropertyName("disabledServe")] public virtual Serve DisabledServe { get; init; } - [JsonPropertyName("defaultServe")] public Serve DefaultServe { get; init; } + [JsonPropertyName("defaultServe")] public virtual Serve DefaultServe { get; init; } [JsonPropertyName("rules")] public List? Rules { get; init; } - [JsonPropertyName("variations")] public List Variations { get; init; } + [JsonPropertyName("variations")] public virtual List Variations { get; init; } [JsonPropertyName("prerequisites")] public List? Prerequisites { get; init; } @@ -49,20 +49,20 @@ public class Toggle public EvaluationResult Eval(FPUser user, ImmutableDictionary toggles, ImmutableDictionary segments, - object? defaultValue, int deep) + object? defaultValue, int depth) { string? reason; try { - return DoEval(user, toggles, segments, defaultValue, deep); + return DoEval(user, toggles, segments, defaultValue, depth); } - catch (Exception e) when (e is PrerequisiteException or PrerequisitesDeepOverflowException) + catch (Exception e) when (e is PrerequisiteException or PrerequisitesDepthOverflowException) { reason = e.Message; } return HitValue( - DefaultServe.EvalIndex(user, Key), + DisabledServe.EvalIndex(user, Key), defaultValue, null, reason @@ -70,7 +70,7 @@ public EvaluationResult Eval(FPUser user, ImmutableDictionary to } private EvaluationResult DoEval(FPUser user, ImmutableDictionary toggles, - ImmutableDictionary segments, object? defaultValue, int deep) + ImmutableDictionary segments, object? defaultValue, int depth) { if (!Enabled) { @@ -78,22 +78,22 @@ private EvaluationResult DoEval(FPUser user, ImmutableDictionary DisabledServe.EvalIndex(user, Key), defaultValue, null, - "Toggle disabled" + "Toggle disabled." ); } - if (deep <= 0) + if (depth <= 0) { - throw new PrerequisitesDeepOverflowException("Prerequisite deep overflow"); + throw new PrerequisitesDepthOverflowException("Prerequisite depth overflow"); } - if (!MeetPrerequisite(user, toggles, segments, deep)) + if (!MeetPrerequisite(user, toggles, segments, depth)) { return HitValue( - DefaultServe.EvalIndex(user, Key), + DisabledServe.EvalIndex(user, Key), defaultValue, null, - "Default rule hit" + "Toggle disabled." ); } @@ -121,8 +121,8 @@ private EvaluationResult DoEval(FPUser user, ImmutableDictionary ); } - private bool MeetPrerequisite(FPUser user, ImmutableDictionary toggles, - ImmutableDictionary segments, int deep) + protected virtual bool MeetPrerequisite(FPUser user, ImmutableDictionary toggles, + ImmutableDictionary segments, int depth) { if (Prerequisites is null || Prerequisites.Count == 0) { @@ -136,7 +136,7 @@ private bool MeetPrerequisite(FPUser user, ImmutableDictionary t throw new PrerequisiteException($"Prerequisite not exist: {Key}"); } - var eval = toggle.DoEval(user, toggles, segments, null, deep - 1); + var eval = toggle.DoEval(user, toggles, segments, null, depth - 1); if (eval.Value is null) { return false; diff --git a/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs index 66c8706..8c5eaef 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs @@ -142,6 +142,12 @@ private void TestFeatureProbeCases() var stringDetailRes = fpClient.StringDetail(toggleKey, user, defaultValue.GetValue()); _testOutputHelper.WriteLine(JsonSerializer.Serialize(stringDetailRes)); Assert.Equal(expectValue.GetValue(), stringDetailRes.Value); + if (expectResult["reason"] != null) + { + Assert.Contains(expectResult["reason"]!.ToString().ToLower(), + stringDetailRes.Reason!.ToLower()); + } + break; } } diff --git a/tests/FeatureProbe.Server.Sdk.Tests/StreamingSynchronizerIT.cs b/tests/FeatureProbe.Server.Sdk.Tests/StreamingSynchronizerIT.cs index a2ae5b3..7f5c713 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/StreamingSynchronizerIT.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/StreamingSynchronizerIT.cs @@ -27,7 +27,8 @@ public class StreamingSynchronizerIT internal static readonly Mock _mockedDataRepository = new(); - [Fact] + // temporary disabled as `https://featureprobe.io/server` is not available now + // [Fact] private void TestSocketRealtimeToggleUpdate() { var config = new FPConfig.Builder() diff --git a/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs new file mode 100644 index 0000000..296cc78 --- /dev/null +++ b/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs @@ -0,0 +1,92 @@ +using System.Collections.Immutable; +using FeatureProbe.Server.Sdk.Models; +using Moq; +using Moq.Protected; + +namespace FeatureProbe.Server.Sdk.UT; + +public class ToggleTest +{ + private readonly FPUser _user = new(); + + [Fact] + private void TestIfToggleIsDisabledServeDisabledVariation() + { + var toggle = new Toggle + { + Enabled = false, + Variations = new List { 0, 1 }, + DisabledServe = new Serve { Select = 0 }, + DefaultServe = new Serve { Select = 1 } + }; + var result = toggle.Eval(_user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, null, 1); + Assert.Equal(0, result.VariationIndex); + } + + [Fact] + private void TestIfToggleIsEnabledServeDefaultVariation() + { + var toggle = new Toggle + { + Enabled = true, + Variations = new List { 0, 1 }, + DisabledServe = new Serve { Select = 0 }, + DefaultServe = new Serve { Select = 1 } + }; + var result = toggle.Eval(_user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, null, 1); + Assert.Equal(1, result.VariationIndex); + } + + [Fact] + private void TestIfToggleIsEnabledServeDisabledVariation() + { + var toggle = new Toggle + { + Enabled = false, + Variations = new List { 0, 1 }, + DisabledServe = new Serve { Select = 1 }, + DefaultServe = new Serve { Select = 1 } + }; + var result = toggle.Eval(_user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, null, 1); + Assert.Equal(1, result.VariationIndex); + } + + [Fact] + private void TestIfToggleIsEnabledServeDefaultVariationWhenMeetPrerequisiteReturnsFalse() + { + var toggle = new Mock(); + toggle.Setup(t => t.Enabled).Returns(true); + toggle.Setup(t => t.Variations).Returns(new List { 0, 1 }); + toggle.Setup(t => t.DisabledServe).Returns(new Serve { Select = 0 }); + toggle.Setup(t => t.DefaultServe).Returns(new Serve { Select = 1 }); + + toggle.Protected().Setup("MeetPrerequisite", + _user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, 1).Returns(false); + + var result = toggle.Object.Eval(_user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, null, 1); + Assert.Equal(0, result.VariationIndex); + } + + [Fact] + private void TestIfToggleIsEnabledServeDefaultVariationWhenMeetPrerequisiteReturnsTrue() + { + var toggle = new Mock(); + toggle.Setup(t => t.Enabled).Returns(true); + toggle.Setup(t => t.Variations).Returns(new List { 0, 1 }); + toggle.Setup(t => t.DisabledServe).Returns(new Serve { Select = 0 }); + toggle.Setup(t => t.DefaultServe).Returns(new Serve { Select = 1 }); + + toggle.Protected().Setup("MeetPrerequisite", + _user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, 1).Returns(true); + + var result = toggle.Object.Eval(_user, ImmutableDictionary.Empty, + ImmutableDictionary.Empty, null, 1); + Assert.Equal(1, result.VariationIndex); + } +} diff --git a/tests/FeatureProbe.Server.Sdk.Tests/resources/test b/tests/FeatureProbe.Server.Sdk.Tests/resources/test new file mode 160000 index 0000000..503eec9 --- /dev/null +++ b/tests/FeatureProbe.Server.Sdk.Tests/resources/test @@ -0,0 +1 @@ +Subproject commit 503eec9218dc0d2af9439ceaf652316ed1e500fa From b3f546b5d8fafd29fcc60a7922c22495dcc5db1a Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 20:51:56 +0800 Subject: [PATCH 2/9] ci: add tests and release ci workflow --- .github/workflows/build.yml | 22 +++++++++++++++++++--- .github/workflows/codeql.yml | 2 +- .github/workflows/docs.yml | 30 ------------------------------ .github/workflows/lint.yml | 23 ----------------------- FeatureProbe.sln | 2 -- 5 files changed, 20 insertions(+), 59 deletions(-) delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f551d4..d1ac94f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,25 @@ name: Build on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: branches: [ main ] -# strategy: linux, macos, windows -# test & cov +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: | + 3.1.x + 5.0.x + 6.0.x + 7.0.x + - run: dotnet restore + - run: dotnet build -c Release --no-restore + - run: dotnet test -c Release --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + - uses: codecov/codecov-action@v2 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0cad799..a97baf8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,7 +16,7 @@ name: CodeQL on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: branches: [ main ] schedule: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 33b9835..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2023 FeatureProbe -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Docs - -on: - push: - branches: [ main ] - paths: - - 'src/**' - -jobs: - publish-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.x diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 09d9e65..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2023 FeatureProbe -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Lint PR - -on: - pull_request: - branches: [ main ] - -jobs: - title: - runs-on: ubuntu-latest diff --git a/FeatureProbe.sln b/FeatureProbe.sln index 7077918..711a423 100644 --- a/FeatureProbe.sln +++ b/FeatureProbe.sln @@ -15,8 +15,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github Workflow", "Github W .github\workflows\build.yml = .github\workflows\build.yml .github\workflows\codeql.yml = .github\workflows\codeql.yml .github\workflows\release.yml = .github\workflows\release.yml - .github\workflows\docs.yml = .github\workflows\docs.yml - .github\workflows\lint.yml = .github\workflows\lint.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{E283CF98-D2A5-445A-9474-F0DE1F587F9F}" From 056585b15a12508adaa1e0d504c447e845676322 Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 20:58:50 +0800 Subject: [PATCH 3/9] fix ci --- .github/workflows/build.yml | 7 ++++++- .github/workflows/codeql.yml | 2 +- .github/workflows/release.yml | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1ac94f..7c8bdde 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - fetch-depth: 0 + submodules: true - uses: actions/setup-dotnet@v3 with: dotnet-version: | @@ -37,4 +37,9 @@ jobs: - run: dotnet restore - run: dotnet build -c Release --no-restore - run: dotnet test -c Release --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + - if: failure() + uses: actions/upload-artifact@v3 + with: + name: TestResults-${{ github.run_id }} + path: '**/TestResults/*' - uses: codecov/codecov-action@v2 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a97baf8..0cad799 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,7 +16,7 @@ name: CodeQL on: push: - branches: [ main, dev ] + branches: [ main ] pull_request: branches: [ main ] schedule: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a2fc35..d5169e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,8 +15,10 @@ name: Release on: - push: - branches: [ main ] + release: + types: + - released + - prereleased jobs: build: From 3b618b0d0058d5ad1eb74eb80b7904522167af88 Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 21:18:46 +0800 Subject: [PATCH 4/9] fix ci --- .github/workflows/build.yml | 4 ++-- .../DataRepositories/MemoryDataRepository.cs | 2 +- tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c8bdde..e17575f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,8 +35,8 @@ jobs: 6.0.x 7.0.x - run: dotnet restore - - run: dotnet build -c Release --no-restore - - run: dotnet test -c Release --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + - run: dotnet build --no-restore + - run: dotnet test --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover - if: failure() uses: actions/upload-artifact@v3 with: diff --git a/src/FeatureProbe.Server.Sdk/DataRepositories/MemoryDataRepository.cs b/src/FeatureProbe.Server.Sdk/DataRepositories/MemoryDataRepository.cs index ec55c74..74f702f 100644 --- a/src/FeatureProbe.Server.Sdk/DataRepositories/MemoryDataRepository.cs +++ b/src/FeatureProbe.Server.Sdk/DataRepositories/MemoryDataRepository.cs @@ -68,7 +68,7 @@ public ImmutableDictionary Segments return !_initialized ? null : _data?.Segments.TryGetValue(key); } - public virtual void Refresh(Repository? repo) + public void Refresh(Repository? repo) { if (repo is null) { diff --git a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs index b29eff1..3330911 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs @@ -26,15 +26,15 @@ private void TestDeserializeFile() { var config = new FPConfig.Builder() .ServerSdkKey("server-8ed48815ef044428826787e9a238b9c6a479f98c") - .LocalFileMode(Path.Combine(Environment.CurrentDirectory, "resources/datasource/repo.json")) + .LocalFileMode("resources/datasource/repo.json") .Build(); using var fp = new FPClient(config, 100); var dataRepo = (IDataRepository)fp.GetType() .GetField("_dataRepository", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(fp)!; + Assert.NotEmpty(dataRepo.Segments); + Assert.NotEmpty(dataRepo.Toggles); Assert.True(dataRepo.Initialized); - Assert.True(dataRepo.Segments.Count > 0); - Assert.True(dataRepo.Toggles.Count > 0); } } From fb651de2dec6a8cd69eb5d9d02480794af5e3bc5 Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 22:11:36 +0800 Subject: [PATCH 5/9] fix fs path --- samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs | 2 +- src/FeatureProbe.Server.Sdk/FPConfig.cs | 6 +++--- tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs | 2 +- tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs | 5 +++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs b/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs index b67089d..7f73971 100644 --- a/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs +++ b/samples/FeatureProbe.Server.Sdk.SampleConsole/Program.cs @@ -33,7 +33,7 @@ public static void Main() //// Below are three modes of synchronizing toggles, you can choose one of them .StreamingMode() // .PollingMode() - // .LocalFileMode("datasource/repo.json") + // .LocalFileMode(Path.Combine("datasource", "repo.json")) //// Optionally provide a logger factory to enable logging .WithLoggers(LoggerFactory.Create(builder => builder.AddNLog())) .Build(); diff --git a/src/FeatureProbe.Server.Sdk/FPConfig.cs b/src/FeatureProbe.Server.Sdk/FPConfig.cs index c9b59b2..40a9827 100644 --- a/src/FeatureProbe.Server.Sdk/FPConfig.cs +++ b/src/FeatureProbe.Server.Sdk/FPConfig.cs @@ -37,7 +37,7 @@ private FPConfig(Builder builder) SynchronizerUrl = builder.SynchronizerUrlVal ?? $"{RemoteUrl}/api/server-sdk/toggles"; EventUrl = builder.EventUrlVal ?? $"{RemoteUrl}/api/events"; RealtimeUrl = builder.RealtimeUrlVal ?? $"{RemoteUrl}/realtime"; - FileLocation = builder.FileLocation ?? "datasource/repo.json"; + FileLocation = builder.FileLocation ?? Path.Combine("datasource", "repo.json"); RefreshInterval = builder.RefreshInterval ?? TimeSpan.FromSeconds(5); HttpConfig = builder.HttpConfig ?? new HttpConfig(); PrerequisiteDeep = builder.PrerequisiteDeepVal ?? 20; @@ -177,9 +177,9 @@ public Builder RealtimeUrl(string url) /// /// File path, default will be 'datasource/repo.json' under current directory. /// - public Builder LocalFileMode(string location = "datasource/repo.json") + public Builder LocalFileMode(string? location) { - FileLocation = location; + FileLocation = location ?? Path.Combine("datasource", "repo.json"); SynchronizerFactory = new FileSynchronizerFactory(); return this; } diff --git a/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs index 8c5eaef..c23dec5 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/FPClientTest.cs @@ -32,7 +32,7 @@ public FPClientTest(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; - var data = File.ReadAllText("resources/test/spec/toggle_simple_spec.json"); + var data = File.ReadAllText(Path.Combine("resources", "test", "spec", "toggle_simple_spec.json")); _testCase = JsonSerializer.Deserialize(data)!; } diff --git a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs index 3330911..b3ec76b 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs @@ -24,17 +24,18 @@ public class FileSynchronizerTest [Fact] private void TestDeserializeFile() { + var path = Path.Combine("resources", "datasource", "repo.json"); var config = new FPConfig.Builder() .ServerSdkKey("server-8ed48815ef044428826787e9a238b9c6a479f98c") - .LocalFileMode("resources/datasource/repo.json") + .LocalFileMode(path) .Build(); using var fp = new FPClient(config, 100); var dataRepo = (IDataRepository)fp.GetType() .GetField("_dataRepository", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(fp)!; + Assert.True(dataRepo.Initialized); Assert.NotEmpty(dataRepo.Segments); Assert.NotEmpty(dataRepo.Toggles); - Assert.True(dataRepo.Initialized); } } From b3858c7327cfaffa0160cccd907ccc6111937963 Mon Sep 17 00:00:00 2001 From: hezean Date: Sat, 7 Oct 2023 22:31:56 +0800 Subject: [PATCH 6/9] sync --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 1 - .../FileSynchronizerTest.cs | 28 ++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e17575f..3b65d3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v3 with: - submodules: true + submodules: 'true' - uses: actions/setup-dotnet@v3 with: dotnet-version: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d5169e7..d8fc686 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,4 +32,3 @@ jobs: 5.0.x 6.0.x 7.0.x - include-prerelease: true diff --git a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs index b3ec76b..23514bc 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/FileSynchronizerTest.cs @@ -16,11 +16,19 @@ using System.Reflection; using FeatureProbe.Server.Sdk.DataRepositories; +using Xunit.Abstractions; namespace FeatureProbe.Server.Sdk.UT; public class FileSynchronizerTest { + private readonly ITestOutputHelper _testOutputHelper; + + public FileSynchronizerTest(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + [Fact] private void TestDeserializeFile() { @@ -30,7 +38,7 @@ private void TestDeserializeFile() .LocalFileMode(path) .Build(); - using var fp = new FPClient(config, 100); + using var fp = new FPClient(config, -1); var dataRepo = (IDataRepository)fp.GetType() .GetField("_dataRepository", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(fp)!; @@ -38,4 +46,22 @@ private void TestDeserializeFile() Assert.NotEmpty(dataRepo.Segments); Assert.NotEmpty(dataRepo.Toggles); } + + [Fact] + private void TestDeserializeFileFail() + { + var path = Path.Combine("resources", "datasource", "not-exists.json"); + var config = new FPConfig.Builder() + .ServerSdkKey("server-8ed48815ef044428826787e9a238b9c6a479f98c") + .LocalFileMode(path) + .Build(); + + using var fp = new FPClient(config, -1); + var dataRepo = (IDataRepository)fp.GetType() + .GetField("_dataRepository", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(fp)!; + + Assert.False(dataRepo.Initialized); + Assert.Empty(dataRepo.Segments); + Assert.Empty(dataRepo.Toggles); + } } From 9d37eddd1a0bc5b97daeb61034845e94e847fc2a Mon Sep 17 00:00:00 2001 From: hezean Date: Sun, 8 Oct 2023 10:55:31 +0800 Subject: [PATCH 7/9] Update release.yml --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 28 +++++++++++++++++-- .../FeatureProbe.AspNet.Sdk.SampleApi.csproj | 3 +- ...atureProbe.Server.Sdk.SampleConsole.csproj | 1 + 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b65d3c..914c8fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ name: Build on: push: - branches: [ main, dev ] + branches: [ main ] pull_request: branches: [ main ] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8fc686..3cf7d61 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,10 +21,12 @@ on: - prereleased jobs: - build: - runs-on: ubuntu-latest + release: + runs-on: windows-latest steps: - uses: actions/checkout@v3 + with: + submodules: 'true' - uses: actions/setup-dotnet@v3 with: dotnet-version: | @@ -32,3 +34,25 @@ jobs: 5.0.x 6.0.x 7.0.x + - run: dotnet restore + - run: dotnet build --no-restore + - run: dotnet test --no-build --no-restore --verbosity normal --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover + + - name: Pack packages + run: | + $ReleaseTag = "${{ github.event.release.tag_name }}"; + $Version = ($ReleaseTag).TrimStart('v'); + echo "Publishing version: $Version"; + dotnet pack -c Release -o packages /p:PackageVersion=$Version /p:Version=$Version; + - uses: actions/upload-artifact@v3 + with: + name: 'packages-${{ github.event.release.tag_name }}' + path: './packages' + +# - name: Upload to github packages +# run: | +# dotnet nuget add source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json -n github.com -u ${{ github.repository_owner }} -p ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text +# dotnet nuget push .\packages\*.nupkg -s github.com -k ${{ secrets.GITHUB_TOKEN }} --skip-duplicate + - name: Upload to nuget + run: | + dotnet nuget push .\packages\*.nupkg -s nuget.org -k ${{ secrets.NUGET_API_KEY }} --skip-duplicate diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj b/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj index cb7d556..6129792 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj @@ -20,10 +20,9 @@ net7.0 enable enable + false - - diff --git a/samples/FeatureProbe.Server.Sdk.SampleConsole/FeatureProbe.Server.Sdk.SampleConsole.csproj b/samples/FeatureProbe.Server.Sdk.SampleConsole/FeatureProbe.Server.Sdk.SampleConsole.csproj index ae3851e..46fa193 100644 --- a/samples/FeatureProbe.Server.Sdk.SampleConsole/FeatureProbe.Server.Sdk.SampleConsole.csproj +++ b/samples/FeatureProbe.Server.Sdk.SampleConsole/FeatureProbe.Server.Sdk.SampleConsole.csproj @@ -21,6 +21,7 @@ net7.0 enable enable + false Example Console app for using FeatureProbe.Server.Sdk From 9fe72f02da5ed33346ae49a8b0f45b9241727fe4 Mon Sep 17 00:00:00 2001 From: hezean Date: Sun, 8 Oct 2023 15:43:07 +0800 Subject: [PATCH 8/9] asp.net sdk --- .licenserc.yaml | 1 + .../FeatureProbeController.cs} | 25 +++++--- .../Controllers/WeatherForecastController.cs | 48 --------------- .../FeatureProbe.AspNet.Sdk.SampleApi.csproj | 4 ++ .../Program.cs | 25 ++------ .../Properties/launchSettings.json | 31 +--------- .../appsettings.json | 6 ++ .../FPServiceExtension.cs | 58 +++++++++++++++++++ .../FeatureProbe.AspNet.Sdk.csproj | 9 +++ src/FeatureProbe.Server.Sdk/FPConfig.cs | 8 +-- .../Synchronizer/StreamingSynchronizer.cs | 9 ++- .../ToggleTest.cs | 16 +++++ 12 files changed, 130 insertions(+), 110 deletions(-) rename samples/FeatureProbe.AspNet.Sdk.SampleApi/{WeatherForecast.cs => Controllers/FeatureProbeController.cs} (51%) delete mode 100644 samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/WeatherForecastController.cs create mode 100644 src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs diff --git a/.licenserc.yaml b/.licenserc.yaml index 0538cb6..2116835 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -23,6 +23,7 @@ header: - "**/*.md" - "**/obj/**/*" - "**/bin/**/*" + - "**/TestResults/**/*" - "**/*.sln" - "**/*.json" - "nlog.config" diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/WeatherForecast.cs b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/FeatureProbeController.cs similarity index 51% rename from samples/FeatureProbe.AspNet.Sdk.SampleApi/WeatherForecast.cs rename to samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/FeatureProbeController.cs index 9e24c0f..744935a 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/WeatherForecast.cs +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/FeatureProbeController.cs @@ -14,15 +14,26 @@ * limitations under the License. */ -namespace FeatureProbe.AspNet.Sdk.SampleApi; +using FeatureProbe.Server.Sdk; +using Microsoft.AspNetCore.Mvc; -public class WeatherForecast -{ - public DateOnly Date { get; set; } +namespace FeatureProbe.AspNet.Sdk.SampleApi.Controllers; - public int TemperatureC { get; set; } +[ApiController] +[Route("/featureprobe")] +public class FeatureProbeController : ControllerBase +{ + private readonly FPClient _fpClient; - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + public FeatureProbeController(FPClient fpClient) + { + _fpClient = fpClient; + } - public string? Summary { get; set; } + [HttpGet] + public FPDetail Get([FromQuery] string userId) + { + var user = new FPUser().With("userId", userId); + return _fpClient.BoolDetail("campaign_allow_list", user, false); + } } diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/WeatherForecastController.cs b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/WeatherForecastController.cs deleted file mode 100644 index 3a6359e..0000000 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2023 FeatureProbe - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Microsoft.AspNetCore.Mvc; - -namespace FeatureProbe.AspNet.Sdk.SampleApi.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj b/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj index 6129792..bf9ca37 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/FeatureProbe.AspNet.Sdk.SampleApi.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Program.cs b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Program.cs index a6c72e1..af71cd3 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Program.cs +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Program.cs @@ -14,28 +14,13 @@ * limitations under the License. */ -var builder = WebApplication.CreateBuilder(args); - -// Add services to the container. +using FeatureProbe.AspNet.Sdk; -builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +var builder = WebApplication.CreateBuilder(args); +builder.Services + .AddFeatureProbe(builder.Configuration.GetSection("FeatureProbe")) + .AddControllers(); var app = builder.Build(); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.UseSwagger(); - app.UseSwaggerUI(); -} - -app.UseHttpsRedirection(); - -app.UseAuthorization(); - app.MapControllers(); - app.Run(); diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Properties/launchSettings.json b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Properties/launchSettings.json index 0b9cbb2..14fece1 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/Properties/launchSettings.json +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/Properties/launchSettings.json @@ -1,41 +1,12 @@ { "$schema": "https://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:18943", - "sslPort": 44383 - } - }, "profiles": { "http": { "commandName": "Project", - "launchBrowser": true, - "launchUrl": "swagger", "applicationUrl": "http://localhost:5247", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "dotnetRunMessages": true - }, - "https": { - "commandName": "Project", "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:7248;http://localhost:5247", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, + "launchUrl": "featureprobe?userId=00001", "dotnetRunMessages": true - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } \ No newline at end of file diff --git a/samples/FeatureProbe.AspNet.Sdk.SampleApi/appsettings.json b/samples/FeatureProbe.AspNet.Sdk.SampleApi/appsettings.json index 2230277..aed35ae 100644 --- a/samples/FeatureProbe.AspNet.Sdk.SampleApi/appsettings.json +++ b/samples/FeatureProbe.AspNet.Sdk.SampleApi/appsettings.json @@ -1,4 +1,10 @@ { + "FeatureProbe": { + "RemoteUrl": "http://localhost:4009/server", + "SdkKey": "server-8ed48815ef044428826787e9a238b9c6a479f98c", + "RefreshInterval": 5, + "StartWait": 200 + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs b/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs new file mode 100644 index 0000000..6f62d31 --- /dev/null +++ b/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs @@ -0,0 +1,58 @@ +/* + * Copyright 2023 FeatureProbe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FeatureProbe.Server.Sdk; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace FeatureProbe.AspNet.Sdk; + +public static class FPServiceExtension +{ + public static IServiceCollection AddFeatureProbe(this IServiceCollection services, IConfigurationSection config) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (config is null) + { + throw new ArgumentNullException(nameof(config)); + } + + var fpConfig = new FPConfig.Builder() + .WithLoggers(services.BuildServiceProvider().GetRequiredService()) + .ServerSdkKey(config["SdkKey"] ?? throw new ArgumentNullException("SdkKey")) + .RemoteUrl(config["RemoteUrl"]) + .EventUrl(config["EventUrl"]) + .SynchronizerUrl(config["SynchronizerUrl"]) + .RealtimeUrl(config["RealtimeUrl"]) + .StreamingMode(refreshInterval: TimeSpan.FromSeconds(Int32.Parse(config["RefreshInterval"] ?? "5"))) + .UseMemoryDataRepository() + .Build(); + + var fpClient = new FPClient(fpConfig, Int32.Parse(config["StartWait"] ?? "500")); + services.AddSingleton(fpClient); + + var lifetime = services.BuildServiceProvider().GetRequiredService(); + lifetime.ApplicationStopping.Register(fpClient.Dispose); + + return services; + } +} diff --git a/src/FeatureProbe.AspNet.Sdk/FeatureProbe.AspNet.Sdk.csproj b/src/FeatureProbe.AspNet.Sdk/FeatureProbe.AspNet.Sdk.csproj index c1f0918..7fa9b08 100644 --- a/src/FeatureProbe.AspNet.Sdk/FeatureProbe.AspNet.Sdk.csproj +++ b/src/FeatureProbe.AspNet.Sdk/FeatureProbe.AspNet.Sdk.csproj @@ -23,4 +23,13 @@ enable + + + + + + + + + diff --git a/src/FeatureProbe.Server.Sdk/FPConfig.cs b/src/FeatureProbe.Server.Sdk/FPConfig.cs index 40a9827..d700346 100644 --- a/src/FeatureProbe.Server.Sdk/FPConfig.cs +++ b/src/FeatureProbe.Server.Sdk/FPConfig.cs @@ -136,7 +136,7 @@ public Builder StreamingMode(TimeSpan? refreshInterval = null, HttpConfig? httpC /// /// URL for FeatureProbe server, default is a local server, i.e. http://localhost:4009/server. /// - public Builder RemoteUrl(string url) + public Builder RemoteUrl(string? url) { RemoteUrlVal = url; return this; @@ -145,7 +145,7 @@ public Builder RemoteUrl(string url) /// /// Overwrite the URL for synchronizer. /// - public Builder SynchronizerUrl(string url) + public Builder SynchronizerUrl(string? url) { SynchronizerUrlVal = url; return this; @@ -154,7 +154,7 @@ public Builder SynchronizerUrl(string url) /// /// Overwrite the URL for event reporting. /// - public Builder EventUrl(string url) + public Builder EventUrl(string? url) { EventUrlVal = url; return this; @@ -165,7 +165,7 @@ public Builder EventUrl(string url) /// /// /// - public Builder RealtimeUrl(string url) + public Builder RealtimeUrl(string? url) { RealtimeUrlVal = url; return this; diff --git a/src/FeatureProbe.Server.Sdk/Synchronizer/StreamingSynchronizer.cs b/src/FeatureProbe.Server.Sdk/Synchronizer/StreamingSynchronizer.cs index fc24ab4..7546c53 100644 --- a/src/FeatureProbe.Server.Sdk/Synchronizer/StreamingSynchronizer.cs +++ b/src/FeatureProbe.Server.Sdk/Synchronizer/StreamingSynchronizer.cs @@ -78,7 +78,14 @@ private SocketIO ConnectSocket(FPConfig config) await _pollingSynchronizer.PollAsync(); }); - socket.ConnectAsync().Wait(); + try + { + socket.ConnectAsync().Wait(); + } + catch (Exception e) + { + Loggers.Synchronizer?.Log(LogLevel.Error, e, "Socket connect error, fallback to polling synchronizer"); + } return socket; } diff --git a/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs b/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs index 296cc78..fa41d74 100644 --- a/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs +++ b/tests/FeatureProbe.Server.Sdk.Tests/ToggleTest.cs @@ -1,3 +1,19 @@ +/* + * Copyright 2023 FeatureProbe + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + using System.Collections.Immutable; using FeatureProbe.Server.Sdk.Models; using Moq; From a8faeff1798db51837ce6eb161087687bcf461ce Mon Sep 17 00:00:00 2001 From: hezean Date: Sun, 8 Oct 2023 15:43:45 +0800 Subject: [PATCH 9/9] Update FPServiceExtension.cs --- src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs b/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs index 6f62d31..bfce31f 100644 --- a/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs +++ b/src/FeatureProbe.AspNet.Sdk/FPServiceExtension.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -using FeatureProbe.Server.Sdk; +using FeatureProbe.Server.Sdk; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;