diff --git a/.github/pkl-workflows/GithubAction/GithubAction.pkl b/.github/pkl-workflows/GithubAction/GithubAction.pkl index d99ca79acd..07c292fcb8 100644 --- a/.github/pkl-workflows/GithubAction/GithubAction.pkl +++ b/.github/pkl-workflows/GithubAction/GithubAction.pkl @@ -545,6 +545,11 @@ class Permissions { } typealias Permission = "read"|"write"|"none" +class Environment { + name: String + url: String? +} + // Concurrency class Concurrency { group: String @@ -569,7 +574,7 @@ abstract class JobBase { outputs: Mapping? `timeout-minutes`: Int? permissions: (*Permissions|"read-all"|"write-all")? - environment: String? + environment: (*Environment|String)? } abstract class StepJobBase extends JobBase { diff --git a/.github/pkl-workflows/codeql.pkl b/.github/pkl-workflows/codeql.pkl deleted file mode 100644 index 6627000c37..0000000000 --- a/.github/pkl-workflows/codeql.pkl +++ /dev/null @@ -1,77 +0,0 @@ -module codeql - -amends "GithubAction/GithubAction.pkl" - -import "helpers/Common.pkl" -import "helpers/Steps.pkl" -import "helpers/Actions.pkl" - -name = "CodeQL" -on { - push { - branches { - Common.mainBranch - } - } - pull_request { - branches { - Common.mainBranch - } - paths { - "**.cs" - "**.cpp" - "**.hpp" - "**.csproj" - ".github/workflows/codeql.yml" - } - } -} -env { - ["REALM_DISABLE_ANALYTICS"] = true -} -concurrency { - group = "codeql-${{ github.head_ref || github.run_id }}" - `cancel-in-progress` = true -} -jobs { - ["analyze-csharp"] = new Job { - name = "Analyze C#" - `runs-on` = new WindowsLatest{} - permissions { - actions = "read" - contents = "read" - `security-events` = "write" - } - steps { - Steps.checkoutWithoutMatchers(false) - new { - name = "Initialize CodeQL" - uses = Actions.codeQLInit - with { - ["languages"] = "csharp" - ["config"] = """ - queries: - - uses: security-and-quality - query-filters: - - exclude: - id: cs/call-to-unmanaged-code - - exclude: - id: cs/unmanaged-code - """ - } - } - ...Steps.setupAndroid() - ...Steps.setupWorkloads("tvos ios maccatalyst android", "8.0.x") - ...Steps.msbuild(new Steps.MSBuildConfig { - projects = Common.packages.map((pkg) -> "Realm/\(pkg)").toListing() - properties { - ["UseSharedCompilation"] = "false" - } - }) - new { - name = "Perform CodeQL Analysis" - uses = Actions.codeQLAnalyze - } - } - } -} \ No newline at end of file diff --git a/.github/pkl-workflows/helpers/Actions.pkl b/.github/pkl-workflows/helpers/Actions.pkl index 7a3007e578..1f1599e0a3 100644 --- a/.github/pkl-workflows/helpers/Actions.pkl +++ b/.github/pkl-workflows/helpers/Actions.pkl @@ -23,3 +23,5 @@ const createPR = "peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2 const publishGithubRelease = "ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5" // 1.14.0 const releaseToSlack = "realm/ci-actions/release-to-slack@6418e15ed9bbdb19b7d456a347e5623779f95cdf" const setupCmake = "jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be" // 2.0.2 +const uploadPagesArtifact = "actions/upload-pages-artifact@v3" +const deployPages = "actions/deploy-pages@v4" diff --git a/.github/pkl-workflows/helpers/BaaS.pkl b/.github/pkl-workflows/helpers/BaaS.pkl deleted file mode 100644 index ae228a428a..0000000000 --- a/.github/pkl-workflows/helpers/BaaS.pkl +++ /dev/null @@ -1,47 +0,0 @@ -module baas - -import "../GithubAction/GithubAction.pkl" as gha -import "Common.pkl" -import "Steps.pkl" - -function deploy(differentiators: Listing): gha.MatrixJob = new { - name = "Deploy BaaS" - `runs-on` = new gha.UbuntuLatest{} - strategy { - matrix { - ["differentiator"] = differentiators - } - } - `if` = Common.ifNotCanceledCondition - steps { - ...Steps.checkout(false) - Steps.setupDotnet("8.0.x") - ...deployStep("${{ matrix.differentiator }}", true) - } -} - -function deployStep(differentiator: Common.SyncDifferentiator?, shouldDeploy: Boolean): List = if (shouldDeploy && differentiator != null) List(new gha.Step { - name = "Deploy Apps" - run = "dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=\(differentiator)-${{ github.run_id }}-${{ github.run_attempt }}" - `working-directory` = "Tools/DeployApps" -}) else List() - -function cleanup(differentiators: Listing): gha.MatrixJob = new { - name = "Cleanup BaaS" - `runs-on` = new gha.UbuntuLatest{} - strategy { - matrix { - ["differentiator"] = differentiators - } - } - `if` = Common.ifNotCanceledCondition - steps { - ...Steps.checkout(false) - Steps.setupDotnet("8.0.x") - new { - name = "Terminate Baas" - run = "dotnet run terminate-baas --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=${{ matrix.differentiator }}-${{ github.run_id }}-${{ github.run_attempt }}" - `working-directory` = "Tools/DeployApps" - } - } -} \ No newline at end of file diff --git a/.github/pkl-workflows/helpers/Common.pkl b/.github/pkl-workflows/helpers/Common.pkl index 7b013bed94..444688ac9d 100644 --- a/.github/pkl-workflows/helpers/Common.pkl +++ b/.github/pkl-workflows/helpers/Common.pkl @@ -1,7 +1,6 @@ module common import "../GithubAction/GithubAction.pkl" as gha -import "BaaS.pkl" import "Lint.pkl" import "Package.pkl" import "Test.pkl" as TestJobs @@ -22,7 +21,7 @@ const job_Unity: String = "build-unity" local const job_Wrappers: String = "build-wrappers" -const mainBranch: String = "main" +const mainBranch: String = "community" typealias NetFramework = "net6.0" | "net8.0" | String(startsWith("${{ matrix")) | String(startsWith("net8")) typealias NetRuntime = "win-x64" | "linux-x64" | "osx-x64" | "osx-arm64" | String(startsWith("${{ matrix")) @@ -37,7 +36,6 @@ const wrapperBinaryNames: List = + applePlatformTargets((platform, target) -> "\(platform)-\(target)") const defaultEnv: Mapping = new { - ["REALM_DISABLE_ANALYTICS"] = true ["DOTNET_NOLOGO"] = true } @@ -45,46 +43,43 @@ const function applePlatformTargets(_transform: (String, String) -> String): Lis const ifNotCanceledCondition = "always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')" -const nugetPackages: List = List("Realm", "Realm.PlatformHelpers") +const nugetPackages: List = List("Realm") const packages: List = nugetPackages + List("Realm.UnityUtils", "Realm.UnityWeaver") const testTimeout: Int = 60 -const function defaultBuildJobs(baasDifferentiators: Listing, netCoreVersions: Listing): Mapping = new { +const function defaultBuildJobs(netCoreVersions: Listing): Mapping = new { [job_Wrappers] = new gha.ReusableWorkflowJob { uses = "./.github/workflows/wrappers.yml" name = "Wrappers" } - [job_Baas] = BaaS.deploy(baasDifferentiators) [job_Packages] = (Package.nuget("contains(github.head_ref, 'release')")){ needs { job_Wrappers } } [job_Unity] = Package.unity() - ...TestJobs.unity(new TestJobs.UnityTestConfig { - os = "linux" - }) - ...TestJobs.unity(new TestJobs.UnityTestConfig { - os = "windows" - }) - ["test-net-framework"] = TestJobs.netFramework(baasDifferentiators) - ["test-uwp"] = TestJobs.uwp(baasDifferentiators) +// TODO: https://github.com/realm/realm-dotnet/issues/3667 the Unity licensing server needs to be fixed before we can reenable these +// ...TestJobs.unity(new TestJobs.UnityTestConfig { +// os = "linux" +// }) +// ...TestJobs.unity(new TestJobs.UnityTestConfig { +// os = "windows" +// }) + ["test-net-framework"] = TestJobs.netFramework() + ["test-uwp"] = TestJobs.uwp() ["test-net-core"] = TestJobs.netCore(netCoreVersions) ["test-macos-xamarin"] = TestJobs.macOS_Xamarin() - ["test-macos-maui"] = TestJobs.macOS_Maui(baasDifferentiators) + ["test-macos-maui"] = TestJobs.macOS_Maui() ["test-ios-xamarin"] = TestJobs.iOS_Xamarin() - ["test-ios-maui"] = TestJobs.iOS_Maui(baasDifferentiators) - ["test-tvos"] = TestJobs.tvOS(baasDifferentiators) + ["test-ios-maui"] = TestJobs.iOS_Maui() + ["test-tvos"] = TestJobs.tvOS() ["test-android-xamarin"] = TestJobs.android_Xamarin() - ["test-android-maui"] = TestJobs.android_Maui(baasDifferentiators) + ["test-android-maui"] = TestJobs.android_Maui() ["test-woven-classes"] = TestJobs.wovenClasses() ["test-source-generation"] = TestJobs.sourceGeneration() ["test-weaver"] = TestJobs.weaver() - ["test-code-coverage"] = TestJobs.codeCoverage(job_Wrappers, baasDifferentiators) - ["cleanup-baas"] = (BaaS.cleanup(baasDifferentiators)) { - needs = baasDifferentiators.toList().map((d) -> "test-\(d)").toListing() - } + ["test-code-coverage"] = TestJobs.codeCoverage(job_Wrappers) ["verify-namespaces"] = Lint.verifyNamespaces() ["lint"] = Lint.lint() } diff --git a/.github/pkl-workflows/helpers/Package.pkl b/.github/pkl-workflows/helpers/Package.pkl index 782e104448..386a1a9f34 100644 --- a/.github/pkl-workflows/helpers/Package.pkl +++ b/.github/pkl-workflows/helpers/Package.pkl @@ -22,8 +22,7 @@ function nuget(shouldBuildDocsCondition: String): gha.Job = new { `timeout-minutes` = 30 steps { ...Steps.checkout(false) - ...Steps.setupAndroid() - ...Steps.setupWorkloads("tvos ios maccatalyst android", "8.0.x") + Steps.setupDotnet("8.0.x") setVersionSuffix ...Steps.fetchWrappers(null) ...Steps.msbuild(new Steps.MSBuildConfig { diff --git a/.github/pkl-workflows/helpers/Steps.pkl b/.github/pkl-workflows/helpers/Steps.pkl index 2abac1bf9f..6d6b932dbf 100644 --- a/.github/pkl-workflows/helpers/Steps.pkl +++ b/.github/pkl-workflows/helpers/Steps.pkl @@ -107,11 +107,11 @@ const function fetchWrappers(wrappers: List(every((wrapper) -> Common.wr } }) -const function setupWorkloads(workloads: String?, dotnetVersion: String?): Listing = new { - setupDotnet(dotnetVersion) +const function setupMaui(): Listing = new { + setupDotnet(null) new { - name = "Setup workloads" - run = "dotnet workload install \(workloads ?? "android ${{ (runner.os != 'Linux' && 'tvos ios maccatalyst') || '' }}")" + name = "Setup Maui workload" + run = "dotnet workload install maui" } } diff --git a/.github/pkl-workflows/helpers/Test.pkl b/.github/pkl-workflows/helpers/Test.pkl index 7b6dcc7c0b..d7a7027481 100644 --- a/.github/pkl-workflows/helpers/Test.pkl +++ b/.github/pkl-workflows/helpers/Test.pkl @@ -3,7 +3,6 @@ module test import "../GithubAction/GithubAction.pkl" as gha import "Common.pkl" import "Steps.pkl" -import "BaaS.pkl" import "Package.pkl" local const actionReportTestResults = "dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5" @@ -12,12 +11,10 @@ local const outputFile = "TestResults.xml" local const executableExpression = "${{ steps.dotnet-publish.outputs.executable-path }}" // Public test targets -function netFramework(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function netFramework(): gha.StepJobBase = testJob( new TestConfig { title = ".NET Framework" needsPackages = true - syncDifferentiator = "net-framework" - syncDifferentiators = _syncDifferentiators }, new gha.WindowsLatest{}, null, @@ -34,7 +31,7 @@ function netFramework(_syncDifferentiators: Listing): gha.StepJobBase = }) new { name = "Run the tests" - run = "./Tests/Realm.Tests/bin/\(Common.configuration)/net461/Realm.Tests.exe --result=\(outputFile) --labels=After \(baasTestArgs(config))" + run = "./Tests/Realm.Tests/bin/\(Common.configuration)/net461/Realm.Tests.exe --result=\(outputFile) --labels=After" } ...reportTestResults(config) }) @@ -102,7 +99,7 @@ function weaver(): gha.StepJobBase = testJob( }, (config) -> new Listing { ...Steps.checkout(false) - ...Steps.setupWorkloads(null, "6.0.x") + Steps.setupDotnet("6.0.x") ...Steps.dotnetPublish("Tests/Weaver/Realm.Fody.Tests", "net6.0", "${{ matrix.os.runtime }}", new Mapping{}).toList() ...dotnetRunTests(false) ...reportTestResults(config) @@ -132,7 +129,7 @@ function sourceGeneration(): gha.StepJobBase = testJob( null, (config) -> new Listing { ...Steps.checkout(false) - ...Steps.setupWorkloads(null, "6.0.x") + Steps.setupDotnet("6.0.x") ...Steps.dotnetPublish("Tests/SourceGenerators/Realm.SourceGenerator.Tests", "net6.0", "win-x64", new Mapping{}).toList() ...dotnetRunTests(false) ...reportTestResults(config) @@ -155,12 +152,10 @@ function wovenClasses(): gha.StepJobBase = testJob( } ) -function tvOS(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function tvOS(): gha.StepJobBase = testJob( new TestConfig { needsPackages = true title = "Xamarin.tvOS" - syncDifferentiator = "tvos" - syncDifferentiators = _syncDifferentiators }, "macos-12", null, @@ -176,7 +171,7 @@ function tvOS(_syncDifferentiators: Listing): gha.Ste }) Steps.runSimulator(new Steps.SimulatorConfig{ appPath = "Tests/Tests.XamarinTVOS/bin/iPhoneSimulator/\(Common.configuration)/Tests.XamarinTVOS.app" - arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All \(baasTestArgs(config))" + arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All" bundleId = "io.realm.Tests-XamarinTVOS" iphoneToSimulate = "Apple-TV-1080p" os = "tvOS" @@ -203,7 +198,7 @@ function iOS_Xamarin(): gha.StepJobBase = testJob( }) Steps.runSimulator(new Steps.SimulatorConfig{ appPath = "Tests/Tests.iOS/bin/iPhoneSimulator/\(Common.configuration)/Tests.iOS.app" - arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All \(baasTestArgs(config))" + arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All " bundleId = "io.realm.dotnettests" iphoneToSimulate = "iPhone-8" os = "iOS" @@ -211,24 +206,22 @@ function iOS_Xamarin(): gha.StepJobBase = testJob( ...reportTestResults(config) }) -function iOS_Maui(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function iOS_Maui(): gha.StepJobBase = testJob( new TestConfig { needsPackages = true title = "Maui.iOS" - syncDifferentiator = "ios-maui" - syncDifferentiators = _syncDifferentiators transformResults = true }, - "macos-13", + "macos-14", null, (config) -> new Listing { ...prepareTests(config) - ...Steps.setupWorkloads("maui", null) + ...Steps.setupMaui() Steps.setupXcode("latest-stable") Steps.dotnetBuild("Tests/Tests.Maui", "net8.0-ios", null, getTestProps(false)) Steps.runSimulator(new Steps.SimulatorConfig{ - appPath = "Tests/Tests.Maui/bin/\(Common.configuration)/net8.0-ios/iossimulator-x64/Tests.Maui.app" - arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All \(baasTestArgs(config))" + appPath = "Tests/Tests.Maui/bin/\(Common.configuration)/net8.0-ios/iossimulator-arm64/Tests.Maui.app" + arguments = "--headless --result=${{ github.workspace }}/\(outputFile) --labels=All" bundleId = "io.realm.mauitests" iphoneToSimulate = "iPhone-15" os = "iOS" @@ -257,34 +250,30 @@ function macOS_Xamarin(): gha.StepJobBase = testJob( ...reportTestResults(config) }) -function macOS_Maui(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function macOS_Maui(): gha.StepJobBase = testJob( new TestConfig { needsPackages = true title = "Maui.MacCatalyst" - syncDifferentiator = "macos-maui" - syncDifferentiators = _syncDifferentiators transformResults = true }, - "macos-13", + "macos-14", null, (config) -> new Listing { ...prepareTests(config) - ...Steps.setupWorkloads("maui", null) + ...Steps.setupMaui() Steps.setupXcode("latest-stable") Steps.dotnetBuild("Tests/Tests.Maui", "net8.0-maccatalyst", null, getTestProps(false)) new { name = "Run the tests" - run = "Tests/Tests.Maui/bin/\(Common.configuration)/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/\(outputFile) --labels=All \(baasTestArgs(config))" + run = "Tests/Tests.Maui/bin/\(Common.configuration)/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/\(outputFile) --labels=All" } ...reportTestResults(config) }) -function uwp(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function uwp(): gha.StepJobBase = testJob( new TestConfig { needsPackages = true title = "UWP" - syncDifferentiators = _syncDifferentiators - syncDifferentiator = "uwp" }, new gha.WindowsLatest{}, null, @@ -314,7 +303,7 @@ function uwp(_syncDifferentiators: Listing): gha.Step }) new { name = "Run the tests" - run = "./Tests/Tests.UWP/RunTests.ps1 -ExtraAppArgs '\(baasTestArgs(config))'" + run = "./Tests/Tests.UWP/RunTests.ps1" shell = "powershell" } ...reportTestResultsWithCustomFile("${{ env.TEST_RESULTS }}", config) @@ -347,12 +336,10 @@ function android_Xamarin(): gha.StepJobBase = testJob( ...reportTestResultsWithCustomFile("${{ steps.run_tests.outputs.test-results-path }}", config) }) -function android_Maui(_syncDifferentiators: Listing): gha.StepJobBase = testJob( +function android_Maui(): gha.StepJobBase = testJob( new TestConfig { needsPackages = true title = "Maui.Android" - syncDifferentiator = "android-maui" - syncDifferentiators = _syncDifferentiators transformResults = true }, new gha.WindowsLatest{}, @@ -360,7 +347,7 @@ function android_Maui(_syncDifferentiators: Listing): (config) -> new Listing { Steps.setupJDK() ...prepareTests(config) - ...Steps.setupWorkloads("maui", null) + ...Steps.setupMaui() ...Steps.dotnetPublish("Tests/Tests.Maui", "net8.0-android", /* runtime */ null, getTestProps(false)) ...Steps.runDeviceFarm(new Steps.DeviceFarmConfig { apkPath = "${{ github.workspace }}/Tests/Tests.Maui/bin/Release/net8.0-android/publish/io.realm.mauitests-Signed.apk" @@ -369,11 +356,9 @@ function android_Maui(_syncDifferentiators: Listing): ...reportTestResultsWithCustomFile("${{ steps.run_tests.outputs.test-results-path }}", config) }) -function codeCoverage(wrappersJob: String, _syncDifferentiators: Listing): gha.StepJobBase = (testJob( +function codeCoverage(wrappersJob: String): gha.StepJobBase = (testJob( new TestConfig { title = "Code Coverage" - syncDifferentiator = "code-coverage" - syncDifferentiators = _syncDifferentiators usedWrappers = List("linux-x86_64") }, new gha.UbuntuLatest{}, @@ -391,7 +376,7 @@ function codeCoverage(wrappersJob: String, _syncDifferentiators: Listing enableCoreDumps(true) archiveCoreDump() ...Steps.publishCoverage("./report.lcov") @@ -531,9 +516,6 @@ local function testJob(config: TestConfig, runsOn: gha.Machine | String, _strate when (config.needsPackages) { needs { Common.job_Packages - when (config.runSyncTests) { - Common.job_Baas - } } } `if` = Common.ifNotCanceledCondition @@ -545,7 +527,6 @@ local function prepareTests(config: TestConfig(needsPackages == true || !usedWra ...Steps.checkout(false) ...cleanWorkspace(config.shouldCleanWorkspace) ...fetchTestArtifacts(config.usedWrappers) - ...BaaS.deployStep(config.syncDifferentiator, config.runSyncTests) } local function cleanWorkspace(shouldClean: Boolean): Listing = new Listing { @@ -564,8 +545,6 @@ local function buildTests(config: Steps.MSBuildConfig): Listing = Step } }) -local function baasTestArgs(config: TestConfig): String = if (config.runSyncTests) " --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=\(config.syncDifferentiator)-${{ github.run_id }}-${{ github.run_attempt }}" else "" - local function reportTestResults(config: TestConfig): Listing = reportTestResultsWithCustomFile(outputFile, config) local function reportTestResultsWithCustomFile(_outputFile: String, config: TestConfig): Listing = new { @@ -637,8 +616,5 @@ local class TestConfig { needsPackages: Boolean = false usedWrappers: List(every((wrapper) -> Common.wrapperBinaryNames.contains(wrapper))) shouldCleanWorkspace: Boolean = false - syncDifferentiator: Common.SyncDifferentiator? = null transformResults: Boolean = false - syncDifferentiators: Listing? - runSyncTests: Boolean = syncDifferentiator != null && (syncDifferentiators?.toList()?.contains(syncDifferentiator) ?? false) } diff --git a/.github/pkl-workflows/main.pkl b/.github/pkl-workflows/main.pkl index 20e7df3ca8..c721c84f04 100644 --- a/.github/pkl-workflows/main.pkl +++ b/.github/pkl-workflows/main.pkl @@ -6,16 +6,6 @@ import "helpers/Common.pkl" import "helpers/Steps.pkl" import "helpers/Test.pkl" as TestJobs -local baasDifferentiators: Listing = new { - "code-coverage" - "net-framework" - "uwp" - "macos-maui" - "android-maui" - "ios-maui" - "macos-maui" -} - local netCoreFrameworks: Listing = new { "net6.0" "net8.0" @@ -46,7 +36,7 @@ on { env = Common.defaultEnv -jobs = (Common.defaultBuildJobs(baasDifferentiators, netCoreFrameworks)) { +jobs = (Common.defaultBuildJobs(netCoreFrameworks)) { ["publish-packages-to-sleet"] = new Job { `runs-on` = new UbuntuLatest{} name = "Publish package to S3" diff --git a/.github/pkl-workflows/pr.pkl b/.github/pkl-workflows/pr.pkl index 3f8de3eaef..684673dcae 100644 --- a/.github/pkl-workflows/pr.pkl +++ b/.github/pkl-workflows/pr.pkl @@ -4,10 +4,6 @@ amends "GithubAction/GithubAction.pkl" import "helpers/Common.pkl" -local baasDifferentiators: Listing = new { - "code-coverage" -} - local netCoreFrameworks: Listing = new { "net6.0" } @@ -41,4 +37,4 @@ concurrency { `cancel-in-progress` = true } -jobs = Common.defaultBuildJobs(baasDifferentiators, netCoreFrameworks) \ No newline at end of file +jobs = Common.defaultBuildJobs(netCoreFrameworks) \ No newline at end of file diff --git a/.github/pkl-workflows/publish-release.pkl b/.github/pkl-workflows/publish-release.pkl index e4023f8cc3..263ca21385 100644 --- a/.github/pkl-workflows/publish-release.pkl +++ b/.github/pkl-workflows/publish-release.pkl @@ -6,13 +6,16 @@ import "helpers/Common.pkl" import "helpers/Steps.pkl" import "helpers/Actions.pkl" +local main_job = "main" +local deployment_step = "deploy-pages" + name = "Publish Release" on { workflow_dispatch{} } env = Common.defaultEnv jobs { - ["main"] = new Job { + [main_job] = new Job { `runs-on` = new WindowsLatest{} name = "Publish Release" environment = "Production" @@ -20,8 +23,6 @@ jobs { Steps.checkoutWithoutMatchers(false) Steps.downloadAllArtifacts() Steps.readVersionFromPackage() - Steps.configureAWSCredentials("DOCS_S3_ACCESS_KEY", "DOCS_S3_SECRET_KEY", "us-east-2") - uploadDocs() ...uploadToNuGet() ...Steps.uploadToNPM("latest") ...mergeReleasePR() @@ -30,18 +31,47 @@ jobs { postReleaseToSlack() } } + ["deploy-docs"] = new Job { + `runs-on` = new UbuntuLatest{} + name = "Deploy Docs" + environment { + name = "github-pages" + url = "${{ steps.\(deployment_step).outputs.page_url }}" + } + needs { + main_job + } + permissions { + pages = "write" + `id-token` = "write" + contents = "read" + } + steps { + Steps.checkoutWithoutMatchers(false) + Steps.downloadAllArtifacts() + ...uploadDocs() + } + } } -local function uploadDocs(): Step = new { - name = "Upload docs" - run = #""" - Expand-Archive -Path Realm/packages/Docs.zip/Docs.zip -DestinationPath Realm/packages - $versions = "${{ steps.\#(Steps.getVersionStepName).outputs.\#(Steps.getVersionOutput) }}", "latest" - Foreach ($ver in $versions) - { - aws s3 sync --acl public-read "${{ github.workspace }}\Realm\packages\_site" s3://realm-sdks/docs/realm-sdks/dotnet/$ver/ +local function uploadDocs(): Listing = new { + new Step { + name = "Expand docs" + run = "Expand-Archive -Path Realm/packages/Docs.zip/Docs.zip -DestinationPath Realm/packages" + shell = "pwsh" + } + new Step { + name = "Upload pages artifact" + uses = Actions.uploadPagesArtifact + with { + ["path"] = "${{ github.workspace }}/Realm/packages/_site" } - """# + } + new Step { + name = "Deploy pages" + uses = Actions.deployPages + id = deployment_step + } } local function uploadToNuGet(): Listing = new { diff --git a/.github/workflows/Issue-Needs-Attention.yml b/.github/workflows/Issue-Needs-Attention.yml deleted file mode 100644 index 842194ac42..0000000000 --- a/.github/workflows/Issue-Needs-Attention.yml +++ /dev/null @@ -1,12 +0,0 @@ -# NOTE: This is a common file that is overwritten by realm/ci-actions sync service -# and should only be modified in that repository. - -name: Issue Needs Attention -# This workflow is triggered on issue comments. -on: - issue_comment: - types: created - -jobs: - applyNeedsAttentionLabel: - uses: realm/ci-actions/.github/workflows/issue-needs-attention.yml@main diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml deleted file mode 100644 index 1842f5bf39..0000000000 --- a/.github/workflows/check-pr-title.yml +++ /dev/null @@ -1,22 +0,0 @@ -# NOTE: This is a common file that is overwritten by realm/ci-actions sync service -# and should only be modified in that repository. - -name: "Check PR Title" -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled, converted_to_draft, edited] - -jobs: - check-pr-title: - name: Check PR Title - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: false - - name: Enforce PR title - uses: realm/ci-actions/title-checker@main - with: - regex: R[A-Z]{2,6}-[0-9]{1,6} - error-hint: Invalid PR title. Make sure it's prefixed with the JIRA ticket the PR addresses or add the no-jira-ticket label. - ignore-labels: 'no-jira-ticket' \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index a7d2f8b2bf..0000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,75 +0,0 @@ -# Do not modify! -# This file was generated from a template using https://github.com/apple/pkl - -name: CodeQL -'on': - pull_request: - paths: - - '**.cs' - - '**.cpp' - - '**.hpp' - - '**.csproj' - - .github/workflows/codeql.yml - branches: - - main - push: - branches: - - main -env: - REALM_DISABLE_ANALYTICS: true -concurrency: - group: codeql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -jobs: - analyze-csharp: - name: Analyze C# - permissions: - actions: read - contents: read - security-events: write - runs-on: windows-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Initialize CodeQL - uses: github/codeql-action/init@a57c67b89589d2d13d5ac85a9fc4679c7539f94c - with: - languages: csharp - config: |- - queries: - - uses: security-and-quality - query-filters: - - exclude: - id: cs/call-to-unmanaged-code - - exclude: - id: cs/unmanaged-code - - name: Setup JDK - uses: actions/setup-java@2e74cbce18569d23ca8b812590dbb83f13ac7c5a - with: - distribution: microsoft - java-version: 17 - - name: Setup Android - uses: android-actions/setup-android@e1f5280adf78cf863c0fa43ffabc64a9cd08153f - - name: Install SDK platform 21 - run: sdkmanager --install "platforms;android-21" - - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 - with: - dotnet-version: 8.0.x - - name: Setup workloads - run: dotnet workload install tvos ios maccatalyst android - - name: Add msbuild to PATH - if: ${{ runner.os == 'Windows' }} - uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 - - name: Build Realm/Realm - run: msbuild Realm/Realm -restore -p:Configuration=Release -p:UseSharedCompilation=false - - name: Build Realm/Realm.PlatformHelpers - run: msbuild Realm/Realm.PlatformHelpers -restore -p:Configuration=Release -p:UseSharedCompilation=false - - name: Build Realm/Realm.UnityUtils - run: msbuild Realm/Realm.UnityUtils -restore -p:Configuration=Release -p:UseSharedCompilation=false - - name: Build Realm/Realm.UnityWeaver - run: msbuild Realm/Realm.UnityWeaver -restore -p:Configuration=Release -p:UseSharedCompilation=false - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@a57c67b89589d2d13d5ac85a9fc4679c7539f94c diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 92b2880a4c..ac01a71b57 100755 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,7 @@ name: Main Build 'on': push: branches: - - main + - community workflow_dispatch: inputs: publish-prerelease: @@ -17,43 +17,11 @@ name: Main Build required: false type: boolean env: - REALM_DISABLE_ANALYTICS: true DOTNET_NOLOGO: true jobs: build-wrappers: name: Wrappers uses: ./.github/workflows/wrappers.yml - deploy-baas: - name: Deploy BaaS - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 - with: - dotnet-version: 8.0.x - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=${{ matrix.differentiator }}-${{ github.run_id }}-${{ github.run_attempt }} - strategy: - matrix: - differentiator: - - code-coverage - - net-framework - - uwp - - macos-maui - - android-maui - - ios-maui - - macos-maui - fail-fast: false build-packages: name: Package NuGet needs: @@ -73,20 +41,9 @@ jobs: run: |- echo "::add-matcher::.github/problem-matchers/csc.json" echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Setup JDK - uses: actions/setup-java@2e74cbce18569d23ca8b812590dbb83f13ac7c5a - with: - distribution: microsoft - java-version: 17 - - name: Setup Android - uses: android-actions/setup-android@e1f5280adf78cf863c0fa43ffabc64a9cd08153f - - name: Install SDK platform 21 - run: sdkmanager --install "platforms;android-21" - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads - run: dotnet workload install tvos ios maccatalyst android - name: Set version suffix id: set-version-suffix run: |- @@ -209,8 +166,6 @@ jobs: uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 - name: Build Realm/Realm run: msbuild Realm/Realm -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - - name: Build Realm/Realm.PlatformHelpers - run: msbuild Realm/Realm.PlatformHelpers -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - name: Build Realm/Realm.UnityUtils run: msbuild Realm/Realm.UnityUtils -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - name: Build Realm/Realm.UnityWeaver @@ -229,13 +184,6 @@ jobs: path: Realm/packages/Realm.${{ steps.get-version.outputs.package_version }}.*nupkg retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} if-no-files-found: error - - name: Store artifacts for Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - uses: actions/upload-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - path: Realm/packages/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}.*nupkg - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - name: Store artifacts for Realm.UnityUtils.${{ steps.get-version.outputs.package_version }} uses: actions/upload-artifact@v4 with: @@ -317,11 +265,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Fetch Realm.UnityUtils uses: actions/download-artifact@v4 with: @@ -350,168 +293,10 @@ jobs: path: Tests/Tests.Unity retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} if-no-files-found: error - build-unity-tests-linux: - name: Build Unity linux - needs: - - build-packages - - build-unity - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: - - unity - - linux - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Cleanup Workspace - run: git clean -fdx - - name: Fetch io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - uses: actions/download-artifact@v4 - with: - name: io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - path: Realm/Realm.Unity - - name: Fetch UnityTests - uses: actions/download-artifact@v4 - with: - name: UnityTests - path: Tests/Tests.Unity - - name: Build Unity Tests - run: unity-editor -runTests -batchmode -projectPath ${{ github.workspace }}/Tests/Tests.Unity -testPlatform StandaloneLinux64 -testSettingsFile ${{ github.workspace }}/Tests/Tests.Unity/.TestConfigs/Mono-Net4.json -logFile - - - name: Store artifacts for UnityTestsRunner.linux - uses: actions/upload-artifact@v4 - with: - name: UnityTestsRunner.linux - path: Tests/Tests.Unity/Player_StandaloneLinux64_Mono-Net4/ - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - run-unity-tests-linux: - name: Test Unity linux - needs: - - build-unity-tests-linux - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Fetch UnityTestsRunner.linux - uses: actions/download-artifact@v4 - with: - name: UnityTestsRunner.linux - path: TestRunner - - name: Install xvfb - run: sudo apt install -y xvfb libglu1 libxcursor1 - - name: Run Tests - run: |- - chmod +x ${{ github.workspace }}/TestRunner/PlayerWithTests.x86_64 - xvfb-run --auto-servernum --server-args='-screen 0 640x480x24:32' ${{ github.workspace }}/TestRunner/PlayerWithTests.x86_64 -logFile - --result=${{ github.workspace }}/TestResults.xml - - name: Publish Unit Test Results - if: always() - uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 - with: - name: Results Unity linux Mono-Net4 - path: TestResults.xml - reporter: java-junit - list-suites: failed - path-replace-backslashes: true - fail-on-error: true - build-unity-tests-windows: - name: Build Unity windows - needs: - - build-packages - - build-unity - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: - - unity - - windows - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Cleanup Workspace - run: git clean -fdx - - name: Fetch io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - uses: actions/download-artifact@v4 - with: - name: io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - path: Realm/Realm.Unity - - name: Fetch UnityTests - uses: actions/download-artifact@v4 - with: - name: UnityTests - path: Tests/Tests.Unity - - name: Build Unity Tests - run: unity-editor -runTests -batchmode -projectPath ${{ github.workspace }}/Tests/Tests.Unity -testPlatform StandaloneWindows64 -testSettingsFile ${{ github.workspace }}/Tests/Tests.Unity/.TestConfigs/Mono-Net4.json -logFile build.log - - name: Store artifacts for UnityTestsRunner.windows - uses: actions/upload-artifact@v4 - with: - name: UnityTestsRunner.windows - path: Tests/Tests.Unity/Player_StandaloneWindows64_Mono-Net4/ - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - run-unity-tests-windows: - name: Test Unity windows - needs: - - build-unity-tests-windows - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: windows-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Fetch UnityTestsRunner.windows - uses: actions/download-artifact@v4 - with: - name: UnityTestsRunner.windows - path: TestRunner - - name: Run Tests - run: |- - Start-Process ${{ github.workspace }}\TestRunner\PlayerWithTests.exe -Wait -ArgumentList "-logFile","${{ github.workspace }}\test.log","--result=${{ github.workspace }}\TestResults.xml" - cat ${{ github.workspace }}\test.log - shell: pwsh - - name: Publish Unit Test Results - if: always() - uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 - with: - name: Results Unity windows Mono-Net4 - path: TestResults.xml - reporter: java-junit - list-suites: failed - path-replace-backslashes: true - fail-on-error: true test-net-framework: name: Test .NET Framework needs: - build-packages - - deploy-baas if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 runs-on: windows-latest @@ -530,21 +315,13 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=net-framework-${{ github.run_id }}-${{ github.run_attempt }} - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 - name: Build Tests/Realm.Tests run: msbuild Tests/Realm.Tests -restore -p:Configuration=Release -p:TargetFramework=net461 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} -p:RealmTestsStandaloneExe=true - name: Run the tests - run: ./Tests/Realm.Tests/bin/Release/net461/Realm.Tests.exe --result=TestResults.xml --labels=After --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=net-framework-${{ github.run_id }}-${{ github.run_attempt }} + run: ./Tests/Realm.Tests/bin/Release/net461/Realm.Tests.exe --result=TestResults.xml --labels=After - name: Publish Unit Test Results if: always() uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 @@ -559,7 +336,6 @@ jobs: name: Test UWP needs: - build-packages - - deploy-baas if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 runs-on: windows-latest @@ -578,14 +354,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=uwp-${{ github.run_id }}-${{ github.run_attempt }} - name: Import test certificate run: |- $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}") @@ -599,7 +367,7 @@ jobs: - name: Build Tests/Tests.UWP run: msbuild Tests/Tests.UWP -restore -p:Configuration=Release -p:AppxBundle=Always -p:PackageCertificateKeyFile=${{ github.workspace }}\Tests\Tests.UWP\Tests.UWP_TemporaryKey.pfx -p:PackageCertificatePassword=${{ secrets.Pfx_Password }} -p:UseDotNetNativeToolchain=false -p:AppxBundlePlatforms=x64 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} - name: Run the tests - run: ./Tests/Tests.UWP/RunTests.ps1 -ExtraAppArgs ' --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=uwp-${{ github.run_id }}-${{ github.run_attempt }}' + run: ./Tests/Tests.UWP/RunTests.ps1 shell: powershell - name: Publish Unit Test Results if: always() @@ -635,11 +403,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Clear nuget cache if: ${{ matrix.os.runner == 'win81' }} run: dotnet nuget locals all --clear @@ -721,11 +484,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -747,10 +505,9 @@ jobs: name: Test Maui.MacCatalyst needs: - build-packages - - deploy-baas if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 - runs-on: macos-13 + runs-on: macos-14 steps: - name: Checkout Code uses: actions/checkout@v4 @@ -766,18 +523,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=macos-maui-${{ github.run_id }}-${{ github.run_attempt }} - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Setup Xcode uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd @@ -786,7 +535,7 @@ jobs: - name: Build Tests/Tests.Maui run: dotnet build Tests/Tests.Maui -c Release -f net8.0-maccatalyst -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} - name: Run the tests - run: Tests/Tests.Maui/bin/Release/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/TestResults.xml --labels=All --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=macos-maui-${{ github.run_id }}-${{ github.run_attempt }} + run: Tests/Tests.Maui/bin/Release/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/TestResults.xml --labels=All - name: Transform Results run: xsltproc --output TestResults.xml_transformed.xml Tests/Realm.Tests/EmbeddedResources/nunit3-junit.xslt TestResults.xml - name: Publish Unit Test Results @@ -821,11 +570,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -853,10 +597,9 @@ jobs: name: Test Maui.iOS needs: - build-packages - - deploy-baas if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 - runs-on: macos-13 + runs-on: macos-14 steps: - name: Checkout Code uses: actions/checkout@v4 @@ -872,18 +615,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=ios-maui-${{ github.run_id }}-${{ github.run_attempt }} - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Setup Xcode uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd @@ -894,10 +629,10 @@ jobs: - name: Run on Simulator uses: realm/ci-actions/run-ios-simulator@6418e15ed9bbdb19b7d456a347e5623779f95cdf with: - appPath: Tests/Tests.Maui/bin/Release/net8.0-ios/iossimulator-x64/Tests.Maui.app + appPath: Tests/Tests.Maui/bin/Release/net8.0-ios/iossimulator-arm64/Tests.Maui.app bundleId: io.realm.mauitests iphoneToSimulate: iPhone-15 - arguments: --headless --result=${{ github.workspace }}/TestResults.xml --labels=All --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=ios-maui-${{ github.run_id }}-${{ github.run_attempt }} + arguments: --headless --result=${{ github.workspace }}/TestResults.xml --labels=All os: iOS - name: Transform Results run: xsltproc --output TestResults.xml_transformed.xml Tests/Realm.Tests/EmbeddedResources/nunit3-junit.xslt TestResults.xml @@ -933,11 +668,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -949,7 +679,7 @@ jobs: appPath: Tests/Tests.XamarinTVOS/bin/iPhoneSimulator/Release/Tests.XamarinTVOS.app bundleId: io.realm.Tests-XamarinTVOS iphoneToSimulate: Apple-TV-1080p - arguments: '--headless --result=${{ github.workspace }}/TestResults.xml --labels=All ' + arguments: --headless --result=${{ github.workspace }}/TestResults.xml --labels=All os: tvOS - name: Publish Unit Test Results if: always() @@ -988,11 +718,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -1026,7 +751,6 @@ jobs: name: Test Maui.Android needs: - build-packages - - deploy-baas if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 runs-on: windows-latest @@ -1050,18 +774,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=android-maui-${{ github.run_id }}-${{ github.run_attempt }} - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Publish Tests/Tests.Maui run: dotnet publish Tests/Tests.Maui -c Release -f net8.0-android -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} --no-self-contained @@ -1117,11 +833,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Publish Tests/Realm.Tests run: dotnet publish Tests/Realm.Tests -c Release -f net8.0 -r win-x64 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} -p:RealmTestsStandaloneExe=true -p:TestWeavedClasses=true --no-self-contained - name: Output executable path @@ -1158,8 +869,6 @@ jobs: - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 6.0.x - - name: Setup workloads - run: dotnet workload install android ${{ (runner.os != 'Linux' && 'tvos ios maccatalyst') || '' }} - name: Publish Tests/SourceGenerators/Realm.SourceGenerator.Tests run: dotnet publish Tests/SourceGenerators/Realm.SourceGenerator.Tests -c Release -f net6.0 -r win-x64 --no-self-contained - name: Output executable path @@ -1196,8 +905,6 @@ jobs: - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 6.0.x - - name: Setup workloads - run: dotnet workload install android ${{ (runner.os != 'Linux' && 'tvos ios maccatalyst') || '' }} - name: Publish Tests/Weaver/Realm.Fody.Tests run: dotnet publish Tests/Weaver/Realm.Fody.Tests -c Release -f net6.0 -r ${{ matrix.os.runtime }} --no-self-contained - name: Output executable path @@ -1248,9 +955,6 @@ jobs: with: name: wrappers-linux-x86_64 path: wrappers/build - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=code-coverage-${{ github.run_id }}-${{ github.run_attempt }} - name: Setup Coverlet & Report Generator run: |- dotnet tool install coverlet.console --tool-path tools @@ -1266,7 +970,7 @@ jobs: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_EnableCrashReport: 1 - run: ./tools/coverlet ./Tests/Realm.Tests/bin/Release/net8.0/linux-x64 -t ${{ steps.dotnet-publish.outputs.executable-path }} -a '--result=TestResults.xml --labels=After --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=code-coverage-${{ github.run_id }}-${{ github.run_attempt }}' -f lcov -o ./report.lcov --exclude '[Realm.Tests]*' --exclude '[Realm.Fody]*' --exclude '[Realm.PlatformHelpers]*' + run: ./tools/coverlet ./Tests/Realm.Tests/bin/Release/net8.0/linux-x64 -t ${{ steps.dotnet-publish.outputs.executable-path }} -a '--result=TestResults.xml --labels=After' -f lcov -o ./report.lcov --exclude '[Realm.Tests]*' --exclude '[Realm.Fody]*' - name: Archive core dump if: ${{ failure() && runner.os != 'Windows' }} uses: actions/upload-artifact@v4 @@ -1295,45 +999,6 @@ jobs: list-suites: failed path-replace-backslashes: true fail-on-error: true - cleanup-baas: - name: Cleanup BaaS - needs: - - test-code-coverage - - test-net-framework - - test-uwp - - test-macos-maui - - test-android-maui - - test-ios-maui - - test-macos-maui - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 - with: - dotnet-version: 8.0.x - - name: Terminate Baas - working-directory: Tools/DeployApps - run: dotnet run terminate-baas --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=${{ matrix.differentiator }}-${{ github.run_id }}-${{ github.run_attempt }} - strategy: - matrix: - differentiator: - - code-coverage - - net-framework - - uwp - - macos-maui - - android-maui - - ios-maui - - macos-maui - fail-fast: false verify-namespaces: name: Verify Namespaces needs: @@ -1350,11 +1015,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Fetch Realm.UnityUtils uses: actions/download-artifact@v4 with: @@ -1425,11 +1085,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x @@ -1443,8 +1098,6 @@ jobs: aws-region: us-east-1 - name: NuGet Publish Realm.${{ needs.build-packages.outputs.package_version }} run: sleet push ${{ github.workspace }}/Realm/packages/Realm.${{ needs.build-packages.outputs.package_version }}.nupkg --config ${{ github.workspace }}/.github/sleet.json --source NugetSource - - name: NuGet Publish Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - run: sleet push ${{ github.workspace }}/Realm/packages/Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }}.nupkg --config ${{ github.workspace }}/.github/sleet.json --source NugetSource test-xunit: name: Test xUnit Compatibility needs: @@ -1467,11 +1120,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Publish Tests/Tests.XUnit run: dotnet publish Tests/Tests.XUnit -c Release -f net6.0 -r win-x64 --no-self-contained - name: Output executable path @@ -1504,11 +1152,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Clear nuget cache run: dotnet nuget locals all --clear - name: Publish Tests/Benchmarks/PerformanceTests diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 755fcbe25f..4df2d29b10 100755 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,7 +20,6 @@ name: PR Build - .github/actions/** - Tests/Tests.Android/Properties/AndroidManifest.xml env: - REALM_DISABLE_ANALYTICS: true DOTNET_NOLOGO: true concurrency: group: ${{ github.head_ref || github.run_id }} @@ -29,31 +28,6 @@ jobs: build-wrappers: name: Wrappers uses: ./.github/workflows/wrappers.yml - deploy-baas: - name: Deploy BaaS - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 - with: - dotnet-version: 8.0.x - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=${{ matrix.differentiator }}-${{ github.run_id }}-${{ github.run_attempt }} - strategy: - matrix: - differentiator: - - code-coverage - fail-fast: false build-packages: name: Package NuGet needs: @@ -73,20 +47,9 @@ jobs: run: |- echo "::add-matcher::.github/problem-matchers/csc.json" echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Setup JDK - uses: actions/setup-java@2e74cbce18569d23ca8b812590dbb83f13ac7c5a - with: - distribution: microsoft - java-version: 17 - - name: Setup Android - uses: android-actions/setup-android@e1f5280adf78cf863c0fa43ffabc64a9cd08153f - - name: Install SDK platform 21 - run: sdkmanager --install "platforms;android-21" - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads - run: dotnet workload install tvos ios maccatalyst android - name: Set version suffix id: set-version-suffix run: |- @@ -209,8 +172,6 @@ jobs: uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 - name: Build Realm/Realm run: msbuild Realm/Realm -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - - name: Build Realm/Realm.PlatformHelpers - run: msbuild Realm/Realm.PlatformHelpers -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - name: Build Realm/Realm.UnityUtils run: msbuild Realm/Realm.UnityUtils -t:Pack -restore -p:Configuration=Release -p:PackageOutputPath=${{ github.workspace }}/Realm/packages -p:VersionSuffix=${{ steps.set-version-suffix.outputs.build_suffix }} - name: Build Realm/Realm.UnityWeaver @@ -229,13 +190,6 @@ jobs: path: Realm/packages/Realm.${{ steps.get-version.outputs.package_version }}.*nupkg retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} if-no-files-found: error - - name: Store artifacts for Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - uses: actions/upload-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - path: Realm/packages/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}.*nupkg - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - name: Store artifacts for Realm.UnityUtils.${{ steps.get-version.outputs.package_version }} uses: actions/upload-artifact@v4 with: @@ -317,11 +271,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Fetch Realm.UnityUtils uses: actions/download-artifact@v4 with: @@ -350,163 +299,6 @@ jobs: path: Tests/Tests.Unity retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} if-no-files-found: error - build-unity-tests-linux: - name: Build Unity linux - needs: - - build-packages - - build-unity - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: - - unity - - linux - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Cleanup Workspace - run: git clean -fdx - - name: Fetch io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - uses: actions/download-artifact@v4 - with: - name: io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - path: Realm/Realm.Unity - - name: Fetch UnityTests - uses: actions/download-artifact@v4 - with: - name: UnityTests - path: Tests/Tests.Unity - - name: Build Unity Tests - run: unity-editor -runTests -batchmode -projectPath ${{ github.workspace }}/Tests/Tests.Unity -testPlatform StandaloneLinux64 -testSettingsFile ${{ github.workspace }}/Tests/Tests.Unity/.TestConfigs/Mono-Net4.json -logFile - - - name: Store artifacts for UnityTestsRunner.linux - uses: actions/upload-artifact@v4 - with: - name: UnityTestsRunner.linux - path: Tests/Tests.Unity/Player_StandaloneLinux64_Mono-Net4/ - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - run-unity-tests-linux: - name: Test Unity linux - needs: - - build-unity-tests-linux - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Fetch UnityTestsRunner.linux - uses: actions/download-artifact@v4 - with: - name: UnityTestsRunner.linux - path: TestRunner - - name: Install xvfb - run: sudo apt install -y xvfb libglu1 libxcursor1 - - name: Run Tests - run: |- - chmod +x ${{ github.workspace }}/TestRunner/PlayerWithTests.x86_64 - xvfb-run --auto-servernum --server-args='-screen 0 640x480x24:32' ${{ github.workspace }}/TestRunner/PlayerWithTests.x86_64 -logFile - --result=${{ github.workspace }}/TestResults.xml - - name: Publish Unit Test Results - if: always() - uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 - with: - name: Results Unity linux Mono-Net4 - path: TestResults.xml - reporter: java-junit - list-suites: failed - path-replace-backslashes: true - fail-on-error: true - build-unity-tests-windows: - name: Build Unity windows - needs: - - build-packages - - build-unity - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: - - unity - - windows - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Cleanup Workspace - run: git clean -fdx - - name: Fetch io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - uses: actions/download-artifact@v4 - with: - name: io.realm.unity-${{ needs.build-packages.outputs.package_version }}.tgz - path: Realm/Realm.Unity - - name: Fetch UnityTests - uses: actions/download-artifact@v4 - with: - name: UnityTests - path: Tests/Tests.Unity - - name: Build Unity Tests - run: unity-editor -runTests -batchmode -projectPath ${{ github.workspace }}/Tests/Tests.Unity -testPlatform StandaloneWindows64 -testSettingsFile ${{ github.workspace }}/Tests/Tests.Unity/.TestConfigs/Mono-Net4.json -logFile build.log - - name: Store artifacts for UnityTestsRunner.windows - uses: actions/upload-artifact@v4 - with: - name: UnityTestsRunner.windows - path: Tests/Tests.Unity/Player_StandaloneWindows64_Mono-Net4/ - retention-days: ${{ github.event_name != 'pull_request' && 30 || 1 }} - if-no-files-found: error - run-unity-tests-windows: - name: Test Unity windows - needs: - - build-unity-tests-windows - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - timeout-minutes: 30 - runs-on: windows-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - name: Fetch UnityTestsRunner.windows - uses: actions/download-artifact@v4 - with: - name: UnityTestsRunner.windows - path: TestRunner - - name: Run Tests - run: |- - Start-Process ${{ github.workspace }}\TestRunner\PlayerWithTests.exe -Wait -ArgumentList "-logFile","${{ github.workspace }}\test.log","--result=${{ github.workspace }}\TestResults.xml" - cat ${{ github.workspace }}\test.log - shell: pwsh - - name: Publish Unit Test Results - if: always() - uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 - with: - name: Results Unity windows Mono-Net4 - path: TestResults.xml - reporter: java-junit - list-suites: failed - path-replace-backslashes: true - fail-on-error: true test-net-framework: name: Test .NET Framework needs: @@ -529,18 +321,13 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 - name: Build Tests/Realm.Tests run: msbuild Tests/Realm.Tests -restore -p:Configuration=Release -p:TargetFramework=net461 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} -p:RealmTestsStandaloneExe=true - name: Run the tests - run: './Tests/Realm.Tests/bin/Release/net461/Realm.Tests.exe --result=TestResults.xml --labels=After ' + run: ./Tests/Realm.Tests/bin/Release/net461/Realm.Tests.exe --result=TestResults.xml --labels=After - name: Publish Unit Test Results if: always() uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 @@ -573,11 +360,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Import test certificate run: |- $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}") @@ -591,7 +373,7 @@ jobs: - name: Build Tests/Tests.UWP run: msbuild Tests/Tests.UWP -restore -p:Configuration=Release -p:AppxBundle=Always -p:PackageCertificateKeyFile=${{ github.workspace }}\Tests\Tests.UWP\Tests.UWP_TemporaryKey.pfx -p:PackageCertificatePassword=${{ secrets.Pfx_Password }} -p:UseDotNetNativeToolchain=false -p:AppxBundlePlatforms=x64 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} - name: Run the tests - run: ./Tests/Tests.UWP/RunTests.ps1 -ExtraAppArgs '' + run: ./Tests/Tests.UWP/RunTests.ps1 shell: powershell - name: Publish Unit Test Results if: always() @@ -627,11 +409,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Clear nuget cache if: ${{ matrix.os.runner == 'win81' }} run: dotnet nuget locals all --clear @@ -712,11 +489,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -740,7 +512,7 @@ jobs: - build-packages if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 - runs-on: macos-13 + runs-on: macos-14 steps: - name: Checkout Code uses: actions/checkout@v4 @@ -756,15 +528,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Setup Xcode uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd @@ -773,7 +540,7 @@ jobs: - name: Build Tests/Tests.Maui run: dotnet build Tests/Tests.Maui -c Release -f net8.0-maccatalyst -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} - name: Run the tests - run: 'Tests/Tests.Maui/bin/Release/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/TestResults.xml --labels=All ' + run: Tests/Tests.Maui/bin/Release/net8.0-maccatalyst/maccatalyst-x64/Tests.Maui.app/Contents/MacOS/Tests.Maui --headless --result=${{ github.workspace }}/TestResults.xml --labels=All - name: Transform Results run: xsltproc --output TestResults.xml_transformed.xml Tests/Realm.Tests/EmbeddedResources/nunit3-junit.xslt TestResults.xml - name: Publish Unit Test Results @@ -808,11 +575,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -842,7 +604,7 @@ jobs: - build-packages if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') timeout-minutes: 60 - runs-on: macos-13 + runs-on: macos-14 steps: - name: Checkout Code uses: actions/checkout@v4 @@ -858,15 +620,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Setup Xcode uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd @@ -877,10 +634,10 @@ jobs: - name: Run on Simulator uses: realm/ci-actions/run-ios-simulator@6418e15ed9bbdb19b7d456a347e5623779f95cdf with: - appPath: Tests/Tests.Maui/bin/Release/net8.0-ios/iossimulator-x64/Tests.Maui.app + appPath: Tests/Tests.Maui/bin/Release/net8.0-ios/iossimulator-arm64/Tests.Maui.app bundleId: io.realm.mauitests iphoneToSimulate: iPhone-15 - arguments: '--headless --result=${{ github.workspace }}/TestResults.xml --labels=All ' + arguments: --headless --result=${{ github.workspace }}/TestResults.xml --labels=All os: iOS - name: Transform Results run: xsltproc --output TestResults.xml_transformed.xml Tests/Realm.Tests/EmbeddedResources/nunit3-junit.xslt TestResults.xml @@ -916,11 +673,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -932,7 +684,7 @@ jobs: appPath: Tests/Tests.XamarinTVOS/bin/iPhoneSimulator/Release/Tests.XamarinTVOS.app bundleId: io.realm.Tests-XamarinTVOS iphoneToSimulate: Apple-TV-1080p - arguments: '--headless --result=${{ github.workspace }}/TestResults.xml --labels=All ' + arguments: --headless --result=${{ github.workspace }}/TestResults.xml --labels=All os: tvOS - name: Publish Unit Test Results if: always() @@ -971,11 +723,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Add msbuild to PATH if: ${{ runner.os == 'Windows' }} uses: microsoft/setup-msbuild@70b70342ae97ca98d5eaad06cafd26d30f9592a9 @@ -1032,15 +779,10 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 8.0.x - - name: Setup workloads + - name: Setup Maui workload run: dotnet workload install maui - name: Publish Tests/Tests.Maui run: dotnet publish Tests/Tests.Maui -c Release -f net8.0-android -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} --no-self-contained @@ -1096,11 +838,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Publish Tests/Realm.Tests run: dotnet publish Tests/Realm.Tests -c Release -f net8.0 -r win-x64 -p:RestoreConfigFile=Tests/Test.NuGet.Config -p:UseRealmNupkgsWithVersion=${{ needs.build-packages.outputs.package_version }} -p:RealmTestsStandaloneExe=true -p:TestWeavedClasses=true --no-self-contained - name: Output executable path @@ -1137,8 +874,6 @@ jobs: - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 6.0.x - - name: Setup workloads - run: dotnet workload install android ${{ (runner.os != 'Linux' && 'tvos ios maccatalyst') || '' }} - name: Publish Tests/SourceGenerators/Realm.SourceGenerator.Tests run: dotnet publish Tests/SourceGenerators/Realm.SourceGenerator.Tests -c Release -f net6.0 -r win-x64 --no-self-contained - name: Output executable path @@ -1175,8 +910,6 @@ jobs: - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 with: dotnet-version: 6.0.x - - name: Setup workloads - run: dotnet workload install android ${{ (runner.os != 'Linux' && 'tvos ios maccatalyst') || '' }} - name: Publish Tests/Weaver/Realm.Fody.Tests run: dotnet publish Tests/Weaver/Realm.Fody.Tests -c Release -f net6.0 -r ${{ matrix.os.runtime }} --no-self-contained - name: Output executable path @@ -1227,9 +960,6 @@ jobs: with: name: wrappers-linux-x86_64 path: wrappers/build - - name: Deploy Apps - working-directory: Tools/DeployApps - run: dotnet run deploy-apps --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=code-coverage-${{ github.run_id }}-${{ github.run_attempt }} - name: Setup Coverlet & Report Generator run: |- dotnet tool install coverlet.console --tool-path tools @@ -1245,7 +975,7 @@ jobs: env: DOTNET_DbgEnableMiniDump: 1 DOTNET_EnableCrashReport: 1 - run: ./tools/coverlet ./Tests/Realm.Tests/bin/Release/net8.0/linux-x64 -t ${{ steps.dotnet-publish.outputs.executable-path }} -a '--result=TestResults.xml --labels=After --baasaas-api-key=${{ secrets.BAASAAS_API_KEY}} --baas-differentiator=code-coverage-${{ github.run_id }}-${{ github.run_attempt }}' -f lcov -o ./report.lcov --exclude '[Realm.Tests]*' --exclude '[Realm.Fody]*' --exclude '[Realm.PlatformHelpers]*' + run: ./tools/coverlet ./Tests/Realm.Tests/bin/Release/net8.0/linux-x64 -t ${{ steps.dotnet-publish.outputs.executable-path }} -a '--result=TestResults.xml --labels=After' -f lcov -o ./report.lcov --exclude '[Realm.Tests]*' --exclude '[Realm.Fody]*' - name: Archive core dump if: ${{ failure() && runner.os != 'Windows' }} uses: actions/upload-artifact@v4 @@ -1274,33 +1004,6 @@ jobs: list-suites: failed path-replace-backslashes: true fail-on-error: true - cleanup-baas: - name: Cleanup BaaS - needs: - - test-code-coverage - if: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - with: - submodules: false - ref: ${{ github.event.pull_request.head.sha }} - - name: Register problem matchers - run: |- - echo "::add-matcher::.github/problem-matchers/csc.json" - echo "::add-matcher::.github/problem-matchers/msvc.json" - - uses: actions/setup-dotnet@5d1464d5da459f3d7085106d52e499f4dc5d0f59 - with: - dotnet-version: 8.0.x - - name: Terminate Baas - working-directory: Tools/DeployApps - run: dotnet run terminate-baas --baasaas-api-key=${{ secrets.BAASAAS_API_KEY }} --baas-differentiator=${{ matrix.differentiator }}-${{ github.run_id }}-${{ github.run_attempt }} - strategy: - matrix: - differentiator: - - code-coverage - fail-fast: false verify-namespaces: name: Verify Namespaces needs: @@ -1317,11 +1020,6 @@ jobs: with: name: Realm.${{ needs.build-packages.outputs.package_version }} path: ${{ github.workspace }}/Realm/packages/ - - name: Fetch Realm.PlatformHelpers - uses: actions/download-artifact@v4 - with: - name: Realm.PlatformHelpers.${{ needs.build-packages.outputs.package_version }} - path: ${{ github.workspace }}/Realm/packages/ - name: Fetch Realm.UnityUtils uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 1f55cf8e06..406cb68522 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -23,12 +23,6 @@ jobs: pkgVersion=$(grep "\bVERSION:" dependencies.yml | cut -d: -f2) echo "core-version=$pkgVersion" >> $GITHUB_OUTPUT shell: bash - - name: Update Analytics.cs - uses: jacobtomlinson/gha-find-replace@0dfd0777cc234ef6947ec1f20873c632114c4167 #! 0.1.4 - with: - find: 'CoreVersion = "\w*"' - replace: 'CoreVersion = "${{ steps.get-core-version.outputs.core-version }}"' - include: Realm/Realm.Weaver/Analytics/Analytics.cs - name: Update Changelog id: update-changelog uses: realm/ci-actions/update-changelog@6418e15ed9bbdb19b7d456a347e5623779f95cdf diff --git a/.github/workflows/publish-prerelease.yml b/.github/workflows/publish-prerelease.yml index 45019006a8..453b593488 100644 --- a/.github/workflows/publish-prerelease.yml +++ b/.github/workflows/publish-prerelease.yml @@ -5,7 +5,6 @@ name: Publish Prerelease 'on': workflow_dispatch: {} env: - REALM_DISABLE_ANALYTICS: true DOTNET_NOLOGO: true jobs: main: @@ -44,8 +43,6 @@ jobs: aws-region: us-east-1 - name: NuGet Publish Realm.${{ steps.get-version.outputs.package_version }} run: sleet push ${{ github.workspace }}/Realm/packages/Realm.${{ steps.get-version.outputs.package_version }}/Realm.${{ steps.get-version.outputs.package_version }}.nupkg --config ${{ github.workspace }}/.github/sleet.json --source NugetSource - - name: NuGet Publish Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - run: sleet push ${{ github.workspace }}/Realm/packages/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}.nupkg --config ${{ github.workspace }}/.github/sleet.json --source NugetSource - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 with: node-version: 16.x diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 7ddf53b098..21620622c4 100755 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -5,7 +5,6 @@ name: Publish Release 'on': workflow_dispatch: {} env: - REALM_DISABLE_ANALYTICS: true DOTNET_NOLOGO: true jobs: main: @@ -32,24 +31,8 @@ jobs: pkgVersion=$(find . -type f -regex ".*Realm.[1-9].*.nupkg" -exec basename {} \; | sed -n 's/Realm\.\(.*\)\.nupkg$/\1/p') echo "package_version=$pkgVersion" >> $GITHUB_OUTPUT shell: bash - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4.0.2 - with: - aws-access-key-id: ${{ secrets.DOCS_S3_ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.DOCS_S3_SECRET_KEY }} - aws-region: us-east-2 - - name: Upload docs - run: |- - Expand-Archive -Path Realm/packages/Docs.zip/Docs.zip -DestinationPath Realm/packages - $versions = "${{ steps.get-version.outputs.package_version }}", "latest" - Foreach ($ver in $versions) - { - aws s3 sync --acl public-read "${{ github.workspace }}\Realm\packages\_site" s3://realm-sdks/docs/realm-sdks/dotnet/$ver/ - } - name: NuGet Publish Realm.${{ steps.get-version.outputs.package_version }} run: dotnet nuget push ${{ github.workspace }}/Realm/packages/Realm.${{ steps.get-version.outputs.package_version }}/Realm.${{ steps.get-version.outputs.package_version }}.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json - - name: NuGet Publish Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }} - run: dotnet nuget push ${{ github.workspace }}/Realm/packages/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}/Realm.PlatformHelpers.${{ steps.get-version.outputs.package_version }}.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 with: node-version: 16.x @@ -75,7 +58,7 @@ jobs: artifacts: Realm/packages/io.realm.unity-${{ steps.get-version.outputs.package_version }}.tgz/io.realm.unity-${{ steps.get-version.outputs.package_version }}.tgz bodyFile: Realm/packages/ExtractedChangelog/ExtractedChangelog.md name: ${{ steps.get-version.outputs.package_version }} - commit: main + commit: community tag: ${{ steps.get-version.outputs.package_version }} token: ${{ secrets.GITHUB_TOKEN }} draft: false @@ -105,7 +88,7 @@ jobs: title: Prepare for vNext body: Update Changelog for vNext delete-branch: true - base: main + base: community commit-message: Prepare for vNext labels: no-jira-ticket - name: Merge Pull Request @@ -121,3 +104,38 @@ jobs: sdk: .NET webhook-url: ${{ secrets.SLACK_RELEASE_WEBHOOK }} version: ${{ steps.get-version.outputs.package_version }} + deploy-docs: + name: Deploy Docs + needs: + - main + permissions: + contents: read + id-token: write + pages: write + environment: + name: github-pages + url: ${{ steps.deploy-pages.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + submodules: false + ref: ${{ github.event.pull_request.head.sha }} + - name: Download all artifacts + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe + with: + workflow: pr.yml + commit: ${{ github.sha }} + path: Realm/packages/ + workflow_conclusion: completed + - name: Expand docs + run: Expand-Archive -Path Realm/packages/Docs.zip/Docs.zip -DestinationPath Realm/packages + shell: pwsh + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ${{ github.workspace }}/Realm/packages/_site + - name: Deploy pages + id: deploy-pages + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/wipe-clusters.yml b/.github/workflows/wipe-clusters.yml deleted file mode 100644 index 7393299d67..0000000000 --- a/.github/workflows/wipe-clusters.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Wipe all clusters and apps - -on: - workflow_dispatch: -jobs: - main: - runs-on: ubuntu-latest - name: Wipe all clusters and apps - steps: - - uses: realm/ci-actions/mdb-realm/deleteAllClusters@6418e15ed9bbdb19b7d456a347e5623779f95cdf - with: - realmUrl: https://realm-qa.mongodb.com - atlasUrl: https://cloud-qa.mongodb.com - projectId: ${{ secrets.ATLAS_QA_PROJECT_ID }} - apiKey: ${{ secrets.ATLAS_QA_PUBLIC_API_KEY }} - privateApiKey: ${{ secrets.ATLAS_QA_PRIVATE_API_KEY }} diff --git a/.github/workflows/wrappers.yml b/.github/workflows/wrappers.yml index 3274f7d0c1..d110c3f09e 100755 --- a/.github/workflows/wrappers.yml +++ b/.github/workflows/wrappers.yml @@ -5,7 +5,6 @@ name: wrappers 'on': workflow_call: {} env: - REALM_DISABLE_ANALYTICS: true DOTNET_NOLOGO: true jobs: check-cache: diff --git a/CHANGELOG.md b/CHANGELOG.md index b55485ee09..9434836ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## vNext (TBD) +### Breaking Changes +* Removed all functionality related to App Services/Atlas Device Sync. + ### Enhancements * None @@ -10,7 +13,7 @@ * Realm Studio: 15.0.0 or later. ### Internal -* Using Core x.y.z. +* Using Core 20.0.1. ## 12.4.0 (2024-08-25) diff --git a/Realm - Windows.sln b/Realm - Windows.sln index df7daff957..d4df0d044d 100644 --- a/Realm - Windows.sln +++ b/Realm - Windows.sln @@ -55,8 +55,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Storage", "wrappers\cmake\W EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Realm.UnityUtils", "Realm\Realm.UnityUtils\Realm.UnityUtils.csproj", "{693D198D-C4F4-44B5-8C04-11544E0FE79A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sync", "wrappers\cmake\Windows\Debug-Win32\realm-core\src\realm\sync\Sync.vcxproj", "{CD7F1F01-CBD4-3CF6-892A-230FB024217C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Realm.UnityWeaver", "Realm\Realm.UnityWeaver\Realm.UnityWeaver.csproj", "{9322B1D0-B3F7-4D6A-9947-2FC11D3161A0}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Realm.Weaver", "Realm\Realm.Weaver\Realm.Weaver.shproj", "{89FDB2A1-6FC0-4604-9AEB-8B8DD8A88DEC}" @@ -87,12 +85,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGeneratorAssemblyToPr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Maui", "Tests\Tests.Maui\Tests.Maui.csproj", "{C84EBA8B-5F7F-4519-BB34-EDE93E275D66}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnalyticsAssembly", "Tests\Weaver\AnalyticsAssembly\AnalyticsAssembly.csproj", "{1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployApps", "Tools\DeployApps\DeployApps.csproj", "{10026D09-FC37-48B3-AAEA-B322AA6D89CE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Realm.PlatformHelpers", "Realm\Realm.PlatformHelpers\Realm.PlatformHelpers.csproj", "{536C3309-F848-4485-ABF3-56DCD9C9F9E8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.XamarinTVOS", "Tests\Tests.XamarinTVOS\Tests.XamarinTVOS.csproj", "{80B9697D-0C57-40E8-A71A-F5E81C7BF467}" EndProject Global @@ -737,34 +729,6 @@ Global {693D198D-C4F4-44B5-8C04-11544E0FE79A}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {693D198D-C4F4-44B5-8C04-11544E0FE79A}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {693D198D-C4F4-44B5-8C04-11544E0FE79A}.RelWithDebInfo|x86.Build.0 = Release|Any CPU - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|ARM.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|iPhone.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|x64.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|x86.ActiveCfg = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Debug|x86.Build.0 = Debug|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|ARM.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|iPhone.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|iPhoneSimulator.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|x64.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|x86.ActiveCfg = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.MinSizeRel|x86.Build.0 = MinSizeRel|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|Any CPU.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|ARM.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|iPhone.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|iPhoneSimulator.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|x64.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|x86.ActiveCfg = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.Release|x86.Build.0 = Release|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|ARM.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|iPhone.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|iPhoneSimulator.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|Win32 - {CD7F1F01-CBD4-3CF6-892A-230FB024217C}.RelWithDebInfo|x86.Build.0 = RelWithDebInfo|Win32 {9322B1D0-B3F7-4D6A-9947-2FC11D3161A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9322B1D0-B3F7-4D6A-9947-2FC11D3161A0}.Debug|Any CPU.Build.0 = Debug|Any CPU {9322B1D0-B3F7-4D6A-9947-2FC11D3161A0}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1333,150 +1297,6 @@ Global {C84EBA8B-5F7F-4519-BB34-EDE93E275D66}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {C84EBA8B-5F7F-4519-BB34-EDE93E275D66}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {C84EBA8B-5F7F-4519-BB34-EDE93E275D66}.RelWithDebInfo|x86.Deploy.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|ARM.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|ARM.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|iPhone.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|x64.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|x64.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|x86.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Debug|x86.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|ARM.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|ARM.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|iPhone.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|iPhone.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|iPhoneSimulator.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.MinSizeRel|x86.Build.0 = Debug|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|Any CPU.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|ARM.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|ARM.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|iPhone.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|iPhone.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|x64.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|x64.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|x86.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.Release|x86.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|iPhone.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|iPhone.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|iPhoneSimulator.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5}.RelWithDebInfo|x86.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|ARM.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|ARM.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|iPhone.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|x64.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|x64.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|x86.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Debug|x86.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|ARM.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|ARM.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|iPhone.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|iPhone.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|iPhoneSimulator.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.MinSizeRel|x86.Build.0 = Debug|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|Any CPU.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|ARM.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|ARM.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|iPhone.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|iPhone.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|x64.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|x64.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|x86.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.Release|x86.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|iPhone.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|iPhone.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|iPhoneSimulator.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|iPhoneSimulator.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU - {10026D09-FC37-48B3-AAEA-B322AA6D89CE}.RelWithDebInfo|x86.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|ARM.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|ARM.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|iPhone.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|x64.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|x64.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|x86.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Debug|x86.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|ARM.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|ARM.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|iPhone.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|iPhone.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|iPhoneSimulator.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.MinSizeRel|x86.Build.0 = Debug|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|Any CPU.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|ARM.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|ARM.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|iPhone.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|iPhone.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|x64.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|x64.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|x86.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.Release|x86.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|ARM.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|ARM.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|iPhone.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|iPhone.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|iPhoneSimulator.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|iPhoneSimulator.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU - {536C3309-F848-4485-ABF3-56DCD9C9F9E8}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Debug|ARM.ActiveCfg = Debug|iPhoneSimulator @@ -1557,9 +1377,6 @@ Global {BCC2A759-231C-405C-BE9C-0C473365B232} = {EC97E75C-3A79-4B00-95BD-218D71C58746} {3C3CEB09-94C5-4FE4-BF75-1CEF4EAF6E47} = {EC97E75C-3A79-4B00-95BD-218D71C58746} {C84EBA8B-5F7F-4519-BB34-EDE93E275D66} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} - {1FBD9F7D-2C7F-4788-9C1E-22A40549AFA5} = {4FF9AAE6-210E-41F0-A5E1-8D7C4A864F51} - {10026D09-FC37-48B3-AAEA-B322AA6D89CE} = {073F6C2D-FECB-41E3-BEA3-1685FAA580DB} - {536C3309-F848-4485-ABF3-56DCD9C9F9E8} = {50F058DF-2B41-403C-BB73-8B4180D1CF39} {80B9697D-0C57-40E8-A71A-F5E81C7BF467} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/Realm.sln b/Realm.sln index 065cfc45ea..ce90ce398e 100644 --- a/Realm.sln +++ b/Realm.sln @@ -44,8 +44,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceGenerators", "SourceG EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.XamarinTVOS", "Tests\Tests.XamarinTVOS\Tests.XamarinTVOS.csproj", "{80B9697D-0C57-40E8-A71A-F5E81C7BF467}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployApps", "Tools\DeployApps\DeployApps.csproj", "{56029D1A-0AF6-4535-9D6A-CD5CD61325C6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Realm.Fody", "Realm\Realm.Fody\Realm.Fody.csproj", "{2AC1DA0E-2B6E-4DBA-A73F-6784BA0A7DB4}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.XamarinMac", "Tests\Tests.XamarinMac\Tests.XamarinMac.csproj", "{3A6CC342-40D0-4487-9964-DDAEBA628199}" @@ -83,10 +81,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.UWP", "Tests\Tests.UW EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SetupUnityPackage", "Tools\SetupUnityPackage\SetupUnityPackage.csproj", "{A9B5E8CA-E1B8-47E4-89D4-8A55327F4121}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Realm.PlatformHelpers", "Realm\Realm.PlatformHelpers\Realm.PlatformHelpers.csproj", "{D08C71CE-0F5B-4855-A77C-58062A5ECB78}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnalyticsAssembly", "Tests\Weaver\AnalyticsAssembly\AnalyticsAssembly.csproj", "{1E392D99-D783-4122-8B31-A4CA19ABADEE}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -271,30 +265,6 @@ Global {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Release|x64.Build.0 = Release|iPhone {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Release|x86.ActiveCfg = Release|iPhone {80B9697D-0C57-40E8-A71A-F5E81C7BF467}.Release|x86.Build.0 = Release|iPhone - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|ARM.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|ARM.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|iPhone.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|x64.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|x64.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|x86.ActiveCfg = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Debug|x86.Build.0 = Debug|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|Any CPU.Build.0 = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|ARM.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|ARM.Build.0 = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|iPhone.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|iPhone.Build.0 = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|x64.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|x64.Build.0 = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|x86.ActiveCfg = Release|Any CPU - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6}.Release|x86.Build.0 = Release|Any CPU {2AC1DA0E-2B6E-4DBA-A73F-6784BA0A7DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AC1DA0E-2B6E-4DBA-A73F-6784BA0A7DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AC1DA0E-2B6E-4DBA-A73F-6784BA0A7DB4}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -703,54 +673,6 @@ Global {A9B5E8CA-E1B8-47E4-89D4-8A55327F4121}.Release|x64.Build.0 = Release|Any CPU {A9B5E8CA-E1B8-47E4-89D4-8A55327F4121}.Release|x86.ActiveCfg = Release|Any CPU {A9B5E8CA-E1B8-47E4-89D4-8A55327F4121}.Release|x86.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|ARM.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|ARM.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|iPhone.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|x64.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|x64.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|x86.ActiveCfg = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Debug|x86.Build.0 = Debug|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|Any CPU.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|ARM.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|ARM.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|iPhone.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|iPhone.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|x64.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|x64.Build.0 = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|x86.ActiveCfg = Release|Any CPU - {D08C71CE-0F5B-4855-A77C-58062A5ECB78}.Release|x86.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|ARM.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|ARM.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|iPhone.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|x64.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|x64.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|x86.ActiveCfg = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Debug|x86.Build.0 = Debug|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|Any CPU.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|ARM.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|ARM.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|iPhone.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|iPhone.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|x64.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|x64.Build.0 = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|x86.ActiveCfg = Release|Any CPU - {1E392D99-D783-4122-8B31-A4CA19ABADEE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -767,7 +689,6 @@ Global {1104E44A-93C2-4B01-9330-6555C08ABFC1} = {50F058DF-2B41-403C-BB73-8B4180D1CF39} {CC933D98-B002-4306-89C4-B1905658D9DE} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} {80B9697D-0C57-40E8-A71A-F5E81C7BF467} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} - {56029D1A-0AF6-4535-9D6A-CD5CD61325C6} = {A25317DE-BB3A-47CC-8E65-F96C9B6AD984} {2AC1DA0E-2B6E-4DBA-A73F-6784BA0A7DB4} = {50F058DF-2B41-403C-BB73-8B4180D1CF39} {3A6CC342-40D0-4487-9964-DDAEBA628199} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} {7174D68E-E76D-4A4F-9F9A-601F5FB2DB65} = {4FF9AAE6-210E-41F0-A5E1-8D7C4A864F51} @@ -785,8 +706,6 @@ Global {641367F9-5149-4E5D-9459-B19DAFC426A8} = {CC933D98-B002-4306-89C4-B1905658D9DE} {7EFF9E5C-5E74-469B-8DB7-C25C9AF0444E} = {D10BE048-9C20-4B8B-BE5B-48CC55F8BB07} {A9B5E8CA-E1B8-47E4-89D4-8A55327F4121} = {A25317DE-BB3A-47CC-8E65-F96C9B6AD984} - {D08C71CE-0F5B-4855-A77C-58062A5ECB78} = {50F058DF-2B41-403C-BB73-8B4180D1CF39} - {1E392D99-D783-4122-8B31-A4CA19ABADEE} = {4FF9AAE6-210E-41F0-A5E1-8D7C4A864F51} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BE5E0028-B74D-4BE1-B1DA-5FFCC8469C41} diff --git a/Realm/Realm.Fody/ModuleWeaver.cs b/Realm/Realm.Fody/ModuleWeaver.cs index 277dafbaa8..ac1d11a8e2 100644 --- a/Realm/Realm.Fody/ModuleWeaver.cs +++ b/Realm/Realm.Fody/ModuleWeaver.cs @@ -16,13 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Versioning; using Mono.Cecil.Cil; using RealmWeaver; -using static RealmWeaver.Analytics; // ReSharper disable once CheckNamespace public class ModuleWeaver : Fody.BaseModuleWeaver, ILogger @@ -41,7 +39,7 @@ public override void Execute() var weaver = new Weaver(ModuleDefinition, this, frameworkName.Identifier); - var executionResult = weaver.Execute(GetAnalyticsConfig(frameworkName)); + var executionResult = weaver.Execute(); WriteInfo(executionResult.ToString()); } @@ -57,46 +55,6 @@ public override IEnumerable GetAssembliesForScanning() yield return "System.Threading"; } - private Config GetAnalyticsConfig(FrameworkName netFramework) - { - AnalyticsCollection analyticsCollection; - if (Enum.TryParse(Config.Attribute("AnalyticsCollection")?.Value, out var collection)) - { - analyticsCollection = collection; - } - else if (bool.TryParse(Config.Attribute("DisableAnalytics")?.Value, out var disableAnalytics)) - { - analyticsCollection = disableAnalytics ? AnalyticsCollection.Disabled : AnalyticsCollection.Full; - } - else if (Environment.GetEnvironmentVariable("REALM_DISABLE_ANALYTICS") != null || Environment.GetEnvironmentVariable("CI") != null) - { - analyticsCollection = AnalyticsCollection.Disabled; - } - else - { -#if DEBUG - analyticsCollection = AnalyticsCollection.DryRun; -#else - analyticsCollection = AnalyticsCollection.Full; -#endif - } - - var framework = AnalyticsUtils.GetFrameworkAndVersion(ModuleDefinition); - - return new( - targetOSName: AnalyticsUtils.GetTargetOsName(netFramework), - netFrameworkTarget: netFramework.Identifier, - netFrameworkTargetVersion: netFramework.Version.ToString(), - installationMethod: "Nuget", - frameworkName: framework.Name, - frameworkVersion: framework.Version, - compiler: "msbuild") - { - AnalyticsCollection = analyticsCollection, - AnalyticsLogPath = Config.Attribute("AnalyticsLogPath")?.Value, - }; - } - void ILogger.Debug(string message) { WriteDebug(message); diff --git a/Realm/Realm.Fody/Realm.Fody.xcf b/Realm/Realm.Fody/Realm.Fody.xcf index e147c2a8b4..4e21196052 100644 --- a/Realm/Realm.Fody/Realm.Fody.xcf +++ b/Realm/Realm.Fody/Realm.Fody.xcf @@ -1,47 +1,5 @@  - - - THIS IS DEPRECATED - USE `AnalyticsCollection` INSTEAD. Disables anonymized - usage information from being sent on build. Read more about what data is being collected and - why here: https://github.com/realm/realm-dotnet/blob/main/Realm/Realm.Weaver/Analytics.cs - - - - - Controls what anonymized usage information is being sent on build. Read more - about what data is being collected and why here: - https://github.com/realm/realm-dotnet/blob/main/Realm/Realm.Weaver/Analytics/Analytics.cs - - - - - - Analytics collection will run normally. This is the default behavior - and we hope you don't change it as the anonymized data collected is critical for - making the right decisions about the future of the Realm SDK. - - - - - Analytics collection will run but will not send it to the server. This - is useful in combination with `AnalyticsLogPath` if you want to review the data being - sent. - - - - - Analytics collection is disabled. No data will be sent on build. - - - - - - - - Controls where the payload for the anonymized metrics collection will be - stored. This can be useful if you want to review the data being collected by Realm. - - + \ No newline at end of file diff --git a/Realm/Realm.PlatformHelpers/Android/DeviceInfo.android.cs b/Realm/Realm.PlatformHelpers/Android/DeviceInfo.android.cs deleted file mode 100644 index 4c8749dc69..0000000000 --- a/Realm/Realm.PlatformHelpers/Android/DeviceInfo.android.cs +++ /dev/null @@ -1,31 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 Android.OS; - -using static Realms.PlatformHelpers.Platform; - -namespace Realms.PlatformHelpers -{ - internal class DeviceInfo : IDeviceInfo - { - public string Name => Build.Manufacturer ?? Unknown; - - public string Version => Build.Model ?? Unknown; - } -} diff --git a/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.ios.cs b/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.ios.cs deleted file mode 100644 index e972cdfbdb..0000000000 --- a/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.ios.cs +++ /dev/null @@ -1,29 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 static Realms.PlatformHelpers.Platform; - -namespace Realms.PlatformHelpers -{ - internal class DeviceInfo : IDeviceInfo - { - public string Name => UIKit.UIDevice.CurrentDevice.Model; - - public string Version => NativeHelpers.GetSysctlProperty("hw.machine") ?? Unknown; - } -} diff --git a/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.mac.cs b/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.mac.cs deleted file mode 100644 index cd61225353..0000000000 --- a/Realm/Realm.PlatformHelpers/Apple/DeviceInfo.mac.cs +++ /dev/null @@ -1,29 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 static Realms.PlatformHelpers.Platform; - -namespace Realms.PlatformHelpers -{ - internal class DeviceInfo : IDeviceInfo - { - public string Name => "Apple"; - - public string Version => NativeHelpers.GetSysctlProperty("hw.model") ?? Unknown; - } -} diff --git a/Realm/Realm.PlatformHelpers/Apple/NativeHelpers.ios.mac.cs b/Realm/Realm.PlatformHelpers/Apple/NativeHelpers.ios.mac.cs deleted file mode 100644 index da44c819c3..0000000000 --- a/Realm/Realm.PlatformHelpers/Apple/NativeHelpers.ios.mac.cs +++ /dev/null @@ -1,65 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using ObjCRuntime; - -namespace Realms.PlatformHelpers -{ - internal static class NativeHelpers - { - [DllImport(Constants.SystemLibrary, EntryPoint = "sysctlbyname")] - internal static extern int SysctlByName([MarshalAs(UnmanagedType.LPStr)] string property, IntPtr output, IntPtr oldLen, IntPtr newp, uint newlen); - - internal static string? GetSysctlProperty(string property) - { - var lengthPtr = Marshal.AllocHGlobal(sizeof(int)); - - IntPtr? valuePtr = null; - try - { - SysctlByName(property, IntPtr.Zero, lengthPtr, IntPtr.Zero, 0); - - var propertyLength = Marshal.ReadInt32(lengthPtr); - - if (propertyLength > 0) - { - valuePtr = Marshal.AllocHGlobal(propertyLength); - SysctlByName(property, valuePtr.Value, lengthPtr, IntPtr.Zero, 0); - - return Marshal.PtrToStringAnsi(valuePtr.Value); - } - } - catch - { - } - finally - { - Marshal.FreeHGlobal(lengthPtr); - - if (valuePtr.HasValue) - { - Marshal.FreeHGlobal(valuePtr.Value); - } - } - - return null; - } - } -} diff --git a/Realm/Realm.PlatformHelpers/AssemblyInfo.shared.cs b/Realm/Realm.PlatformHelpers/AssemblyInfo.shared.cs deleted file mode 100644 index d73a6b33aa..0000000000 --- a/Realm/Realm.PlatformHelpers/AssemblyInfo.shared.cs +++ /dev/null @@ -1,23 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Realm")] -[assembly: InternalsVisibleTo("Realm.Tests")] -[assembly: InternalsVisibleTo("Realm.UnityUtils")] diff --git a/Realm/Realm.PlatformHelpers/DeviceInfo.shared.cs b/Realm/Realm.PlatformHelpers/DeviceInfo.shared.cs deleted file mode 100644 index 0080398e05..0000000000 --- a/Realm/Realm.PlatformHelpers/DeviceInfo.shared.cs +++ /dev/null @@ -1,27 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.PlatformHelpers -{ - internal interface IDeviceInfo - { - string Name { get; } - - string Version { get; } - } -} diff --git a/Realm/Realm.PlatformHelpers/Platform.shared.cs b/Realm/Realm.PlatformHelpers/Platform.shared.cs deleted file mode 100644 index 454b679b5d..0000000000 --- a/Realm/Realm.PlatformHelpers/Platform.shared.cs +++ /dev/null @@ -1,85 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography; -using System.Text; - -namespace Realms.PlatformHelpers -{ - internal static class Platform - { - public const string Unknown = ""; - - private static IDeviceInfo? _deviceInfo; - - private static readonly Lazy _deviceInfoLazy = new(() => _deviceInfo ?? new DeviceInfo()); - - public static IDeviceInfo DeviceInfo - { - get => _deviceInfoLazy.Value; - set - { - if (_deviceInfoLazy.IsValueCreated) - { - throw new Exception("DeviceInfo should only be configured once"); - } - - _deviceInfo = value; - } - } - - private static string? _bundleId; - public static string BundleId - { - get - { - var bundleId = _bundleId ?? Assembly.GetEntryAssembly()?.GetName().Name; - - if (bundleId == null) - { - // On Android, the entry assembly is null (there's no main() method), so we need to find - // the first assembly that has the ResourceDesignerAttribute with IsApplication = true. - var entryAssembly = AppDomain.CurrentDomain.GetAssemblies() - .FirstOrDefault(a => a.CustomAttributes.Any(att => - att.AttributeType.FullName == "Android.Runtime.ResourceDesignerAttribute" && - att.NamedArguments?.FirstOrDefault(arg => arg.MemberName == "IsApplication").TypedValue.Value is true)); - - bundleId = entryAssembly?.GetName().Name; - } - - return Sha256(bundleId); - } - set => _bundleId = value; - } - - internal static string Sha256(string? value) - { - if (value == null) - { - return Unknown; - } - - using var sha256 = SHA256.Create(); - var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(value)); - return Convert.ToBase64String(hash); - } - } -} diff --git a/Realm/Realm.PlatformHelpers/Realm.PlatformHelpers.csproj b/Realm/Realm.PlatformHelpers/Realm.PlatformHelpers.csproj deleted file mode 100644 index b04840911b..0000000000 --- a/Realm/Realm.PlatformHelpers/Realm.PlatformHelpers.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - - Realm Platform Helpers - - A set of platform-specific helpers used in conjunction with Realm. This package is always referenced by the main Realm package - and should never be added directly to your project. - - - netstandard2.0 - - - $(TargetFrameworks);net7.0-ios;net7.0-android;net7.0-maccatalyst;net7.0-tvos - - - - - $(TargetFrameworks);MonoAndroid5;Xamarin.iOS10;Xamarin.TVOS10;Xamarin.Mac20 - - $(TargetFrameworks);uap10.0.19041; - false - 9.0 - enable - Realms.PlatformHelpers - - NETSDK1202 - - - - - - - - - - - - - Windows Desktop Extensions for the UWP - - - Windows Mobile Extensions for the UWP - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Realm/Realm.PlatformHelpers/UWP/DeviceInfo.uwp.cs b/Realm/Realm.PlatformHelpers/UWP/DeviceInfo.uwp.cs deleted file mode 100644 index 17d78cb480..0000000000 --- a/Realm/Realm.PlatformHelpers/UWP/DeviceInfo.uwp.cs +++ /dev/null @@ -1,31 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 Windows.Security.ExchangeActiveSyncProvisioning; - -namespace Realms.PlatformHelpers -{ - internal class DeviceInfo : IDeviceInfo - { - private static EasClientDeviceInformation deviceInfo = new(); - - public string Name => deviceInfo.SystemManufacturer; - - public string Version => deviceInfo.SystemProductName; - } -} diff --git a/Realm/Realm.PlatformHelpers/fallback/DeviceInfo.netstandard.cs b/Realm/Realm.PlatformHelpers/fallback/DeviceInfo.netstandard.cs deleted file mode 100644 index 8b5a2e9b3d..0000000000 --- a/Realm/Realm.PlatformHelpers/fallback/DeviceInfo.netstandard.cs +++ /dev/null @@ -1,29 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 static Realms.PlatformHelpers.Platform; - -namespace Realms.PlatformHelpers -{ - internal class DeviceInfo : IDeviceInfo - { - public string Name => Unknown; - - public string Version => Unknown; - } -} diff --git a/Realm/Realm.SourceGenerator/Diagnostics.cs b/Realm/Realm.SourceGenerator/Diagnostics.cs index a3440068d0..a9af4c71da 100644 --- a/Realm/Realm.SourceGenerator/Diagnostics.cs +++ b/Realm/Realm.SourceGenerator/Diagnostics.cs @@ -92,7 +92,7 @@ public static Diagnostic ClassUnclearDefinition(string className, Location locat return CreateDiagnosticError( Id.ClassUnclearDefinition, "Realm classes cannot implement multiple class interfaces", - $"Class {className} is declared as implementing multiple class interfaces. A class can implement only one interface between IRealmObject, IEmbeddedObject, IAsymmetricObject.", + $"Class {className} is declared as implementing multiple class interfaces. A class can implement only one interface between IRealmObject, IEmbeddedObject.", location); } @@ -317,7 +317,7 @@ public static Diagnostic TypeNotSupported(string className, string propertyName, return CreateDiagnosticError( Id.TypeNotSupported, "Type not supported", - $"{className}.{propertyName} is of type '{propertyType}' which is not yet supported. If that is supposed to be a model class, make sure it implements IRealmObject/IEmbeddedObject/IAsymmetricObject.", + $"{className}.{propertyName} is of type '{propertyType}' which is not yet supported. If that is supposed to be a model class, make sure it implements IRealmObject/IEmbeddedObject.", location); } diff --git a/Realm/Realm.SourceGenerator/InfoClasses.cs b/Realm/Realm.SourceGenerator/InfoClasses.cs index c852de97d2..a67e4fd1c0 100644 --- a/Realm/Realm.SourceGenerator/InfoClasses.cs +++ b/Realm/Realm.SourceGenerator/InfoClasses.cs @@ -381,7 +381,6 @@ internal enum ObjectType None, RealmObject, EmbeddedObject, - AsymmetricObject } internal enum CollectionType diff --git a/Realm/Realm.SourceGenerator/Utils.cs b/Realm/Realm.SourceGenerator/Utils.cs index 7a0b6e4881..82e2e98519 100644 --- a/Realm/Realm.SourceGenerator/Utils.cs +++ b/Realm/Realm.SourceGenerator/Utils.cs @@ -107,10 +107,6 @@ public static IEnumerable ImplementingObjectTypes(this ITypeSymbol s { yield return ObjectType.EmbeddedObject; } - else if (IsIAsymmetricObjectInterface(i)) - { - yield return ObjectType.AsymmetricObject; - } } } @@ -120,14 +116,10 @@ public static IEnumerable ImplementingObjectTypes(this ITypeSymbol s public static bool IsEmbeddedObject(this ITypeSymbol symbol) => symbol.Interfaces.Any(IsIEmbeddedObjectInterface); - public static bool IsAsymmetricObject(this ITypeSymbol symbol) => symbol.Interfaces.Any(IsIAsymmetricObjectInterface); - private static bool IsIRealmObjectInterface(this INamedTypeSymbol interfaceSymbol) => interfaceSymbol.Name == "IRealmObject"; private static bool IsIEmbeddedObjectInterface(this INamedTypeSymbol interfaceSymbol) => interfaceSymbol.Name == "IEmbeddedObject"; - private static bool IsIAsymmetricObjectInterface(this INamedTypeSymbol interfaceSymbol) => interfaceSymbol.Name == "IAsymmetricObject"; - public static INamedTypeSymbol AsNamed(this ITypeSymbol symbol) { if (symbol is INamedTypeSymbol namedSymbol) diff --git a/Realm/Realm.UnityUtils/DeviceInfo.cs b/Realm/Realm.UnityUtils/DeviceInfo.cs deleted file mode 100644 index 50cf65e2cf..0000000000 --- a/Realm/Realm.UnityUtils/DeviceInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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 Realms.PlatformHelpers; -using UnityEngine; - -namespace UnityUtils -{ - internal class UnityDeviceInfo : IDeviceInfo - { - public string Name => Platform.Unknown; - - public string Version => SystemInfo.deviceModel; - } -} diff --git a/Realm/Realm.UnityUtils/Initializer.cs b/Realm/Realm.UnityUtils/Initializer.cs index f8dd575389..b77c2e8abd 100644 --- a/Realm/Realm.UnityUtils/Initializer.cs +++ b/Realm/Realm.UnityUtils/Initializer.cs @@ -19,7 +19,6 @@ using System.Reflection; using System.Threading; using Realms; -using Realms.PlatformHelpers; using UnityEngine; namespace UnityUtils @@ -34,8 +33,6 @@ public static void Initialize() { if (Interlocked.CompareExchange(ref _isInitialized, 1, 0) == 0) { - Platform.DeviceInfo = new UnityDeviceInfo(); - Platform.BundleId = Application.productName; InteropConfig.AddPotentialStorageFolder(FileHelper.GetStorageFolder()); Realms.Logging.RealmLogger.Console = new UnityLogger(); Application.quitting += () => diff --git a/Realm/Realm.UnityWeaver/UnityWeaver.cs b/Realm/Realm.UnityWeaver/UnityWeaver.cs index e59064caa1..f56e9d7fa8 100644 --- a/Realm/Realm.UnityWeaver/UnityWeaver.cs +++ b/Realm/Realm.UnityWeaver/UnityWeaver.cs @@ -28,10 +28,7 @@ using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEditor.Compilation; -using UnityEditor.PackageManager; -using UnityEditor.PackageManager.Requests; using UnityEngine; -using static RealmWeaver.Analytics; using Assembly = UnityEditor.Compilation.Assembly; // ReSharper disable once CheckNamespace @@ -40,31 +37,12 @@ namespace RealmWeaver // Heavily influenced by https://github.com/ExtendRealityLtd/Malimbe and https://github.com/fody/fody public class UnityWeaver : IPostBuildPlayerScriptDLLs, IPreprocessBuildWithReport, IPostprocessBuildWithReport { - private const string EnableAnalyticsPref = "realm_enable_analytics"; - private const string EnableAnalyticsMenuItemPath = "Tools/Realm/Enable build-time analytics"; - private const string WeaveEditorAssembliesPref = "realm_weave_editor_assemblies"; private const string WeaveEditorAssembliesMenuItemPath = "Tools/Realm/Process editor assemblies"; - private const string UnityPackageName = "io.realm.unity"; private static readonly int UnityMajorVersion = int.Parse(Application.unityVersion.Split('.')[0]); - private static readonly TaskCompletionSource _installMethodTask = new(); - - private static bool _analyticsEnabled; - - private static bool AnalyticsEnabled - { - get => _analyticsEnabled; - set - { - _analyticsEnabled = value; - EditorPrefs.SetBool(EnableAnalyticsPref, value); - Menu.SetChecked(EnableAnalyticsMenuItemPath, value); - } - } private static bool _weaveEditorAssemblies; - private static ListRequest? _listRequest; private static bool WeaveEditorAssemblies { @@ -82,24 +60,9 @@ private static bool WeaveEditorAssemblies [InitializeOnLoadMethod] public static void Initialize() { - if (Application.isBatchMode) - { - // In batch mode, `update` won't get called until compilation is complete, - // which means we'll deadlock when we block compilation on the tcs completing - _installMethodTask.TrySetResult(Metric.Unknown()); - } - // We need to call that again after the editor is initialized to ensure that we populate the checkmark correctly. EditorApplication.delayCall += () => { - _listRequest = Client.List(); - - if (!Application.isBatchMode) - { - EditorApplication.update += OnEditorApplicationUpdate; - } - - AnalyticsEnabled = EditorPrefs.GetBool(EnableAnalyticsPref, defaultValue: true); WeaveEditorAssemblies = EditorPrefs.GetBool(WeaveEditorAssembliesPref, defaultValue: false); WeaverAssemblyResolver.ApplicationDataPath = Application.dataPath; WeaveAssembliesOnEditorLaunch(); @@ -119,36 +82,10 @@ public static void Initialize() return; } - var config = GetAnalyticsConfig(); - WeaveAssemblyCore(assemblyPath, assembly.allReferences, config); + WeaveAssemblyCore(assemblyPath, assembly.allReferences); }; } - private static void OnEditorApplicationUpdate() - { - if (_listRequest?.IsCompleted != true) - { - return; - } - - EditorApplication.update -= OnEditorApplicationUpdate; - - var installMethod = Metric.Unknown(); - - if (_listRequest.Status == StatusCode.Success) - { - var realmPackage = _listRequest.Result.FirstOrDefault(p => p.name == UnityPackageName); - installMethod = realmPackage?.source switch - { - PackageSource.LocalTarball => "Manual", - PackageSource.Registry => "NPM", - _ => Metric.Unknown(realmPackage?.source.ToString()), - }; - } - - _installMethodTask.TrySetResult(installMethod); - } - [MenuItem("Tools/Realm/Weave Assemblies")] public static async void WeaveAllAssembliesMenuItem() { @@ -156,12 +93,6 @@ public static async void WeaveAllAssembliesMenuItem() UnityLogger.Instance.Info($"Weaving completed. {assembliesWoven} assemblies needed weaving."); } - [MenuItem(EnableAnalyticsMenuItemPath)] - public static void DisableAnalyticsMenuItem() - { - AnalyticsEnabled = !AnalyticsEnabled; - } - [MenuItem(WeaveEditorAssembliesMenuItemPath)] public static void WeaveEditorAssembliesMenuItem() { @@ -199,13 +130,11 @@ private static async Task WeaveAllAssemblies() var assembliesToWeave = GetAssemblies(); var weaveResults = new List(); - var config = GetAnalyticsConfig(); - await Task.Run(() => { foreach (var assembly in assembliesToWeave) { - if (!WeaveAssemblyCore(assembly.outputPath, assembly.allReferences, config)) + if (!WeaveAssemblyCore(assembly.outputPath, assembly.allReferences)) { continue; } @@ -242,7 +171,7 @@ await Task.Run(() => return assembliesWoven; } - private static bool WeaveAssemblyCore(string assemblyPath, IEnumerable references, Config analyticsConfig) + private static bool WeaveAssemblyCore(string assemblyPath, IEnumerable references) { var name = Path.GetFileNameWithoutExtension(assemblyPath); @@ -263,7 +192,7 @@ private static bool WeaveAssemblyCore(string assemblyPath, IEnumerable r // using Mono, so we just hardcode Unity which is treated as Mono/.NET Framework by the weaver. var weaver = new Weaver(resolutionResult.Module, UnityLogger.Instance, "Unity"); - var results = weaver.Execute(analyticsConfig); + var results = weaver.Execute(); if (results.ErrorMessage != null) { @@ -305,11 +234,10 @@ public void OnPostBuildPlayerScriptDLLs(BuildReport report) .ToArray(); var assembliesToWeave = files.Where(f => f.role == "ManagedLibrary"); - var config = GetAnalyticsConfig(report.summary.platform); foreach (var file in assembliesToWeave) { - WeaveAssemblyCore(file.path, referencePaths, config); + WeaveAssemblyCore(file.path, referencePaths); } if (report.summary.platform != BuildTarget.iOS && report.summary.platform != BuildTarget.tvOS) @@ -415,53 +343,6 @@ private static Assembly[] GetAssemblies() return CompilationPipeline.GetAssemblies(AssembliesType.Player); } - private static string GetTargetOSName(BuildTarget? target) - { - // target is null for editor builds - in that case, we return the current OS - // as target. - if (target == null) - { - return Application.platform switch - { - RuntimePlatform.WindowsEditor => Metric.OperatingSystem.Windows, - RuntimePlatform.OSXEditor => Metric.OperatingSystem.MacOS, - RuntimePlatform.LinuxEditor => Metric.OperatingSystem.Linux, - _ => Metric.Unknown(Application.platform.ToString()), - }; - } - - // These have to match Analytics.GetConfig(FrameworkName) - return target switch - { - BuildTarget.StandaloneOSX => Metric.OperatingSystem.MacOS, - BuildTarget.StandaloneWindows or BuildTarget.StandaloneWindows64 or BuildTarget.WSAPlayer => Metric.OperatingSystem.Windows, - BuildTarget.iOS => Metric.OperatingSystem.Ios, - BuildTarget.Android => Metric.OperatingSystem.Android, - BuildTarget.StandaloneLinux64 => Metric.OperatingSystem.Linux, - BuildTarget.tvOS => Metric.OperatingSystem.TvOs, - BuildTarget.XboxOne => Metric.OperatingSystem.XboxOne, - _ => Metric.Unknown(target.ToString()), - }; - } - - private static string GetTargetOsVersion(BuildTarget? target) - { - // target is null for editor builds - in that case, we return the host OS - // version. - if (target == null) - { - return Environment.OSVersion.Version.ToString(); - } - - return target switch - { - BuildTarget.Android => ((int)PlayerSettings.Android.targetSdkVersion).ToString(), - BuildTarget.iOS => PlayerSettings.iOS.targetOSVersionString, - BuildTarget.tvOS => PlayerSettings.tvOS.targetOSVersionString, - _ => Metric.Unknown(), - }; - } - private static BuildFile[] GetFiles(BuildReport report) { try @@ -484,130 +365,6 @@ private static BuildFile[] GetFiles(BuildReport report) } } - private static string GetMinimumOsVersion(BuildTarget? target) - { - // target is null for editor builds - in that case, we return the host OS - // version. - if (target == null) - { - return Environment.OSVersion.Version.ToString(); - } - - return target switch - { - BuildTarget.Android => ((int)PlayerSettings.Android.minSdkVersion).ToString(), - BuildTarget.iOS => PlayerSettings.iOS.targetOSVersionString, - BuildTarget.tvOS => PlayerSettings.tvOS.targetOSVersionString, - _ => Metric.Unknown(), - }; - } - - private static Config GetAnalyticsConfig(BuildTarget? target = null) - { - var netFrameworkInfo = GetNetFrameworkInfo(target); - var compiler = PlayerSettings.GetScriptingBackend(BuildPipeline.GetBuildTargetGroup(target ?? EditorUserBuildSettings.activeBuildTarget)).ToString(); - - var analyticsEnabled = AnalyticsEnabled && - Environment.GetEnvironmentVariable("REALM_DISABLE_ANALYTICS") == null && - Environment.GetEnvironmentVariable("CI") == null; - - return new(targetOSName: GetTargetOSName(target), - compiler: compiler, - netFrameworkTarget: netFrameworkInfo.Name, - netFrameworkTargetVersion: netFrameworkInfo.Version, - installationMethod: _installMethodTask.Task.Wait(1000) ? _installMethodTask.Task.Result : Metric.Unknown(), - frameworkName: target == null ? Metric.Framework.UnityEditor : Metric.Framework.Unity, - frameworkVersion: Application.unityVersion) - { - AnalyticsCollection = analyticsEnabled ? AnalyticsCollection.Full : AnalyticsCollection.Disabled, - TargetArchitecture = GetCpuArchitecture(target), - TargetOsVersion = GetTargetOsVersion(target), - TargetOsMinimumVersion = GetMinimumOsVersion(target), - ProjectId = PlayerSettings.productName, - }; - } - - private static (string Name, string Version) GetNetFrameworkInfo(BuildTarget? buildTarget) - { - var targetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget ?? EditorUserBuildSettings.activeBuildTarget); - var apiTarget = PlayerSettings.GetApiCompatibilityLevel(targetGroup); - - // these consts are exactly mapped to what .NET reports in any .NET application - const string netStandardApi = ".NETStandard"; - const string netFrameworkApi = ".NETFramework"; - - var unityVersion = new Version(Application.unityVersion.Substring(0, 6)); - - // conversion necessary as after unity version 2021.1, entry NET_4_6 and NET_Standard_2_0 - // are actually representing .NET 4.8 and .NET Standard 2.1 - // https://github.com/Unity-Technologies/UnityCsReference/blob/664dfe30cee8ee2ef7dd8c5e9db6235915245ecb/Editor/Mono/PlayerSettings.bindings.cs#L158 - if (unityVersion >= new Version("2021.2")) - { - if (apiTarget == ApiCompatibilityLevel.NET_Standard_2_0) - { - return (netStandardApi, "2.1"); - } - - if (apiTarget == ApiCompatibilityLevel.NET_4_6) - { - return (netFrameworkApi, "4.8"); - } - } - - if (apiTarget == ApiCompatibilityLevel.NET_Standard_2_0) - { - return (netStandardApi, "2.0"); - } - - if (apiTarget == ApiCompatibilityLevel.NET_4_6) - { - return (netFrameworkApi, "4.6"); - } - - // this should really never be the case - return (apiTarget.ToString(), ""); - } - - private static string GetCpuArchitecture(BuildTarget? buildTarget) - { - // buildTarget is null when we're building for the editor - if (buildTarget == null) - { - if (SystemInfo.processorType.IndexOf("ARM", StringComparison.OrdinalIgnoreCase) > -1) - { - return Environment.Is64BitProcess ? Metric.CpuArchitecture.Arm64 : Metric.CpuArchitecture.Arm; - } - - // Must be in the x86 family. - return Environment.Is64BitProcess ? Metric.CpuArchitecture.X64 : Metric.CpuArchitecture.X86; - } - - return buildTarget switch - { - BuildTarget.iOS or BuildTarget.tvOS => Metric.CpuArchitecture.Arm64, - BuildTarget.StandaloneOSX => EditorUserBuildSettings.GetPlatformSettings(BuildPipeline.GetBuildTargetName(buildTarget.Value), "Architecture") switch - { - "ARM64" => Metric.CpuArchitecture.Arm64, - "x64" => Metric.CpuArchitecture.X64, - _ => Metric.CpuArchitecture.Universal, - }, - BuildTarget.StandaloneWindows => Metric.CpuArchitecture.X86, - BuildTarget.Android => PlayerSettings.Android.targetArchitectures switch - { - AndroidArchitecture.ARMv7 => Metric.CpuArchitecture.Arm, - AndroidArchitecture.ARM64 => Metric.CpuArchitecture.Arm64, - - // These two don't have enum values in our Unity reference dll, but exist in newer versions - // See https://github.com/Unity-Technologies/UnityCsReference/blob/70abf502c521c169ee8a302aa48c5600fc7c39fc/Editor/Mono/PlayerSettingsAndroid.bindings.cs#L14 - (AndroidArchitecture)(1 << 2) => Metric.CpuArchitecture.X86, - (AndroidArchitecture)(1 << 3) => Metric.CpuArchitecture.X64, - _ => Metric.CpuArchitecture.Universal, - }, - BuildTarget.StandaloneWindows64 or BuildTarget.StandaloneLinux64 or BuildTarget.XboxOne => Metric.CpuArchitecture.X64, - _ => Metric.Unknown(), - }; - } - private class UnityLogger : ILogger { public static UnityLogger Instance { get; } = new UnityLogger(); diff --git a/Realm/Realm.Weaver/Analytics/Analytics.cs b/Realm/Realm.Weaver/Analytics/Analytics.cs deleted file mode 100644 index f0d2e7ccd3..0000000000 --- a/Realm/Realm.Weaver/Analytics/Analytics.cs +++ /dev/null @@ -1,593 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using Mono.Cecil; -using Mono.Cecil.Cil; -using static RealmWeaver.AnalyticsUtils; - -using Feature = RealmWeaver.Metric.Feature; -using UserEnvironment = RealmWeaver.Metric.Environment; - -namespace RealmWeaver -{ - // Asynchronously submits build information to Realm when the assembly weaver - // is running - // - // To be clear: this does *not* run when your app is in production or on - // your end-user's devices; it will only run when you build your app from source. - // - // Why are we doing this? Because it helps us build a better product for you. - // None of the data personally identifies you, your employer or your app, but it - // *will* help us understand what Realm version you use, what host OS you use, - // etc. Having this info will help with prioritizing our time, adding new - // features and deprecating old ones. Collecting an anonymized assembly name & - // anonymized MAC is the only way for us to count actual usage of the other - // metrics accurately. If we don't have a way to deduplicate the info reported, - // it will be useless, as a single developer building their app on Windows ten - // times would report 10 times more than a single developer that only builds - // once from Mac OS X, making the data all but useless. No one likes sharing - // data unless it's necessary, we get it, and we've debated adding this for a - // long long time. Since Realm is a free product without an email sign-up, we - // feel this is a necessary step so we can collect relevant data to build a - // better product for you. - // - // Currently the following information is reported: - // - What OS and CPU architecture you are running on - // - What OS and CPU architecture you are building for - // - What version of the Realm SDK you're using - // - What framework and what framework version Realm is being used with (e.g. Xamarin, MAUI, etc.) - // - How the Realm SDK was installed (e.g. Nuget, manual, etc.) - // - What APIs of the Realm SDK you're using - // - An anonymized identifier and assembly name ID to aggregate the other information on. - internal class Analytics - { - // The value of this field is modified by CI in the "prepare-release" action, so do not change its name. - private const string CoreVersion = "13.15.0"; - - private readonly ImportedReferences _references; - private readonly ILogger _logger; - - private readonly Dictionary _realmEnvMetrics = new(); - private readonly Dictionary _realmFeaturesToAnalyze; - - private readonly Dictionary> _apiAnalysisSetters; - - private readonly Dictionary> _classAnalysisSetters; - - private readonly Config _config; - - private readonly Task _analyzeUserAssemblyTask; - - public Analytics(Config config, ImportedReferences references, ILogger logger, ModuleDefinition module) - { - _config = config; - _references = references; - _logger = logger; - - if (config.AnalyticsCollection == AnalyticsCollection.Disabled) - { - _realmFeaturesToAnalyze = new(); - _classAnalysisSetters = new(); - _apiAnalysisSetters = new(); - _analyzeUserAssemblyTask = Task.CompletedTask; - return; - } - - _realmFeaturesToAnalyze = Metric.SdkFeatures.Keys.ToDictionary(c => c, _ => (byte)0); - - _classAnalysisSetters = new() - { - ["Class"] = member => - member is PropertyDefinition property && property.PropertyType.IsAnyRealmObject(_references) ? - new(true, Feature.RealmObjectReference) : default, - [Feature.RealmValue] = _ => new(true, Feature.RealmValue), - ["IList`1"] = member => AnalyzeCollectionProperty(member, Feature.PrimitiveList, Feature.ReferenceList), - ["IDictionary`2"] = member => AnalyzeCollectionProperty(member, Feature.PrimitiveDictionary, Feature.ReferenceDictionary, 1), - ["ISet`1"] = member => AnalyzeCollectionProperty(member, Feature.PrimitiveSet, Feature.ReferenceSet), - ["RealmInteger`1"] = _ => new(true, Feature.RealmInteger), - [Feature.BacklinkAttribute] = _ => new(true, Feature.BacklinkAttribute) - }; - - _apiAnalysisSetters = new() - { - [Feature.GetInstanceAsync] = instruction => AnalyzeRealmApi(instruction, Feature.GetInstanceAsync), - [Feature.GetInstance] = instruction => AnalyzeRealmApi(instruction, Feature.GetInstance), - [Feature.Find] = instruction => AnalyzeRealmApi(instruction, Feature.Find), - [Feature.WriteAsync] = instruction => AnalyzeRealmApi(instruction, Feature.WriteAsync), - [Feature.ThreadSafeReference] = instruction => AnalyzeRealmApi(instruction, Feature.ThreadSafeReference), - - // check if it's the right signature, that is 2 params in total of which - // the second a bool and that it's set to true. - [Feature.Add] = instruction => - IsInRealmNamespace(instruction?.Operand) && - instruction?.Operand is MethodSpecification methodSpecification && - methodSpecification.Parameters.Count == 2 && - methodSpecification.Parameters[1].ParameterType.MetadataType == MetadataType.Boolean && - instruction.Previous?.OpCode == OpCodes.Ldc_I4_1 - ? new(true, Feature.Add) : default, - [Feature.ShouldCompactOnLaunch] = _ => new(true, Feature.ShouldCompactOnLaunch), - [Feature.MigrationCallback] = _ => new(true, Feature.MigrationCallback), - [Feature.RealmChanged] = _ => new(true, Feature.RealmChanged), - ["SubscribeForNotifications"] = instruction => - { - if (instruction?.Operand is not MethodSpecification methodSpecification || !IsInRealmNamespace(instruction?.Operand)) - { - return default; - } - - var collectionType = ((TypeSpecification)methodSpecification.Parameters[0].ParameterType).Name; - var key = collectionType switch - { - "IQueryable`1" or "IOrderedQueryable`1" => Feature.ResultSubscribeForNotifications, - "IList`1" => Feature.ListSubscribeForNotifications, - "ISet`1" => Feature.SetSubscribeForNotifications, - "IDictionary`2" => Feature.DictionarySubscribeForNotifications, - _ => $"{collectionType} unknown collection" - }; - - var shouldDelete = ContainsAllRelatedFeatures(key, - Feature.ResultSubscribeForNotifications, - Feature.ListSubscribeForNotifications, - Feature.SetSubscribeForNotifications, - Feature.DictionarySubscribeForNotifications); - - return new(shouldDelete, key); - }, - ["PropertyChanged"] = instruction => - { - string? key = null; - if (instruction.Operand is MemberReference reference) - { - if (reference.DeclaringType.IsAnyRealmObject(_references)) - { - key = Feature.ObjectNotification; - } - else if (reference.DeclaringType.IsSameAs(_references.SyncSession)) - { - key = Feature.ConnectionNotification; - } - } - - if (key == null) - { - return default; - } - - var shouldDelete = ContainsAllRelatedFeatures(key, Feature.ObjectNotification, Feature.ConnectionNotification); - return new(shouldDelete, key); - }, - [Feature.RecoverOrDiscardUnsyncedChangesHandler] = _ => new(true, Feature.RecoverOrDiscardUnsyncedChangesHandler), - [Feature.RecoverUnsyncedChangesHandler] = _ => new(true, Feature.RecoverUnsyncedChangesHandler), - [Feature.DiscardUnsyncedChangesHandler] = _ => new(true, Feature.DiscardUnsyncedChangesHandler), - [Feature.ManualRecoveryHandler] = _ => new(true, Feature.ManualRecoveryHandler), - [Feature.GetProgressObservable] = _ => new(true, Feature.GetProgressObservable), - [Feature.PartitionSyncConfiguration] = _ => new(true, Feature.PartitionSyncConfiguration), - [Feature.FlexibleSyncConfiguration] = _ => new(true, Feature.FlexibleSyncConfiguration), - [Feature.Anonymous] = instruction => AnalyzeRealmApi(instruction, Feature.Anonymous), - [Feature.EmailPassword] = instruction => AnalyzeRealmApi(instruction, Feature.EmailPassword), - [Feature.Facebook] = instruction => AnalyzeRealmApi(instruction, Feature.Facebook), - [Feature.Google] = instruction => AnalyzeRealmApi(instruction, Feature.Google), - [Feature.Apple] = instruction => AnalyzeRealmApi(instruction, Feature.Apple), - [Feature.JWT] = instruction => AnalyzeRealmApi(instruction, Feature.JWT), - [Feature.ApiKey] = instruction => AnalyzeRealmApi(instruction, Feature.ApiKey), - [Feature.Function] = instruction => AnalyzeRealmApi(instruction, Feature.Function), - [Feature.CallAsync] = instruction => AnalyzeRealmApi(instruction, Feature.CallAsync), - [Feature.GetMongoClient] = _ => new(true, Feature.GetMongoClient), - [Feature.DynamicApi] = _ => new(true, Feature.DynamicApi) - }; - - _analyzeUserAssemblyTask = Task.Run(() => - { - AnalyzeUserAssembly(module); - }); - - FeatureAnalysisResult AnalyzeCollectionProperty(IMemberDefinition member, string primitiveKey, string referenceKey, int genericArgIndex = 0) - { - if (member is not PropertyDefinition property || - property.PropertyType is not GenericInstanceType genericType || - genericType.GenericArguments.Count < genericArgIndex + 1) - { - return default; - } - - var keyToAdd = genericType.GenericArguments[genericArgIndex].IsPrimitive ? - primitiveKey : referenceKey; - - var shouldDelete = ContainsAllRelatedFeatures(keyToAdd, referenceKey, primitiveKey); - return new(shouldDelete, keyToAdd); - } - - FeatureAnalysisResult AnalyzeRealmApi(Instruction instruction, string key) - { - if (IsInRealmNamespace(instruction?.Operand)) - { - return new(true, key); - } - - return default; - } - - bool ContainsAllRelatedFeatures(string key, params string[] features) - { - foreach (var feature in features) - { - if (feature != key && (!_realmFeaturesToAnalyze.TryGetValue(feature, out var value) || value == 0)) - { - return false; - } - } - - return true; - } - } - - private void AnalyzeUserAssembly(ModuleDefinition module) - { - try - { - // collect environment details - _realmEnvMetrics[UserEnvironment.UserId] = AnonymizedUserId; - _realmEnvMetrics[UserEnvironment.LegacyUserId] = LegacyAnonymizedUserId; - _realmEnvMetrics[UserEnvironment.ProjectId] = SHA256Hash(Encoding.UTF8.GetBytes(_config.ProjectId ?? module.Assembly.Name.Name)); - _realmEnvMetrics[UserEnvironment.RealmSdk] = "dotnet"; - _realmEnvMetrics[UserEnvironment.RealmSdkVersion] = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - _realmEnvMetrics[UserEnvironment.Language] = "c#"; - _realmEnvMetrics[UserEnvironment.LanguageVersion] = InferLanguageVersion(_config.NetFrameworkTarget, _config.NetFrameworkTargetVersion); - _realmEnvMetrics[UserEnvironment.HostOsType] = GetHostOsName(); - _realmEnvMetrics[UserEnvironment.HostOsVersion] = Environment.OSVersion.Version.ToString(); - _realmEnvMetrics[UserEnvironment.HostCpuArch] = GetHostCpuArchitecture(); - _realmEnvMetrics[UserEnvironment.TargetOsType] = _config.TargetOSName; - _realmEnvMetrics[UserEnvironment.TargetCpuArch] = _config.TargetArchitecture; - _realmEnvMetrics[UserEnvironment.TargetOsVersion] = _config.TargetOsVersion; - _realmEnvMetrics[UserEnvironment.TargetOsMinimumVersion] = _config.TargetOsMinimumVersion; - _realmEnvMetrics[UserEnvironment.CoreVersion] = CoreVersion; - _realmEnvMetrics[UserEnvironment.FrameworkUsedInConjunction] = _config.FrameworkName; - _realmEnvMetrics[UserEnvironment.FrameworkUsedInConjunctionVersion] = _config.FrameworkVersion; - _realmEnvMetrics[UserEnvironment.SdkInstallationMethod] = _config.InstallationMethod; - _realmEnvMetrics[UserEnvironment.NetFramework] = _config.NetFrameworkTarget; - _realmEnvMetrics[UserEnvironment.NetFrameworkVersion] = _config.NetFrameworkTargetVersion; - _realmEnvMetrics[UserEnvironment.Compiler] = _config.Compiler; - - foreach (var type in module.Types.ToArray()) - { - InternalAnalyzeSdkApi(type); - } - - // We need to first analyze the features before we can set `IsSyncEnabled`. - _realmFeaturesToAnalyze.TryGetValue(Feature.PartitionSyncConfiguration, out var isPbsUsed); - _realmFeaturesToAnalyze.TryGetValue(Feature.FlexibleSyncConfiguration, out var isFlxSUsed); - _realmEnvMetrics[UserEnvironment.IsSyncEnabled] = (isPbsUsed == 1 || isFlxSUsed == 1).ToString().ToLower(); - } - catch (Exception e) - { - _logger.Warning($"Could not analyze the user's assembly. Please file an issue at https://github.com/realm/realm-dotnet/issues. {Environment.NewLine}{e}"); - } - } - - public void AnalyzeRealmClassProperties(WeaveTypeResult[] types) - { - if (_config.AnalyticsCollection == AnalyticsCollection.Disabled) - { - return; - } - - _analyzeUserAssemblyTask.Wait(); - - foreach (var type in types) - { - if (type.Properties == null) - { - continue; - } - - foreach (var propertyResult in type.Properties.Where(p => p.Woven)) - { - var property = propertyResult.Property; - - var key = property!.PropertyType.Name; - if (!_classAnalysisSetters.ContainsKey(key) && property.PropertyType.MetadataType == MetadataType.Class) - { - key = "Class"; - } - - AnalyzeClassFeature(key, property); - - foreach (var attribute in property.CustomAttributes) - { - AnalyzeClassFeature(attribute.AttributeType.Name, property); - } - } - } - - void AnalyzeClassFeature(string key, PropertyDefinition property) - { - if (_classAnalysisSetters.TryGetValue(key, out var featureFunc)) - { - var analysisResult = featureFunc(property); - - if (!string.IsNullOrEmpty(analysisResult.DictKey)) - { - _realmFeaturesToAnalyze[analysisResult.DictKey] = 1; - } - - if (analysisResult.ShouldDelete) - { - _classAnalysisSetters.Remove(key); - } - } - } - } - - private void InternalAnalyzeSdkApi(TypeDefinition type) - { - if (!type.IsClass) - { - return; - } - - if (type.IsIAsymmetricObjectImplementor(_references) || type.IsAsymmetricObjectDescendant(_references)) - { - _realmFeaturesToAnalyze[Feature.IAsymmetricObject] = 1; - } - else if (type.IsIEmbeddedObjectImplementor(_references) || type.IsEmbeddedObjectDescendant(_references)) - { - _realmFeaturesToAnalyze[Feature.IEmbeddedObject] = 1; - } - - AnalyzeClassMethods(type); - - foreach (var innerType in type.NestedTypes) - { - InternalAnalyzeSdkApi(innerType); - } - } - - private void AnalyzeClassMethods(TypeDefinition type) - { - var prefixes = new[] { "get_", "set_", "add_" }; - - foreach (var method in type.Methods) - { - if (!method.HasBody) - { - continue; - } - - foreach (var cil in method.Body.Instructions) - { - var key = (cil.Operand as MemberReference)?.Name; - if (key.IsNullOrEmpty()) - { - continue; - } - - var (prefix, index) = prefixes.Select(p => (p, key.IndexOf(p, StringComparison.Ordinal))) - .OrderByDescending(p => p.Item2) - .First(); - - if (index > -1) - { - // when dealing with: - // set_ShouldCompactOnLaunch - // add_RealmChanged - // add_PropertyChanged - // get_DynamicApi - key = key[prefix.Length..]; - } - - if (!_apiAnalysisSetters.ContainsKey(key) && - cil.Operand is MethodReference methodReference && - methodReference.ReturnType.DeclaringType != null) - { - // when dealing with ThreadSafeReference - key = methodReference.ReturnType.DeclaringType.Name; - } - - if (!_apiAnalysisSetters.ContainsKey(key) && key == ".ctor") - { - key = ((MemberReference)cil.Operand).DeclaringType.Name; - } - - if (!_apiAnalysisSetters.TryGetValue(key, out var featureFunc)) - { - continue; - } - - var analysisResult = featureFunc.Invoke(cil); - - if (!string.IsNullOrEmpty(analysisResult.DictKey)) - { - _realmFeaturesToAnalyze[analysisResult.DictKey] = 1; - } - - if (analysisResult.ShouldDelete) - { - _apiAnalysisSetters.Remove(key); - } - } - } - } - - public async Task SubmitAnalytics() - { - var payload = "Analytics disabled"; - - // this is necessary since when not in the assembly that has the models - // AnalyzeRealmClassProperties won't be called - _analyzeUserAssemblyTask.Wait(); - - if (_config.AnalyticsCollection != AnalyticsCollection.Disabled) - { - try - { - const string sendAddr = "https://data.mongodb-api.com/app/realmsdkmetrics-zmhtm/endpoint/v2/metric?data="; - payload = GetJsonPayload(); - - if (_config.AnalyticsCollection != AnalyticsCollection.DryRun) - { - var base64Payload = Convert.ToBase64String(Encoding.UTF8.GetBytes(payload)); - await SendRequest(sendAddr, base64Payload, string.Empty); - } - } - catch (Exception e) - { - payload = e.Message; - } - } - - if (!_config.AnalyticsLogPath.IsNullOrEmpty()) - { - File.WriteAllText(_config.AnalyticsLogPath, payload); - } - } - - private string GetJsonPayload() - { - var jsonPayload = new StringBuilder(); - - jsonPayload.Append('{'); - - jsonPayload.Append(GetJsonString(_realmEnvMetrics)); - - var featuresString = GetJsonString(_realmFeaturesToAnalyze, Metric.SdkFeatures); - if (!string.IsNullOrEmpty(featuresString)) - { - jsonPayload.Append(','); - jsonPayload.Append(featuresString); - } - - jsonPayload.Append('}'); - - return jsonPayload.ToString(); - - string GetJsonString(IDictionary dict, IDictionary? keyMapping = null) - { - var mapping = dict - .Select(kvp => - { - if (kvp.Value is byte and 0 || - (kvp.Value is string s && string.IsNullOrEmpty(s))) - { - // skip empty strings/0 - return null; - } - - var key = keyMapping == null ? kvp.Key : keyMapping[kvp.Key]; - var value = kvp.Value is string ? $"\"{kvp.Value}\"" : $"{kvp.Value}"; - return $"\"{key}\":{value}"; - }) - .Where(s => s != null); - - return string.Join(",", mapping); - } - } - - private static async Task SendRequest(string prefixAddr, string payload, string suffixAddr) - { - using var httpClient = new HttpClient(); - httpClient.Timeout = TimeSpan.FromSeconds(4); - await httpClient.GetAsync(new Uri(prefixAddr + payload + suffixAddr)); - } - - private static bool IsInRealmNamespace(object? operand) - { - if (operand is not MemberReference memberReference) - { - return false; - } - - return memberReference?.DeclaringType?.FullName?.StartsWith("Realms", StringComparison.Ordinal) == true; - } - - public class Config - { - public AnalyticsCollection AnalyticsCollection { get; init; } - - public string? AnalyticsLogPath { get; init; } - - public string TargetOSName { get; } - - public string NetFrameworkTarget { get; } - - public string NetFrameworkTargetVersion { get; } - - public string InstallationMethod { get; } - - public string FrameworkName { get; } - - public string FrameworkVersion { get; } - - public string Compiler { get; } - - // These are only available on Unity for now. - public string TargetArchitecture { get; init; } = Metric.Unknown(); - - public string TargetOsVersion { get; init; } = Metric.Unknown(); - - public string TargetOsMinimumVersion { get; init; } = Metric.Unknown(); - - public string? ProjectId { get; init; } - - public Config( - string targetOSName, - string netFrameworkTarget, - string netFrameworkTargetVersion, - string installationMethod, - string frameworkName, - string frameworkVersion, - string compiler) - { - TargetOSName = targetOSName; - NetFrameworkTarget = netFrameworkTarget; - NetFrameworkTargetVersion = netFrameworkTargetVersion; - InstallationMethod = installationMethod; - FrameworkName = frameworkName; - FrameworkVersion = frameworkVersion; - Compiler = compiler; - } - } - - public enum AnalyticsCollection - { - Disabled, - DryRun, - Full, - } - - private readonly struct FeatureAnalysisResult - { - public bool ShouldDelete { get; } - - public string DictKey { get; } - - public FeatureAnalysisResult(bool isToDelete = false, string dictKey = "") - { - ShouldDelete = isToDelete; - DictKey = dictKey; - } - } - } -} diff --git a/Realm/Realm.Weaver/Analytics/AnalyticsUtils.cs b/Realm/Realm.Weaver/Analytics/AnalyticsUtils.cs deleted file mode 100644 index b0cd35c052..0000000000 --- a/Realm/Realm.Weaver/Analytics/AnalyticsUtils.cs +++ /dev/null @@ -1,309 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.NetworkInformation; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using Mono.Cecil; -using static RealmWeaver.Metric; -using OperatingSystem = RealmWeaver.Metric.OperatingSystem; - -namespace RealmWeaver -{ - internal static class AnalyticsUtils - { - private static readonly Lazy _anonymizedUserId = new(() => - { - var id = string.Empty; - try - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var machineIdToParse = RunProcess("reg", "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography -v MachineGuid"); - var regex = new Regex("\\s+MachineGuid\\s+\\w+\\s+((\\w+-?)+)", RegexOptions.Multiline); - var match = regex.Match(machineIdToParse); - - if (match.Groups.Count > 1) - { - id = match.Groups[1].Value; - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - var machineIdToParse = RunProcess("ioreg", "-rd1 -c IOPlatformExpertDevice"); - var regex = new Regex(".*\\\"IOPlatformUUID\\\"\\s=\\s\\\"(.+)\\\"", RegexOptions.Multiline); - var match = regex.Match(machineIdToParse); - - if (match.Groups.Count > 1) - { - id = match.Groups[1].Value; - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - id = File.ReadAllText("/etc/machine-id"); - } - - if (id.Length == 0) - { - return Unknown(); - } - - // We're salting the id with an hardcoded byte array just to avoid that a machine is recognizable across - // unrelated projects that use the same mechanics to obtain a machine's ID - const string salt = "Realm is great"; - var saltedId = Encoding.UTF8.GetBytes(id + salt); - return SHA256Hash(saltedId); - } - catch - { - return Unknown(); - } - }); - - private static readonly Lazy _legacyAnonymizedUserId = new(() => - { - try - { - var id = NetworkInterface.GetAllNetworkInterfaces() - .Where(n => n.Name == "en0" || (n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback)) - .Select(n => n.GetPhysicalAddress().GetAddressBytes()) - .First(); - return SHA256Hash(id, useLegacyEncoding: true); - } - catch - { - return Unknown(); - } - }); - - public static string AnonymizedUserId => _anonymizedUserId.Value; - - public static string LegacyAnonymizedUserId => _legacyAnonymizedUserId.Value; - - public static string GetTargetOsName(FrameworkName frameworkName) - { - var targetOs = frameworkName.Identifier; - - if (targetOs.ContainsIgnoreCase("android")) - { - return OperatingSystem.Android; - } - - if (targetOs.ContainsIgnoreCase("ios")) - { - return OperatingSystem.Ios; - } - - if (targetOs.ContainsIgnoreCase("mac")) - { - return OperatingSystem.MacOS; - } - - if (targetOs.ContainsIgnoreCase("tvos")) - { - return OperatingSystem.TvOs; - } - - if (targetOs.ContainsIgnoreCase("linux")) - { - return OperatingSystem.Linux; - } - - if (targetOs.ContainsIgnoreCase("win") || targetOs.ContainsIgnoreCase("net4")) - { - return OperatingSystem.Windows; - } - - if (targetOs.ContainsIgnoreCase("core") || - targetOs.ContainsIgnoreCase("standard") || - targetOs.ContainsIgnoreCase("net")) - { - return OperatingSystem.CrossPlatform; - } - - return Unknown(frameworkName.Identifier); - } - - public static FrameworkInfo GetFrameworkAndVersion(ModuleDefinition module) - { - // the order in the array matters as we first need to look at the libraries (maui and forms) - // and then at the frameworks (xamarin native, Catalyst and UWP) - var possibleFrameworks = new Dictionary - { - { "Microsoft.Maui", Framework.Maui }, - { "Xamarin.Forms.Core", Framework.XamarinForms }, - { "Xamarin.iOS", Framework.Xamarin }, - { "Xamarin.tvOS", Framework.Xamarin }, - { "Xamarin.Mac", Framework.Xamarin }, - { "Mono.Android", Framework.Xamarin }, - { "Microsoft.MacCatalyst", Framework.MacCatalyst }, - { "Windows.Foundation.UniversalApiContract", Framework.Uwp }, - }; - - foreach (var kvp in possibleFrameworks) - { - var frameworkUsedInConjunction = module.AssemblyReferences.SingleOrDefault(a => a.Name == kvp.Key); - if (frameworkUsedInConjunction != null) - { - return new(kvp.Value, frameworkUsedInConjunction.Version.ToString()); - } - } - - return new("No framework of interest", "0.0.0"); - } - - public static string SHA256Hash(byte[] bytes, bool useLegacyEncoding = false) - { - using var sha256 = SHA256.Create(); - var hash = sha256.ComputeHash(bytes); - return useLegacyEncoding ? BitConverter.ToString(hash) : Convert.ToBase64String(hash); - } - - public static string GetHostCpuArchitecture() => RuntimeInformation.OSArchitecture switch - { - Architecture.X86 => CpuArchitecture.X86, - Architecture.Arm => CpuArchitecture.Arm, - Architecture.Arm64 => CpuArchitecture.Arm64, - Architecture.X64 => CpuArchitecture.X64, - _ => Unknown(RuntimeInformation.OSArchitecture.ToString()) - }; - - public static string GetHostOsName() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return OperatingSystem.Windows; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return OperatingSystem.Linux; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return OperatingSystem.MacOS; - } - - return Unknown(System.Environment.OSVersion.Platform.ToString()); - } - - public static string InferLanguageVersion(string netFramework, string netFrameworkVersion) - { - // We don't have a reliable way to get the version in the weaver so we're using the default version - // associated with the framework. - // Values taken from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version - if (!netFramework.ContainsIgnoreCase("net")) - { - // Likely this isn't the model assembly, but the platform specific one - return Unknown(netFramework); - } - - if (netFrameworkVersion.ContainsIgnoreCase("2.0") || - netFrameworkVersion.ContainsIgnoreCase("4.")) - { - return "7.3"; - } - - if (netFrameworkVersion.ContainsIgnoreCase("2.1") || - netFrameworkVersion.ContainsIgnoreCase("3.1")) - { - return "8"; - } - - if (netFrameworkVersion.ContainsIgnoreCase("5.0")) - { - return "9"; - } - - if (netFrameworkVersion.ContainsIgnoreCase("6.0")) - { - return "10"; - } - - if (netFrameworkVersion.ContainsIgnoreCase("7.0")) - { - return "11"; - } - - return Unknown(); - } - - private static bool ContainsIgnoreCase(this string @this, string strCompare) => - @this.IndexOf(strCompare, StringComparison.OrdinalIgnoreCase) > -1; - - private static string RunProcess(string filename, string arguments) - { - using var proc = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = filename, - Arguments = arguments, - UseShellExecute = false, - RedirectStandardOutput = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, -#if DEBUG - RedirectStandardError = true, -#endif - } - }; - - proc.Start(); - - var stdout = new StringBuilder(); - while (!proc.HasExited) - { - stdout.AppendLine(proc.StandardOutput.ReadToEnd()); -#if DEBUG - stdout.AppendLine(proc.StandardError.ReadToEnd()); -#endif - } - - stdout.AppendLine(proc.StandardOutput.ReadToEnd()); -#if DEBUG - stdout.AppendLine(proc.StandardError.ReadToEnd()); -#endif - - return stdout.ToString(); - } - - public readonly struct FrameworkInfo - { - public string Name { get; } - - public string Version { get; } - - public FrameworkInfo(string name, string version) - { - Name = name; - Version = version; - } - } - } -} diff --git a/Realm/Realm.Weaver/Analytics/Metric.cs b/Realm/Realm.Weaver/Analytics/Metric.cs deleted file mode 100644 index 2b372cc698..0000000000 --- a/Realm/Realm.Weaver/Analytics/Metric.cs +++ /dev/null @@ -1,210 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace RealmWeaver -{ - internal static class Metric - { - public static string Unknown(string? clarifier = null) - { - var result = "Unknown"; - if (!clarifier.IsNullOrEmpty()) - { - result += $" ({clarifier})"; - } - - return result; - } - - public static class OperatingSystem - { - public const string Linux = "Linux"; - public const string MacOS = "macOS"; - public const string Windows = "Windows"; - public const string CrossPlatform = "Cross Platform"; - public const string Android = "Android"; - public const string Ios = "iOS"; - public const string IpadOs = "iPadOS"; - public const string WatchOs = "watchOS"; - public const string TvOs = "tvOS"; - public const string XboxOne = "XboxOne"; - } - - public static class CpuArchitecture - { - public const string X86 = "x86"; - public const string X64 = "x64"; - public const string Arm = "Arm"; - public const string Arm64 = "Arm64"; - public const string Universal = "Universal"; - } - - public static class Framework - { - public const string Unity = "Unity"; - public const string UnityEditor = "Unity Editor"; - public const string Maui = "MAUI"; - public const string Xamarin = "Xamarin"; - public const string XamarinForms = "Xamarin Forms"; - public const string Uwp = "UWP"; - public const string MacCatalyst = "MacCatalyst"; - } - - public static class Environment - { - public const string LegacyUserId = "distinct_id"; - public const string UserId = "builder_id"; - public const string ProjectId = "Anonymized Bundle ID"; - public const string RealmSdk = "Binding"; - public const string RealmSdkVersion = "Realm Version"; - public const string Language = "Language"; - public const string LanguageVersion = "Language Version"; - public const string HostOsType = "Host OS Type"; - public const string HostOsVersion = "Host OS Version"; - public const string HostCpuArch = "Host CPU Arch"; - public const string TargetOsType = "Target OS Type"; - public const string TargetCpuArch = "Target CPU Arch"; - public const string TargetOsMinimumVersion = "Target OS Minimum Version"; - public const string TargetOsVersion = "Target OS Version"; - public const string CoreVersion = "Core Version"; - public const string IsSyncEnabled = "Sync Enabled"; - public const string FrameworkUsedInConjunction = "Framework"; // this refers to UI frameworks and similar Realm is used together with - public const string FrameworkUsedInConjunctionVersion = "Framework Version"; - public const string SdkInstallationMethod = "Installation Method"; - public const string NetFramework = "Net Framework"; - public const string NetFrameworkVersion = "Net Framework Version"; - public const string Compiler = "Compiler"; - - // These are not currently supported - [SuppressMessage("Performance", "CA1823:Avoid unused private fields", Justification = "Placeholder")] - [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Placeholder")] - private const string _IdeUsed = "IDE"; - - [SuppressMessage("Performance", "CA1823:Avoid unused private fields", Justification = "Placeholder")] - [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Placeholder")] - private const string _IdeUsedVersion = "IDE Version"; - } - - public static class Feature - { - // ReSharper disable InconsistentNaming - public const string IEmbeddedObject = nameof(IEmbeddedObject); - public const string IAsymmetricObject = nameof(IAsymmetricObject); - public const string ReferenceList = nameof(ReferenceList); - public const string PrimitiveList = nameof(PrimitiveList); - public const string ReferenceDictionary = nameof(ReferenceDictionary); - public const string PrimitiveDictionary = nameof(PrimitiveDictionary); - public const string ReferenceSet = nameof(ReferenceSet); - public const string PrimitiveSet = nameof(PrimitiveSet); - public const string RealmInteger = nameof(RealmInteger); - public const string RealmObjectReference = nameof(RealmObjectReference); - public const string RealmValue = nameof(RealmValue); - public const string BacklinkAttribute = nameof(BacklinkAttribute); - public const string GetInstanceAsync = nameof(GetInstanceAsync); - public const string GetInstance = nameof(GetInstance); - public const string Find = nameof(Find); - public const string WriteAsync = nameof(WriteAsync); - public const string ThreadSafeReference = nameof(ThreadSafeReference); - public const string Add = nameof(Add); - public const string ShouldCompactOnLaunch = nameof(ShouldCompactOnLaunch); - public const string MigrationCallback = nameof(MigrationCallback); - public const string RealmChanged = nameof(RealmChanged); - public const string ListSubscribeForNotifications = nameof(ListSubscribeForNotifications); - public const string SetSubscribeForNotifications = nameof(SetSubscribeForNotifications); - public const string DictionarySubscribeForNotifications = nameof(DictionarySubscribeForNotifications); - public const string ResultSubscribeForNotifications = nameof(ResultSubscribeForNotifications); - public const string RecoverOrDiscardUnsyncedChangesHandler = nameof(RecoverOrDiscardUnsyncedChangesHandler); - public const string RecoverUnsyncedChangesHandler = nameof(RecoverUnsyncedChangesHandler); - public const string DiscardUnsyncedChangesHandler = nameof(DiscardUnsyncedChangesHandler); - public const string ManualRecoveryHandler = nameof(ManualRecoveryHandler); - public const string GetProgressObservable = nameof(GetProgressObservable); - public const string PartitionSyncConfiguration = nameof(PartitionSyncConfiguration); - public const string FlexibleSyncConfiguration = nameof(FlexibleSyncConfiguration); - public const string Anonymous = nameof(Anonymous); - public const string EmailPassword = nameof(EmailPassword); - public const string Facebook = nameof(Facebook); - public const string Google = nameof(Google); - public const string Apple = nameof(Apple); - public const string JWT = nameof(JWT); - public const string ApiKey = nameof(ApiKey); - public const string Function = nameof(Function); - public const string CallAsync = nameof(CallAsync); - public const string GetMongoClient = nameof(GetMongoClient); - public const string DynamicApi = nameof(DynamicApi); - public const string ConnectionNotification = nameof(ConnectionNotification); - public const string ObjectNotification = nameof(ObjectNotification); - } - - // This holds a mapping from Feature -> the name we send to DW. - public static readonly Dictionary SdkFeatures = new() - { - [Feature.IEmbeddedObject] = "Embedded_Object", - [Feature.IAsymmetricObject] = "Asymmetric_Object", - [Feature.ReferenceList] = "Object_List", - [Feature.PrimitiveList] = "Primitive_List", - [Feature.ReferenceDictionary] = "Object_Dict", - [Feature.PrimitiveDictionary] = "Primitive_Dict", - [Feature.ReferenceSet] = "Object_Set", - [Feature.PrimitiveSet] = "Primitive_Set", - [Feature.RealmInteger] = "Counter", - [Feature.RealmObjectReference] = "Object_Link", - [Feature.RealmValue] = "Mixed", - [Feature.BacklinkAttribute] = "Backlink", - - [Feature.GetInstanceAsync] = "Async_Open", - [Feature.GetInstance] = "Sync_Open", - - [Feature.Find] = "Find_PK", - [Feature.WriteAsync] = "Write_Async", - [Feature.ThreadSafeReference] = "Thread_Safe_Reference", - [Feature.Add] = "Insert_Modified", - [Feature.ShouldCompactOnLaunch] = "Compact_On_Launch", - [Feature.MigrationCallback] = "Migration_Block", - [Feature.RealmChanged] = "Realm_Notifications", - [Feature.ListSubscribeForNotifications] = "List_Notifications", - [Feature.SetSubscribeForNotifications] = "Set_Notifications", - [Feature.DictionarySubscribeForNotifications] = "Dict_Notifications", - [Feature.ResultSubscribeForNotifications] = "Results_Notifications", - [Feature.ObjectNotification] = "Object_Notifications", - [Feature.RecoverOrDiscardUnsyncedChangesHandler] = "CR_Recover_Discard", - [Feature.RecoverUnsyncedChangesHandler] = "CR_Recover", - [Feature.DiscardUnsyncedChangesHandler] = "CR_Discard", - [Feature.ManualRecoveryHandler] = "CR_Manual", - [Feature.GetProgressObservable] = "Progress_Notification", - [Feature.PartitionSyncConfiguration] = "Pbs_Sync", - [Feature.FlexibleSyncConfiguration] = "Flx_Sync", - [Feature.Anonymous] = "Auth_Anon", - [Feature.EmailPassword] = "Auth_Email", - [Feature.Facebook] = "Auth_Facebook", - [Feature.Google] = "Auth_Google", - [Feature.Apple] = "Auth_Apple", - [Feature.JWT] = "Auth_JWT", - [Feature.ApiKey] = "Auth_API_Key", - [Feature.Function] = "Auth_Function", - [Feature.CallAsync] = "Remote_Function", - [Feature.GetMongoClient] = "Remote_Mongo", - [Feature.DynamicApi] = "Dynamic_API", - [Feature.ConnectionNotification] = "Connection_Notification", - - // ["NOT_SUPPORTED_YET"] = "Query_Async", - }; - } -} diff --git a/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs b/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs index d467a30f52..8e5303fb73 100644 --- a/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs +++ b/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs @@ -199,9 +199,6 @@ internal static bool IsIndexable(this PropertyDefinition property, ImportedRefer public static bool ContainsRealmObject(this PropertyDefinition property, ImportedReferences references) => property.PropertyType.Resolve().IsRealmObjectInheritor(references); - public static bool ContainsAsymmetricObject(this PropertyDefinition property, ImportedReferences references) => - property.PropertyType.Resolve().IsAsymmetricObjectInheritor(references); - public static bool ContainsEmbeddedObject(this PropertyDefinition property, ImportedReferences references) => property.PropertyType.Resolve().IsEmbeddedObjectInheritor(references); diff --git a/Realm/Realm.Weaver/Extensions/TypeDefinitionExtensions.cs b/Realm/Realm.Weaver/Extensions/TypeDefinitionExtensions.cs index 4f9e44be47..d29f2b4d14 100644 --- a/Realm/Realm.Weaver/Extensions/TypeDefinitionExtensions.cs +++ b/Realm/Realm.Weaver/Extensions/TypeDefinitionExtensions.cs @@ -29,20 +29,13 @@ public static bool IsEmbeddedObjectInheritor(this TypeDefinition type, ImportedR public static bool IsRealmObjectInheritor(this TypeDefinition type, ImportedReferences references) => type.BaseType.IsSameAs(references.RealmObject); - public static bool IsAsymmetricObjectInheritor(this TypeDefinition type, ImportedReferences references) => - type.BaseType.IsSameAs(references.AsymmetricObject); - public static bool IsValidRealmObjectBaseInheritor(this TypeDefinition type, ImportedReferences references) => type.IsRealmObjectInheritor(references) || - type.IsEmbeddedObjectInheritor(references) || - type.IsAsymmetricObjectInheritor(references); + type.IsEmbeddedObjectInheritor(references); public static bool IsIEmbeddedObjectImplementor(this TypeDefinition type, ImportedReferences references) => IsImplementorOf(type, references.IEmbeddedObject); - public static bool IsIAsymmetricObjectImplementor(this TypeDefinition type, ImportedReferences references) => - IsImplementorOf(type, references.IAsymmetricObject); - public static bool IsImplementorOf(TypeDefinition @this, params TypeReference[] targetInterfaces) { foreach (var @interface in @this.Interfaces) diff --git a/Realm/Realm.Weaver/Extensions/TypeReferenceExtensions.cs b/Realm/Realm.Weaver/Extensions/TypeReferenceExtensions.cs index 7969d41949..5ed116fe0a 100644 --- a/Realm/Realm.Weaver/Extensions/TypeReferenceExtensions.cs +++ b/Realm/Realm.Weaver/Extensions/TypeReferenceExtensions.cs @@ -55,10 +55,7 @@ public static bool IsAnyRealmObject(this TypeReference @this, ImportedReferences || @this.IsRealmObjectDescendant(references); public static bool IsRealmObjectDescendant(this TypeReference @this, ImportedReferences references) => - IsDescendantOf(@this, references.RealmObject, references.EmbeddedObject, references.AsymmetricObject); - - public static bool IsAsymmetricObjectDescendant(this TypeReference @this, ImportedReferences references) => - IsDescendantOf(@this, references.AsymmetricObject); + IsDescendantOf(@this, references.RealmObject, references.EmbeddedObject); public static bool IsEmbeddedObjectDescendant(this TypeReference @this, ImportedReferences references) => IsDescendantOf(@this, references.EmbeddedObject); diff --git a/Realm/Realm.Weaver/ImportedReferences.cs b/Realm/Realm.Weaver/ImportedReferences.cs index 1aa113a0f2..8513c6bdad 100644 --- a/Realm/Realm.Weaver/ImportedReferences.cs +++ b/Realm/Realm.Weaver/ImportedReferences.cs @@ -78,14 +78,10 @@ internal abstract class ImportedReferences public TypeReference IEmbeddedObject { get; private set; } - public TypeReference IAsymmetricObject { get; private set; } - public TypeReference ManagedAccessor { get; private set; } public TypeReference EmbeddedObject { get; private set; } - public TypeReference AsymmetricObject { get; private set; } - public MethodReference RealmObject_get_IsManaged { get; private set; } public MethodReference RealmObject_get_Realm { get; private set; } @@ -140,8 +136,6 @@ internal abstract class ImportedReferences public MethodReference CollectionExtensions_PopulateDictionary { get; private set; } - public TypeReference SyncSession { get; private set; } - protected ModuleDefinition Module { get; } public TypeSystem Types { get; } @@ -225,11 +219,9 @@ private void InitializeRealm(IMetadataScope realmAssembly) ManagedAccessor = new TypeReference("Realms", "ManagedAccessor", Module, realmAssembly); RealmObject = new TypeReference("Realms", "RealmObject", Module, realmAssembly); EmbeddedObject = new TypeReference("Realms", "EmbeddedObject", Module, realmAssembly); - AsymmetricObject = new TypeReference("Realms", "AsymmetricObject", Module, realmAssembly); RealmSchema_PropertyType = new TypeReference("Realms.Schema", "PropertyType", Module, realmAssembly, valueType: true); RealmValue = new TypeReference("Realms", "RealmValue", Module, realmAssembly, valueType: true); IEmbeddedObject = new TypeReference("Realms", "IEmbeddedObject", Module, realmAssembly, valueType: false); - IAsymmetricObject = new TypeReference("Realms", "IAsymmetricObject", Module, realmAssembly, valueType: false); RealmValue_GetNull = new MethodReference("get_Null", RealmValue, RealmValue) { HasThis = false }; { @@ -347,8 +339,6 @@ private void InitializeRealm(IMetadataScope realmAssembly) RealmSchema_AddDefaultTypes.Parameters.Add(new ParameterDefinition(ienumerableOfType)); } - SyncSession = new TypeReference("Realms.Sync", "Session", Module, realmAssembly); - var collectionExtensions = new TypeReference("Realms", "CollectionExtensions", Module, realmAssembly); CollectionExtensions_PopulateCollection = new MethodReference("PopulateCollection", Types.Void, collectionExtensions) { HasThis = false }; { diff --git a/Realm/Realm.Weaver/RealmWeaver.cs b/Realm/Realm.Weaver/RealmWeaver.cs index f301457e41..6d02d19612 100644 --- a/Realm/Realm.Weaver/RealmWeaver.cs +++ b/Realm/Realm.Weaver/RealmWeaver.cs @@ -178,20 +178,12 @@ public Weaver(ModuleDefinition module, ILogger logger, string framework) _propertyChanged_DoNotNotify_Ctor = new Lazy(GetOrAddPropertyChanged_DoNotNotify); } - public WeaveModuleResult Execute(Analytics.Config analyticsConfig) + public WeaveModuleResult Execute() { - var analytics = new Analytics(analyticsConfig, _references, _logger, _moduleDefinition); - - var result = ExecuteInternal(out var weaveResults); - analytics.AnalyzeRealmClassProperties(weaveResults); - - // Don't wait for submission - _ = analytics.SubmitAnalytics(); - - return result; + return ExecuteInternal(); } - private WeaveModuleResult ExecuteInternal(out WeaveTypeResult[] weaveResults) + private WeaveModuleResult ExecuteInternal() { _logger.Debug("Weaving file: " + _moduleDefinition.FileName); @@ -199,20 +191,18 @@ private WeaveModuleResult ExecuteInternal(out WeaveTypeResult[] weaveResults) // specific code in another assembly, but we still want to report what target the user is building for if (_references.Realm == null) { - weaveResults = Array.Empty(); return WeaveModuleResult.Skipped($"Not weaving assembly '{_moduleDefinition.Assembly.Name}' because it doesn't reference Realm."); } var isWoven = _moduleDefinition.Assembly.CustomAttributes.Any(a => a.AttributeType.IsSameAs(_references.WovenAssemblyAttribute)); if (isWoven) { - weaveResults = Array.Empty(); return WeaveModuleResult.Skipped($"Not weaving assembly '{_moduleDefinition.Assembly.Name}' because it has already been processed."); } var matchingTypes = GetMatchingTypes().ToArray(); - weaveResults = matchingTypes.Select(matchingType => + var weaveResults = matchingTypes.Select(matchingType => { var type = matchingType.Type; var isGenerated = matchingType.IsGenerated; @@ -234,12 +224,9 @@ private WeaveModuleResult ExecuteInternal(out WeaveTypeResult[] weaveResults) _moduleDefinition.Assembly.CustomAttributes.Add(wovenAssemblyAttribute); var failedResults = weaveResults.Where(r => !r.IsSuccessful).ToArray(); - if (failedResults.Any()) - { - return WeaveModuleResult.Error($"The following types had errors when woven: {string.Join(", ", failedResults.Select(f => f.Type))}"); - } - - return WeaveModuleResult.Success(weaveResults); + return failedResults.Any() + ? WeaveModuleResult.Error($"The following types had errors when woven: {string.Join(", ", failedResults.Select(f => f.Type))}") + : WeaveModuleResult.Success(weaveResults); } private static void RemoveBackingFields(TypeDefinition type, HashSet backingFields) @@ -570,11 +557,6 @@ private WeavePropertyResult WeaveProperty(PropertyDefinition prop, TypeDefinitio return WeavePropertyResult.Warning($"{type.Name}.{prop.Name} is not an automatic property but its type is a RealmObject/EmbeddedObject which normally indicates a relationship."); } - if (prop.ContainsAsymmetricObject(_references)) - { - return WeavePropertyResult.Warning($"{type.Name}.{prop.Name} is not an automatic property but its type is a AsymmetricObject. This usually indicates a relationship but AsymmetricObjects are not allowed to be the receiving end of any relationships."); - } - return WeavePropertyResult.Skipped("Property is not autoimplemented"); } @@ -619,10 +601,6 @@ private WeavePropertyResult WeaveProperty(PropertyDefinition prop, TypeDefinitio return WeavePropertyResult.Error($"{type.Name}.{prop.Name} is an {collectionType} but its generic type is {elementType.Name} which is not supported by Realm."); } } - else if (elementType.IsAsymmetricObjectDescendant(_references)) - { - return WeavePropertyResult.Error($"{type.Name}.{prop.Name} is an {collectionType}, but AsymmetricObjects aren't allowed to be contained in any RealmObject inheritor."); - } if (prop.SetMethod != null) { @@ -656,10 +634,6 @@ private WeavePropertyResult WeaveProperty(PropertyDefinition prop, TypeDefinitio break; } } - else if (prop.ContainsAsymmetricObject(_references)) - { - return WeavePropertyResult.Error($"{type.Name}.{prop.Name} is of type AsymmetricObject, but AsymmetricObjects aren't allowed to be the receiving end of any relationship."); - } else if (prop.ContainsRealmObject(_references) || prop.ContainsEmbeddedObject(_references)) { if (prop.SetMethod == null) @@ -683,11 +657,6 @@ private WeavePropertyResult WeaveProperty(PropertyDefinition prop, TypeDefinitio return WeavePropertyResult.Error($"{type.Name}.{prop.Name} has a setter but also has [Backlink] applied, which only supports getters."); } - if (type.IsAsymmetricObjectDescendant(_references)) - { - return WeavePropertyResult.Error($"{type.Name}.{prop.Name} has [Backlink] applied which is not allowed on AsymmetricObject."); - } - var elementType = ((GenericInstanceType)prop.PropertyType).GenericArguments.Single(); var inversePropertyName = (string)backlinkAttribute.ConstructorArguments[0].Value; var inverseProperty = elementType.Resolve().Properties.SingleOrDefault(p => p.Name == inversePropertyName); @@ -724,7 +693,7 @@ private WeavePropertyResult WeaveProperty(PropertyDefinition prop, TypeDefinitio } else { - return WeavePropertyResult.Error($"{type.Name}.{prop.Name} is a '{prop.PropertyType}' which is not yet supported. If that is supposed to be a model class, make sure it inherits from RealmObject/EmbeddedObject/AsymmetricObject."); + return WeavePropertyResult.Error($"{type.Name}.{prop.Name} is a '{prop.PropertyType}' which is not yet supported. If that is supposed to be a model class, make sure it inherits from RealmObject/EmbeddedObject."); } var preserveAttribute = new CustomAttribute(_references.PreserveAttribute_Constructor); diff --git a/Realm/Realm/Configurations/FlexibleSyncConfiguration.cs b/Realm/Realm/Configurations/FlexibleSyncConfiguration.cs deleted file mode 100644 index 1e4b610951..0000000000 --- a/Realm/Realm/Configurations/FlexibleSyncConfiguration.cs +++ /dev/null @@ -1,187 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; - -namespace Realms.Sync -{ - /// - /// A is used to setup a whose data can be synchronized - /// between devices using Atlas Device Sync. Unlike , a Realm opened with - /// will be initially empty until one or more subscriptions are added - /// via . - /// - /// Device Sync Docs - public class FlexibleSyncConfiguration : SyncConfigurationBase - { - /// - /// A delegate invoked when a flexible sync Realm is first opened. - /// - /// The realm that has just been opened. - public delegate void InitialSubscriptionsDelegate(Realm realm); - - /// - /// Initializes a new instance of the class. - /// - /// - /// A valid . - /// - /// - /// Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename. - /// - public FlexibleSyncConfiguration(User user, string? optionalPath = null) - : base(user, GetPathToRealm(optionalPath ?? user?.Handle.GetRealmPath())) - { - } - - /// - /// Gets or sets a callback that will be invoked the first time a Realm is opened. - /// - /// - /// This callback allows you to populate an initial set of subscriptions, which will - /// then be awaited when is invoked. - ///
- /// The returned by is already - /// called in an block, so there's no need - /// to start one inside the callback. - ///
- /// - /// - /// var config = new FlexibleSyncConfiguration(user) - /// { - /// PopulateInitialSubscriptions = (realm) => - /// { - /// var myNotes = realm.All<Note>().Where(n => n.AuthorId == myUserId); - /// realm.Subscriptions.Add(myNotes); - /// } - /// }; - /// - /// // The task will complete when all the user notes have been downloaded. - /// var realm = await Realm.GetInstanceAsync(config); - /// - /// - /// - /// The that will be invoked the first time - /// a Realm is opened. - /// - public InitialSubscriptionsDelegate? PopulateInitialSubscriptions { get; set; } - - internal override async Task CreateRealmAsync(CancellationToken cancellationToken) - { - var tracker = InjectInitialSubscriptions(); - - var result = await base.CreateRealmAsync(cancellationToken); - - InvokeInitialSubscriptions(tracker, result); - - if (tracker.PopulateInitialDataInvoked) - { - await result.Subscriptions.WaitForSynchronizationAsync(); - - // TODO: remove the wait once https://github.com/realm/realm-core/issues/5705 is resolved - await result.SyncSession.WaitForDownloadAsync(); - } - - return result; - } - - internal override Realm CreateRealm() - { - var tracker = InjectInitialSubscriptions(); - - var result = base.CreateRealm(); - - InvokeInitialSubscriptions(tracker, result); - - return result; - } - - private InitialDataTracker InjectInitialSubscriptions() - { - var tracker = new InitialDataTracker - { - Callback = PopulateInitialSubscriptions - }; - - if (tracker.Callback != null) - { - var oldDataCallback = PopulateInitialData; - PopulateInitialData = (realm) => - { - // We can't run the PopulateInitialSubscriptions callback here because - // the Realm is already in a write transaction, so Subscriptions.Update - // will hang. We flag that with `shouldPopulate` and update the subscriptions - // after we open the realm. - oldDataCallback?.Invoke(realm); - tracker.PopulateInitialDataInvoked = true; - }; - } - - return tracker; - } - - private static void InvokeInitialSubscriptions(InitialDataTracker tracker, Realm realm) - { - if (!tracker.PopulateInitialDataInvoked) - { - return; - } - - try - { - realm.Subscriptions.Update(() => - { - // We're not guarded by a write lock between invoking the `InitialDataCallback` - // and getting here, so it's possible someone managed to insert subscriptions - // before us. If that's the case, then don't insert anything and just wait for - // sync. - if (realm.Subscriptions.Count == 0) - { - tracker.Callback(realm); - } - }); - } - catch (Exception ex) - { - // This needs to duplicate the logic in RealmConfigurationBase.CreateRealmAsync - throw new AggregateException("Exception occurred in a Realm.PopulateInitialSubscriptions callback. See inner exception for more details.", ex); - } - } - - internal override Native.SyncConfiguration CreateNativeSyncConfiguration() - { - var config = base.CreateNativeSyncConfiguration(); - config.is_flexible_sync = true; - return config; - } - - // This is a holder class to workaround the fact that we can't use ref booleans - // inside lambda functions. Its purpose is to server as a marker that PopulateInitialData - // was invoked, which means we need to then update subscriptions. - private class InitialDataTracker - { - [MemberNotNullWhen(returnValue: true, nameof(Callback))] - public bool PopulateInitialDataInvoked { get; set; } - - public InitialSubscriptionsDelegate? Callback { get; set; } - } - } -} diff --git a/Realm/Realm/Configurations/PartitionSyncConfiguration.cs b/Realm/Realm/Configurations/PartitionSyncConfiguration.cs deleted file mode 100644 index fbf2ffd483..0000000000 --- a/Realm/Realm/Configurations/PartitionSyncConfiguration.cs +++ /dev/null @@ -1,150 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using MongoDB.Bson; -using Realms.Helpers; - -namespace Realms.Sync -{ - /// - /// A is used to setup a that can be synchronized between devices using Atlas Device Sync. - /// - /// Device Sync Docs - public class PartitionSyncConfiguration : SyncConfigurationBase - { - /// - /// Gets or sets a callback that is invoked when download progress is made when using . - /// This will only be invoked for the initial download of the Realm and will not be invoked as further download - /// progress is made during the lifetime of the Realm. It is ignored when using - /// . - /// - /// A callback that will be periodically invoked as the Realm is downloaded. - public Action? OnProgress { get; set; } - - /// - /// Gets the partition identifying the Realm this configuration is describing. - /// - /// The partition value for the Realm. - public RealmValue Partition { get; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The partition identifying the remote Realm that will be synchronized. - /// - /// - /// A valid . - /// - /// - /// Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename. - /// - public PartitionSyncConfiguration(string? partition, User user, string? optionalPath = null) - : this(user, partition, optionalPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The partition identifying the remote Realm that will be synchronized. - /// - /// - /// A valid . - /// - /// - /// Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename. - /// - public PartitionSyncConfiguration(long? partition, User user, string? optionalPath = null) - : this(user, partition, optionalPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The partition identifying the remote Realm that will be synchronized. - /// - /// - /// A valid . - /// - /// - /// Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename. - /// - public PartitionSyncConfiguration(ObjectId? partition, User user, string? optionalPath = null) - : this(user, partition, optionalPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The partition identifying the remote Realm that will be synchronized. - /// - /// - /// A valid . - /// - /// - /// Path to the realm, must be a valid full path for the current platform, relative subdirectory, or just filename. - /// - public PartitionSyncConfiguration(Guid? partition, User user, string? optionalPath = null) - : this(user, partition, optionalPath) - { - } - - private PartitionSyncConfiguration(User user, RealmValue partition, string? path) - : base(user, GetPathToRealm(path ?? user?.Handle.GetRealmPath(partition.ToNativeJson()))) - { - Partition = partition; - } - - internal override IDisposable? OnBeforeRealmOpen(AsyncOpenTaskHandle handle) - { - var onProgress = OnProgress; - if (onProgress is null) - { - return null; - } - - return new ProgressNotificationToken( - observer: (progress) => - { - onProgress(progress); - }, - register: handle.RegisterProgressNotifier, - unregister: (token) => - { - if (!handle.IsClosed) - { - handle.UnregisterProgressNotifier(token); - } - }); - } - - internal override Native.SyncConfiguration CreateNativeSyncConfiguration() - { - var config = base.CreateNativeSyncConfiguration(); - config.Partition = Partition.ToNativeJson(); - return config; - } - } -} diff --git a/Realm/Realm/Configurations/SyncConfigurationBase.cs b/Realm/Realm/Configurations/SyncConfigurationBase.cs deleted file mode 100644 index a552f114c7..0000000000 --- a/Realm/Realm/Configurations/SyncConfigurationBase.cs +++ /dev/null @@ -1,173 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Realms.Helpers; -using Realms.Native; -using Realms.Sync.ErrorHandling; -using Realms.Sync.Exceptions; - -namespace Realms.Sync -{ - /// - /// A is used to setup a that can be synchronized between devices using Atlas Device Sync. - /// There are two synchronization modes with their respective configurations - "partition" sync with allows you - /// to split your data in separate partitions and synchronize an entire partition with an entire Realm; "flexible" sync with - /// allows you to start with an empty Realm and send the server a set of queries which it will run and - /// populate the Realm with all documents matching them. - /// - /// Device Sync Docs - public abstract class SyncConfigurationBase : RealmConfigurationBase - { - private ClientResetHandlerBase _clientResetHandler = new RecoverOrDiscardUnsyncedChangesHandler(); - - /// - /// Callback triggered when an error occurs in a session. - /// - /// - /// The where the error happened on. - /// - /// - /// The specific occurred on this . - /// - public delegate void SessionErrorCallback(Session session, SessionException error); - - /// - /// Gets the used to create this . - /// - /// The whose s will be synced. - public User User { get; } - - /// - /// Gets or sets a handler that will be invoked if a client reset error occurs for this Realm. Default is , - /// that attempts to automatically recover any unsynchronized changes and, if that fails, falls back to the discarding unsynced changes. - /// - /// The that will be used to handle a client reset. - /// - /// Supported values are instances of , , - /// and . - /// The default will have no custom actions set for the before and after callbacks. - /// - /// Client reset docs - public virtual ClientResetHandlerBase ClientResetHandler - { - get => _clientResetHandler; - set => _clientResetHandler = Argument.ValidateNotNull(value, nameof(value)); - } - - /// - /// Gets or sets a callback that will be invoked whenever a occurs for the synchronized Realm. - /// - /// The that will be used to report transient session errors. - /// - /// Client reset errors will not be reported through this callback as they are handled by the set . - /// - public SessionErrorCallback? OnSessionError { get; set; } - - /// - /// Gets or sets a value indicating whether async operations, such as , - /// , or should throw an - /// error whenever a non-fatal error, such as timeout occurs. - /// - /// - /// If set to false, non-fatal session errors will be ignored and sync will continue retrying the - /// connection under in the background. This means that in cases where the device is offline, these operations - /// may take an indeterminate time to complete. - /// - /// true to throw an error if a non-fatal session error occurs, false otherwise. - public bool CancelAsyncOperationsOnNonFatalErrors { get; set; } - - /// - /// Gets or sets the key, used to encrypt the entire Realm. Once set, must be specified each time the file is used. - /// - /// Full 64byte (512bit) key for AES-256 encryption. - public new byte[]? EncryptionKey - { - get => base.EncryptionKey; - set => base.EncryptionKey = value; - } - - internal SessionStopPolicy SessionStopPolicy { get; set; } = SessionStopPolicy.AfterChangesUploaded; - - private protected SyncConfigurationBase(User user, string databasePath) - : base(databasePath) - { - Argument.NotNull(user, nameof(user)); - - User = user; - } - - internal override SharedRealmHandle CreateHandle(in Configuration configuration) - { - var syncConfiguration = CreateNativeSyncConfiguration(); - return SharedRealmHandle.OpenWithSync(configuration, syncConfiguration); - } - - internal override Task CreateHandleAsync(in Configuration configuration, CancellationToken cancellationToken) - { - var syncConfiguration = CreateNativeSyncConfiguration(); - - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - var handle = SharedRealmHandle.OpenWithSyncAsync(configuration, syncConfiguration, GCHandle.ToIntPtr(tcsHandle)); - cancellationToken.Register(() => - { - if (!handle.IsClosed) - { - handle.Cancel(); - tcs.TrySetCanceled(); - } - }); - - async Task WaitForAsyncOpenTask() - { - using var progressToken = OnBeforeRealmOpen(handle); - - try - { - using var realmReference = await tcs.Task; - return SharedRealmHandle.ResolveFromReference(realmReference); - } - finally - { - tcsHandle.Free(); - handle.Dispose(); - } - } - - return WaitForAsyncOpenTask(); - } - - internal virtual IDisposable? OnBeforeRealmOpen(AsyncOpenTaskHandle handle) => null; - - internal virtual Native.SyncConfiguration CreateNativeSyncConfiguration() - { - return new Native.SyncConfiguration - { - SyncUserHandle = User.Handle, - session_stop_policy = SessionStopPolicy, - schema_mode = _schema is null ? SchemaMode.AdditiveDiscovered : SchemaMode.AdditiveExplicit, - client_resync_mode = ClientResetHandler.ClientResetMode, - cancel_waits_on_nonfatal_error = CancelAsyncOperationsOnNonFatalErrors, - }; - } - } -} diff --git a/Realm/Realm/DatabaseTypes/Accessors/IRealmAccessor.cs b/Realm/Realm/DatabaseTypes/Accessors/IRealmAccessor.cs index ef89c9b6c6..40cfb431bb 100644 --- a/Realm/Realm/DatabaseTypes/Accessors/IRealmAccessor.cs +++ b/Realm/Realm/DatabaseTypes/Accessors/IRealmAccessor.cs @@ -65,10 +65,9 @@ public interface IRealmAccessor /// /// A collection of properties describing the underlying schema of this object. /// - /// This will always be available for models that use the Realm source generator tool (i.e. inheriting from , - /// , or ). It will be null for unmanaged objects if the models have - /// been processed by the Fody weaver (i.e. inheriting from , , or - /// ). + /// This will always be available for models that use the Realm source generator tool (i.e. inheriting from or + /// ). It will be null for unmanaged objects if the models have + /// been processed by the Fody weaver (i.e. inheriting from or ). /// ObjectSchema? ObjectSchema { get; } @@ -149,8 +148,7 @@ IQueryable GetBacklinks(string propertyName) /// /// Gets the parent of the embedded object. It can be either another - /// embedded object, a standalone realm object, - /// or an asymmetric object. + /// embedded object or a standalone realm object. /// /// The parent of the embedded object. IRealmObjectBase? GetParent(); diff --git a/Realm/Realm/DatabaseTypes/AsymmetricObject.cs b/Realm/Realm/DatabaseTypes/AsymmetricObject.cs deleted file mode 100644 index 276bde30a7..0000000000 --- a/Realm/Realm/DatabaseTypes/AsymmetricObject.cs +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms -{ - /// - /// Base for any object that can be persisted in a but that cannot be retrieved, hence cannot be modified. - /// - /// - /// The benefit of using is that the performance of each sync operation is much higher. - /// The drawback is that an is synced unidirectionally, so it cannot be queried. You should - /// use this base when you have a write-heavy use case. If, instead you want to persist an object that you can also query - /// against, use instead. RealmObjects and - /// EmbeddedObjects can't link (or backlink) to AsymmetricObjects. - /// AsymmetricObjects can only link to EmbeddedObjects. - /// - /// - public class AsymmetricObject : RealmObjectBase, IAsymmetricObject - { - } -} diff --git a/Realm/Realm/DatabaseTypes/IRealmObjectBase.cs b/Realm/Realm/DatabaseTypes/IRealmObjectBase.cs index d8f5d56183..346e1318c3 100644 --- a/Realm/Realm/DatabaseTypes/IRealmObjectBase.cs +++ b/Realm/Realm/DatabaseTypes/IRealmObjectBase.cs @@ -104,33 +104,6 @@ public interface IRealmObject : IRealmObjectBase { } - /// - /// Base interface for any asymmetric object that can be persisted in a . - /// - /// - /// The benefit of using is that the performance of each sync operation is much higher. - /// The drawback is that an is synced unidirectionally, so it cannot be queried. - /// You should use this base when you have a write-heavy use case. - /// If, instead you want to persist an object that you can also query against, use instead. - ///
- /// This interface will be implemented automatically by the Realm source generator as long as your - /// model class is declared as partial. - ///
- /// - /// - /// public partial class SensorReading : IAsymmetricObject - /// { - /// public DateTimeOffset TimeStamp { get; set; } = DateTimeOffset.UtcNow; - /// - /// public double Value { get; set; } - /// } - /// - /// - /// - public interface IAsymmetricObject : IRealmObjectBase - { - } - /// /// Base interface for any embedded object that can be persisted in a . /// @@ -152,8 +125,7 @@ public interface IEmbeddedObject : IRealmObjectBase { /// /// Gets the parent of the embedded object. It can be either another - /// embedded object, a standalone realm object, - /// or an asymmetric object. + /// embedded object or a standalone realm object. /// /// The parent object that owns this . public IRealmObjectBase? Parent { get; } diff --git a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs index 1ba7570d87..830b724764 100644 --- a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs +++ b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs @@ -265,9 +265,6 @@ protected void AddToRealmIfNecessary(in RealmValue value) case IEmbeddedObject: Debug.Assert(typeof(T) == typeof(RealmValue) || typeof(T) == typeof(KeyValuePair), $"Expected a RealmValue to contain the IEmbeddedObject, but was a {typeof(T).Name}"); throw new NotSupportedException("A RealmValue cannot contain an embedded object."); - case IAsymmetricObject: - Debug.Assert(typeof(T) == typeof(RealmValue) || typeof(T) == typeof(KeyValuePair), $"Expected a RealmValue to contain the IAsymmetricObject, but was a {typeof(T).Name}"); - throw new NotSupportedException("A RealmValue cannot contain an asymmetric object."); default: throw new NotSupportedException($"{robj.GetType().Name} is not a valid Realm object type."); } diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index f94deaefee..d2a8d987ee 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -35,7 +35,7 @@ namespace Realms /// A type that can represent any valid Realm data type. It is a valid type in and of itself, which /// means that it can be used to declare a property of type . /// Please note that a property in a managed realm object - /// cannot contain an embedded object or an asymmetric object. + /// cannot contain an embedded object. /// /// /// diff --git a/Realm/Realm/Dynamic/DynamicAsymmetricObject.cs b/Realm/Realm/Dynamic/DynamicAsymmetricObject.cs deleted file mode 100644 index 10cde3f39f..0000000000 --- a/Realm/Realm/Dynamic/DynamicAsymmetricObject.cs +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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.ComponentModel; -using System.Dynamic; -using System.Linq.Expressions; - -namespace Realms.Dynamic -{ - [EditorBrowsable(EditorBrowsableState.Never)] - [Ignored] - public class DynamicAsymmetricObject : DynamicRealmObjectBase, IAsymmetricObject, IDynamicMetaObjectProvider - { - public DynamicMetaObject GetMetaObject(Expression parameter) - { - return new MetaRealmObject(parameter, this); - } - } -} diff --git a/Realm/Realm/Dynamic/DynamicRealmObjectHelper.cs b/Realm/Realm/Dynamic/DynamicRealmObjectHelper.cs index 30790bffbe..4cdfb2ac72 100644 --- a/Realm/Realm/Dynamic/DynamicRealmObjectHelper.cs +++ b/Realm/Realm/Dynamic/DynamicRealmObjectHelper.cs @@ -27,7 +27,6 @@ internal class DynamicRealmObjectHelper : IRealmObjectHelper { private static readonly DynamicRealmObjectHelper _embeddedInstance = new(ObjectSchema.ObjectType.EmbeddedObject); private static readonly DynamicRealmObjectHelper _objectInstance = new(ObjectSchema.ObjectType.RealmObject); - private static readonly DynamicRealmObjectHelper _asymmetricInstance = new(ObjectSchema.ObjectType.AsymmetricObject); private readonly ObjectSchema.ObjectType _schemaType; @@ -36,7 +35,6 @@ internal static DynamicRealmObjectHelper Instance(ObjectSchema schema) => { ObjectSchema.ObjectType.RealmObject => _objectInstance, ObjectSchema.ObjectType.EmbeddedObject => _embeddedInstance, - ObjectSchema.ObjectType.AsymmetricObject => _asymmetricInstance, _ => throw new NotSupportedException($"{schema.BaseType} type not supported, yet."), }; @@ -55,7 +53,6 @@ public IRealmObjectBase CreateInstance() => { ObjectSchema.ObjectType.RealmObject => new DynamicRealmObject(), ObjectSchema.ObjectType.EmbeddedObject => new DynamicEmbeddedObject(), - ObjectSchema.ObjectType.AsymmetricObject => new DynamicAsymmetricObject(), _ => throw new NotSupportedException($"{_schemaType} type not supported, yet."), }; diff --git a/Realm/Realm/Dynamic/MetaRealmObject.cs b/Realm/Realm/Dynamic/MetaRealmObject.cs index c89943c483..3c9c311d08 100644 --- a/Realm/Realm/Dynamic/MetaRealmObject.cs +++ b/Realm/Realm/Dynamic/MetaRealmObject.cs @@ -160,7 +160,6 @@ public override DynamicMetaObject BindGetMember(GetMemberBinder binder) if (property.Type.UnderlyingType() == PropertyType.LinkingObjects) { - // no AsymmetricObjects involved here expression = IsTargetEmbedded(property) ? Expression.Call(RealmObjectGetBacklinksForHandle_EmbeddedObject, self, Expression.Constant(binder.Name), expression) : Expression.Call(RealmObjectGetBacklinksForHandle_RealmObject, self, Expression.Constant(binder.Name), expression); @@ -270,7 +269,6 @@ private static Type GetDynamicObjectType(ObjectSchema schema) => { ObjectSchema.ObjectType.RealmObject => typeof(DynamicRealmObject), ObjectSchema.ObjectType.EmbeddedObject => typeof(DynamicEmbeddedObject), - ObjectSchema.ObjectType.AsymmetricObject => typeof(DynamicAsymmetricObject), _ => throw new NotSupportedException($"{schema.BaseType} not supported yet."), }; @@ -285,7 +283,6 @@ private MethodInfo GetObjectGetCollectionMethod(Property property, CollectionTyp { ObjectSchema.ObjectType.RealmObject => GetCollectionGetter(collectionType), ObjectSchema.ObjectType.EmbeddedObject => GetCollectionGetter(collectionType), - ObjectSchema.ObjectType.AsymmetricObject => GetCollectionGetter(collectionType), _ => throw new NotSupportedException($"{metadata.Schema.BaseType} not supported yet."), }; diff --git a/Realm/Realm/Exceptions/CompensatingWriteException.cs b/Realm/Realm/Exceptions/CompensatingWriteException.cs deleted file mode 100644 index 984f7e64bb..0000000000 --- a/Realm/Realm/Exceptions/CompensatingWriteException.cs +++ /dev/null @@ -1,79 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Realms.Sync.Exceptions -{ - /// - /// An exception class that indicates that one more object changes have been reverted - /// by the server. - /// - /// - /// The two typical cases in which the server will revert a client write are: - /// 1. The client created an object that doesn't match any . - /// 2. The client created/updated an object it didn't have permissions to. - /// - public class CompensatingWriteException : SessionException - { - /// - /// Gets a list of the compensating writes performed by the server. - /// - /// The compensating writes performed by the server. - public IEnumerable CompensatingWrites { get; } - - internal CompensatingWriteException(string message, IEnumerable compensatingWrites) - : base(message, ErrorCode.CompensatingWrite) - { - CompensatingWrites = compensatingWrites; - } - } - - /// - /// A class containing the details for a compensating write performed by the server. - /// - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "This is closely related to CompensatingWriteException")] - public class CompensatingWriteInfo - { - /// - /// Gets the type of the object which was affected by the compensating write. - /// - /// The object type. - public string ObjectType { get; } - - /// - /// Gets the reason for the server to perform a compensating write. - /// - /// The compensating write reason. - public string Reason { get; } - - /// - /// Gets the primary key of the object which was affected by the compensating write. - /// - /// The object primary key. - public RealmValue PrimaryKey { get; } - - internal CompensatingWriteInfo(string objectName, string reason, RealmValue primaryKey) - { - ObjectType = objectName; - Reason = reason; - PrimaryKey = primaryKey; - } - } -} diff --git a/Realm/Realm/Exceptions/RealmDecryptionFailedException.cs b/Realm/Realm/Exceptions/RealmDecryptionFailedException.cs index d0c8b7f7e0..32756df083 100644 --- a/Realm/Realm/Exceptions/RealmDecryptionFailedException.cs +++ b/Realm/Realm/Exceptions/RealmDecryptionFailedException.cs @@ -16,14 +16,11 @@ // //////////////////////////////////////////////////////////////////////////// -using Realms.Sync; - namespace Realms.Exceptions { /// /// An exception, raised when file decryption is unsuccessful, most likely due to invalid /// . - /// . /// public class RealmDecryptionFailedException : RealmFileAccessErrorException { diff --git a/Realm/Realm/Exceptions/RealmException.cs b/Realm/Realm/Exceptions/RealmException.cs index 29074660d9..04c57a1679 100644 --- a/Realm/Realm/Exceptions/RealmException.cs +++ b/Realm/Realm/Exceptions/RealmException.cs @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////////////////////// using System; -using Realms.Sync.Exceptions; namespace Realms.Exceptions { @@ -105,11 +104,6 @@ internal static Exception Create(RealmExceptionCodes exceptionCode, string messa return new RealmMigrationException(message); } - if (categories.HasFlag(RealmExceptionCategories.RLM_ERR_CAT_APP_ERROR)) - { - return new AppException(message, helpLink: null, httpStatusCode: 0); - } - if (categories.HasFlag(RealmExceptionCategories.RLM_ERR_CAT_FILE_ACCESS)) { return new RealmFileAccessErrorException(message); diff --git a/Realm/Realm/Exceptions/RealmExceptionCategories.cs b/Realm/Realm/Exceptions/RealmExceptionCategories.cs index 20c170e45b..b1d6d883a1 100644 --- a/Realm/Realm/Exceptions/RealmExceptionCategories.cs +++ b/Realm/Realm/Exceptions/RealmExceptionCategories.cs @@ -28,12 +28,7 @@ internal enum RealmExceptionCategories RLM_ERR_CAT_INVALID_ARG = 0x0008, RLM_ERR_CAT_FILE_ACCESS = 0x0010, RLM_ERR_CAT_SYSTEM_ERROR = 0x0020, - RLM_ERR_CAT_APP_ERROR = 0x0040, RLM_ERR_CAT_CLIENT_ERROR = 0x0080, - RLM_ERR_CAT_JSON_ERROR = 0x0100, - RLM_ERR_CAT_SERVICE_ERROR = 0x0200, - RLM_ERR_CAT_HTTP_ERROR = 0x0400, RLM_ERR_CAT_CUSTOM_ERROR = 0x0800, - RLM_ERR_CAT_WEBSOCKET_ERROR = 0x1000, } } diff --git a/Realm/Realm/Exceptions/Sync/AppException.cs b/Realm/Realm/Exceptions/Sync/AppException.cs deleted file mode 100644 index 10a166e878..0000000000 --- a/Realm/Realm/Exceptions/Sync/AppException.cs +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Net; -using Realms.Sync.Native; - -namespace Realms.Sync.Exceptions -{ - /// - /// An exception thrown from operations interacting with a Atlas App Services app. - /// - public class AppException : Exception - { - /// - /// Gets the HTTP status code returned by the remote operation. - /// - /// The HTTP status code of the operation that failed or null if the error was not an http one. - public HttpStatusCode? StatusCode { get; } - - internal AppException(AppError appError) - : this($"{appError.ErrorCategory}: {appError.Message}", appError.LogsLink, appError.http_status_code) - { - } - - internal AppException(string message, string? helpLink, int httpStatusCode) - : base(message) - { - HelpLink = helpLink; - if (httpStatusCode != 0) - { - StatusCode = (HttpStatusCode)httpStatusCode; - } - } - } -} diff --git a/Realm/Realm/Exceptions/Sync/ClientError.cs b/Realm/Realm/Exceptions/Sync/ClientError.cs deleted file mode 100644 index 6cf93d3133..0000000000 --- a/Realm/Realm/Exceptions/Sync/ClientError.cs +++ /dev/null @@ -1,35 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Exceptions.Sync -{ - internal enum ClientError - { - /// - /// A fatal error was encountered which prevents the completion of a client reset. - /// - AutoClientResetFailed = 132, - } - - internal enum ServerRequestsAction - { - NoAction = 0, - ApplicationBug = 2, - ClientReset = 6 - } -} diff --git a/Realm/Realm/Exceptions/Sync/ClientResetException.cs b/Realm/Realm/Exceptions/Sync/ClientResetException.cs deleted file mode 100644 index 10752ff871..0000000000 --- a/Realm/Realm/Exceptions/Sync/ClientResetException.cs +++ /dev/null @@ -1,61 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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.Generic; -using System.IO; - -namespace Realms.Sync.Exceptions -{ - /// - /// An exception describing a condition where a reset of the local Realm is required. - /// - public class ClientResetException : SessionException - { - private readonly string _originalFilePath; - private readonly App _app; - - /// - /// Gets the path where the backup copy of the realm will be placed once the client reset process is complete. - /// - /// The path to the backup realm. - public string BackupFilePath { get; } - - internal ClientResetException(App app, string message, ErrorCode errorCode, IDictionary userInfo) - : base(message, errorCode) - { - // Using Path.GetFullPath to normalize path separators on Windows - _originalFilePath = Path.GetFullPath(userInfo[OriginalFilePathKey]!); - _app = app; - BackupFilePath = Path.GetFullPath(userInfo[BackupFilePathKey]!); - HelpLink = "https://www.mongodb.com/docs/realm/sdk/dotnet/sync/client-reset/"; - } - - /// - /// Initiates the client reset process. - /// - /// true if actions were run successfully, false otherwise. - /// - /// On Windows, all Realm instances for that path must be disposed before this method is called or an - /// Exception will be thrown. - /// - public bool InitiateClientReset() - { - return _app.Handle.ImmediatelyRunFileActions(_originalFilePath); - } - } -} diff --git a/Realm/Realm/Exceptions/Sync/ErrorCode.cs b/Realm/Realm/Exceptions/Sync/ErrorCode.cs deleted file mode 100644 index 066e8dc0cb..0000000000 --- a/Realm/Realm/Exceptions/Sync/ErrorCode.cs +++ /dev/null @@ -1,220 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using Realms.Sync.ErrorHandling; - -namespace Realms.Sync.Exceptions; - -/// -/// Error code enumeration, indicating the type of the session error. -/// -/// -public enum ErrorCode -{ - /// - /// Unrecognized error code. It usually indicates incompatibility between the App Services server and client SDK versions. - /// - RuntimeError = 1000, - - /// - /// The partition value specified by the user is not valid - i.e. its the wrong type or is encoded incorrectly. - /// - BadPartitionValue = 1029, - - /// - /// A fundamental invariant in the communication between the client and the server was not upheld. This typically indicates - /// a bug in the synchronization layer and should be reported at https://github.com/realm/realm-core/issues. - /// - ProtocolInvariantFailed = 1038, - - /// - /// The changeset is invalid. - /// - BadChangeset = 1015, - - /// - /// The client attempted to create a subscription which the server rejected. - /// - SubscriptionFailed = 1016, - - /// - /// The client attempted to create a subscription for a query is invalid/malformed. - /// - BadQuery = 1031, - - /// - /// A client reset has occurred. This error code will only be reported via a and only - /// in the case manual client reset handling is required - either via or when - /// ManualResetFallback is invoked on one of the automatic client reset handlers. - /// - /// - /// - ClientReset = 1032, - - /// - /// The client attempted to upload an invalid schema change - either an additive schema change - /// when developer mode is off or a destructive schema change. - /// - InvalidSchemaChange = 1036, - - /// - /// Permission to Realm has been denied. - /// - PermissionDenied = 1037, - - /// - /// The server permissions for this file have changed since the last time it was used. - /// - ServerPermissionsChanged = 1040, - - /// - /// The user for this session doesn't match the user who originally created the file. This can happen - /// if you explicitly specify the Realm file path in the configuration and you open the Realm first with - /// user A, then with user B without changing the on-disk path. - /// - UserMismatch = 1041, - - /// - /// Client attempted a write that is disallowed by permissions, or modifies an object - /// outside the current query - this will result in a . - /// - WriteNotAllowed = 1044, - - /// - /// Automatic client reset has failed. This will only be reported via - /// when an automatic client reset handler was used but it failed to perform the client reset operation - - /// typically due to a breaking schema change in the server schema or due to an exception occurring in the - /// before or after client reset callbacks. - /// - AutoClientResetFailed = 1028, - - /// - /// The wrong sync type was used to connect to the server. This means that you're using - /// to connect to an app configured for flexible sync or that you're using to connect - /// to an app configured to use partition sync. - /// - WrongSyncType = 1043, - - /// - /// Client attempted a write that is disallowed by permissions, or modifies an - /// object outside the current query, and the server undid the modification. - /// - /// - CompensatingWrite = 1033, - - /// - /// Unrecognized error code. It usually indicates incompatibility between the App Services server and client SDK versions. - /// - [Obsolete("Use RuntimeError instead.")] - Unknown = RuntimeError, - - /// - /// Other session level error has occurred. - /// - /// - /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code. - /// - [Obsolete("Use RuntimeError instead.")] - OtherSessionError = RuntimeError, - - /// - /// Path to Realm is invalid. - /// - /// - /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code. - /// - [Obsolete("Use BadPartitionValue instead")] - IllegalRealmPath = BadPartitionValue, - - /// - /// The client file identifier is invalid. - /// - /// - [Obsolete("Use ClientReset instead")] - BadClientFileIdentifier = ClientReset, - - /// - /// The server version is invalid. - /// - /// - /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code. - /// - [Obsolete("Use ProtocolInvariantFailed instead")] - BadServerVersion = ProtocolInvariantFailed, - - /// - /// The client version is invalid. - /// - /// - /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code. - /// - [Obsolete("Use ProtocolInvariantFailed instead")] - BadClientVersion = ProtocolInvariantFailed, - - /// - /// Histories have diverged and cannot be merged. - /// - /// - [Obsolete("Use ClientReset instead")] - DivergingHistories = ClientReset, - - /// - /// The client file is invalid. - /// - /// - [Obsolete("Use ClientReset instead")] - BadClientFile = ClientReset, - - /// - /// Client file has expired likely due to history compaction on the server. - /// - /// - [Obsolete("Use ClientReset instead")] - ClientFileExpired = ClientReset, - - /// - /// The server has received too many sessions from this client. This is typically a transient error - /// but can also indicate that the client has too many Realms open at the same time. - /// - [Obsolete("This error code is no longer reported")] - TooManySessions = -2, - - /// - /// The client attempted to create an object that already exists outside their view. - /// - [Obsolete("This error code is no longer reported")] - ObjectAlreadyExists = -3, - - /// - /// The client tried to synchronize before initial sync has completed. Please wait for - /// the server process to complete and try again. - /// - [Obsolete("This error code is no longer reported")] - InitialSyncNotCompleted = -4, - - /// - /// An error sent by the server when its data structures used to track client progress - /// become corrupted. - /// - /// - /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code. - /// - [Obsolete("Use ProtocolInvariantFailed instead")] - BadProgress = ProtocolInvariantFailed, -} diff --git a/Realm/Realm/Exceptions/Sync/SessionException.cs b/Realm/Realm/Exceptions/Sync/SessionException.cs deleted file mode 100644 index 540b9bab89..0000000000 --- a/Realm/Realm/Exceptions/Sync/SessionException.cs +++ /dev/null @@ -1,42 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; - -namespace Realms.Sync.Exceptions -{ - /// - /// An exception type that describes a session-level error condition. - /// - public class SessionException : Exception - { - internal const string OriginalFilePathKey = "ORIGINAL_FILE_PATH"; - internal const string BackupFilePathKey = "RECOVERY_FILE_PATH"; - - /// - /// Gets the error code that describes the session error this exception represents. - /// - /// An enum value, providing more detailed information for the cause of the error. - public ErrorCode ErrorCode { get; } - - internal SessionException(string message, ErrorCode errorCode, Exception? innerException = null) : base(message, innerException) - { - ErrorCode = errorCode; - } - } -} diff --git a/Realm/Realm/Exceptions/Sync/SubscriptionException.cs b/Realm/Realm/Exceptions/Sync/SubscriptionException.cs deleted file mode 100644 index b479d507e4..0000000000 --- a/Realm/Realm/Exceptions/Sync/SubscriptionException.cs +++ /dev/null @@ -1,32 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; - -namespace Realms.Sync.Exceptions -{ - /// - /// An exception that describes an issue with a Flexible Sync . - /// - public class SubscriptionException : Exception - { - internal SubscriptionException(string message) : base(message) - { - } - } -} diff --git a/Realm/Realm/Extensions/CollectionExtensions.cs b/Realm/Realm/Extensions/CollectionExtensions.cs index 54a7f81eee..bf3db48b0d 100644 --- a/Realm/Realm/Extensions/CollectionExtensions.cs +++ b/Realm/Realm/Extensions/CollectionExtensions.cs @@ -22,10 +22,7 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Realms.Helpers; -using Realms.Sync; namespace Realms; @@ -497,66 +494,6 @@ public static IQueryable Filter(this IDictionary dictionary, s return realmDictionary.GetFilteredValueResults(predicate, arguments); } - /// - /// Adds a query to the set of active flexible sync subscriptions. The query will be joined via an OR statement - /// with any existing queries for the same type. - /// - /// The query that will be matched on the server. - /// - /// The subscription options controlling the name and/or the type of insert that will be performed. - /// - /// - /// A parameter controlling when this method should asynchronously wait for the server to send the objects - /// matching the subscription. - /// - /// - /// An optional cancellation token to cancel waiting for synchronization with the server. Note that cancelling the - /// operation only cancels the wait itself and not the actual subscription, so the subscription will be added even - /// if the task is cancelled. To remove the subscription, you can use . - /// - /// The type of objects in the query results. - /// - /// Adding a query that already exists is a no-op. - ///
- /// This method is roughly equivalent to calling and then - /// . - ///
- /// The original query after it has been added to the subscription set. - /// - public static async Task> SubscribeAsync(this IQueryable query, - SubscriptionOptions? options = null, - WaitForSyncMode waitForSync = WaitForSyncMode.FirstTime, - CancellationToken? cancellationToken = null) - where T : IRealmObject - { - Argument.NotNull(query, nameof(query)); - - var realmResults = Argument.EnsureType>(query, $"{nameof(query)} must be a query obtained by calling Realm.All.", nameof(query)); - if (realmResults.Realm.Config is not FlexibleSyncConfiguration) - { - throw new NotSupportedException( - "SubscribeAsync can only be called on queries created in a flexible sync Realm (i.e. one open with a FlexibleSyncConfiguration)"); - } - - var subscriptions = realmResults.Realm.Subscriptions; - var existingSub = options?.Name == null ? subscriptions.Find(query) : subscriptions.Find(options.Name); - - Subscription newSub = null!; - - subscriptions.Update(() => - { - newSub = subscriptions.Add(realmResults, options); - }); - - if (ShouldWaitForSync(waitForSync, existingSub, newSub)) - { - await subscriptions.WaitForSynchronizationAsync(cancellationToken); - await realmResults.Realm.SyncSession.WaitForDownloadAsync(cancellationToken); - } - - return query; - } - [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is only used by the weaver/source generated classes and should not be exposed to users.")] @@ -569,25 +506,6 @@ public static void PopulateCollection(ICollection source, ICollection t public static void PopulateCollection(IDictionary source, IDictionary target, bool update, bool skipDefaults) => PopulateCollectionCore(source, target, update, skipDefaults, kvp => kvp.Value); - private static bool ShouldWaitForSync(WaitForSyncMode mode, Subscription? oldSub, Subscription newSub) - { - switch (mode) - { - case WaitForSyncMode.Never: - return false; - case WaitForSyncMode.FirstTime: - // For FirstTimeSync mode we want to wait for sync only if we're adding a brand new sub - // or if the sub changed object type/query. - return oldSub == null || - oldSub.ObjectType != newSub.ObjectType || - oldSub.Query != newSub.Query; - case WaitForSyncMode.Always: - return true; - default: - throw new ArgumentOutOfRangeException(nameof(mode), mode, null); - } - } - private static void PopulateCollectionCore(ICollection? source, ICollection target, bool update, bool skipDefaults, Func valueGetter) { Argument.NotNull(target, nameof(target)); diff --git a/Realm/Realm/Extensions/FrozenObjectsExtensions.cs b/Realm/Realm/Extensions/FrozenObjectsExtensions.cs index 9cfb3102ea..27155837c5 100644 --- a/Realm/Realm/Extensions/FrozenObjectsExtensions.cs +++ b/Realm/Realm/Extensions/FrozenObjectsExtensions.cs @@ -44,8 +44,8 @@ public static class FrozenObjectsExtensions /// Note: Keeping a large number of frozen objects with different versions alive can have a negative impact on the filesize /// of the Realm. In order to avoid such a situation it is possible to set . /// - /// The , , or instance that you want to create a frozen version of. - /// The type of the //. + /// The or instance that you want to create a frozen version of. + /// The type of the /. /// A new frozen instance of the passed in object or the object itself if it was already frozen. public static T Freeze(this T realmObj) where T : IRealmObjectBase diff --git a/Realm/Realm/Extensions/ReflectionExtensions.cs b/Realm/Realm/Extensions/ReflectionExtensions.cs index d0b0cce93d..999078afad 100644 --- a/Realm/Realm/Extensions/ReflectionExtensions.cs +++ b/Realm/Realm/Extensions/ReflectionExtensions.cs @@ -49,17 +49,10 @@ public static bool HasCustomAttribute(this MemberInfo member) public static bool IsEmbeddedObject(this Type type) => typeof(IEmbeddedObject).IsAssignableFrom(type); - public static bool IsAsymmetricObject(this Type type) => typeof(IAsymmetricObject).IsAssignableFrom(type); - public static bool IsRealmObject(this Type type) => typeof(IRealmObject).IsAssignableFrom(type); public static ObjectSchema.ObjectType GetRealmSchemaType(this Type type) { - if (type.IsAsymmetricObject()) - { - return ObjectSchema.ObjectType.AsymmetricObject; - } - if (type.IsEmbeddedObject()) { return ObjectSchema.ObjectType.EmbeddedObject; diff --git a/Realm/Realm/Extensions/StringExtensions.cs b/Realm/Realm/Extensions/StringExtensions.cs index b382149852..c3ea11fcdc 100644 --- a/Realm/Realm/Extensions/StringExtensions.cs +++ b/Realm/Realm/Extensions/StringExtensions.cs @@ -18,70 +18,12 @@ using System; using System.ComponentModel; -using System.Linq; -using System.Text.RegularExpressions; namespace Realms { - /// - /// A set of extensions methods over strings, useable in LINQ queries. - /// [EditorBrowsable(EditorBrowsableState.Never)] - public static class StringExtensions + internal static class StringExtensions { -#if !NETCOREAPP2_1_OR_GREATER - /// - /// Returns a value indicating whether a specified substring occurs within this string. - /// - /// The original string. - /// The string to seek. - /// One of the enumeration values that determines how this string and value are compared. - /// true if the value parameter occurs within this string, or if value is the empty string (""); otherwise, false. - /// Thrown when str or value is null. - /// Thrown when comparisonType is not a valid value. - [Obsolete("Use QueryMethods.Contains instead")] - public static bool Contains(this string str, string value, StringComparison comparisonType) - { - if (str == null) - { - throw new ArgumentNullException(nameof(str)); - } - - return str.IndexOf(value, comparisonType) >= 0; - } -#endif - - /// - /// Performs a 'like' comparison between the specified string and pattern. - /// - /// - /// ? and * are allowed where ? matches a single character and * matches zero or - /// more characters, such that ?bc* matches abcde and bbc, but does not match bcd. - /// - /// This extension method can be used in LINQ queries against the returned from - /// . If used outside of a query context, it will use a to perform - /// the comparison using the same rules. - /// - /// The string to compare against the pattern. - /// The pattern to compare against. - /// If set to true performs a case sensitive comparison. - /// true if the string matches the pattern, false otherwise. - [Obsolete("Use QueryMethods.Like instead")] - public static bool Like(this string str, string pattern, bool caseSensitive = true) - { - if (str == null) - { - return pattern == null; - } - - pattern = Regex.Escape(pattern) - .Replace("\\?", ".") - .Replace("\\*", ".*"); - - var options = caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase; - return Regex.Match(str, $"^{pattern}$", options).Success; - } - internal static IntPtr IntPtrLength(this string? str) => (IntPtr)(str?.Length ?? 0); } } diff --git a/Realm/Realm/Extensions/TestingExtensions.cs b/Realm/Realm/Extensions/TestingExtensions.cs deleted file mode 100644 index 8e9917138a..0000000000 --- a/Realm/Realm/Extensions/TestingExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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 Realms.Exceptions.Sync; -using Realms.Helpers; -using Realms.Sync.Exceptions; - -namespace Realms.Sync.Testing -{ - /// - /// A set of extension methods to be used in unit-testing scenarios. Should not be used in production. - /// - public static class TestingExtensions - { - /// - /// Simulates a session error. - /// - /// The session where the simulated error will occur. - /// Error code. - /// Error message. - /// If set to true the error will be marked as fatal. - /// - /// Use this method to test your error handling code without connecting to Atlas Device Sync. - /// Some error codes, such as will be ignored and will not be reported - /// to the callback. - /// - public static void SimulateError(this Session session, ErrorCode errorCode, string message, bool isFatal = false) - { - Argument.NotNull(session, nameof(session)); - Argument.NotNull(message, nameof(message)); - - session.ReportErrorForTesting((int)errorCode, message, isFatal, ServerRequestsAction.ApplicationBug); - } - } -} diff --git a/Realm/Realm/GlobalSuppressions.cs b/Realm/Realm/GlobalSuppressions.cs index 26ee2a8e66..0418b89096 100644 --- a/Realm/Realm/GlobalSuppressions.cs +++ b/Realm/Realm/GlobalSuppressions.cs @@ -5,7 +5,6 @@ [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicRealmObject")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicEmbeddedObject")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicRealmObjectBase")] -[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicAsymmetricObject")] [assembly: SuppressMessage("Performance", "CA1820:Test for empty strings using string length", Justification = "We're only capturing the method group.", Scope = "type", Target = "~T:Realms.RealmResultsVisitor.Methods.String")] [assembly: SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "index is the argument name for ElementAt.", Scope = "member", Target = "~M:Realms.RealmResultsVisitor.VisitMethodCall(System.Linq.Expressions.MethodCallExpression)~System.Linq.Expressions.Expression")] [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Native structs use verbose names.", Scope = "namespaceanddescendants", Target = "~N:Realms.Native")] @@ -13,9 +12,6 @@ [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.NativeCommon")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.NativeCommon")] [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Native structs use snake_case fields.", Scope = "type", Target = "~T:Realms.NativeException")] -[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Native structs use verbose names.", Scope = "namespaceanddescendants", Target = "~N:Realms.Sync.Native")] -[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Native structs use snake_case fields.", Scope = "namespaceanddescendants", Target = "~N:Realms.Sync.Native")] -[assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Private struct fields are important for correct offsets while marshalling", Scope = "namespaceanddescendants", Target = "~N:Realms.Sync.Native")] [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.SynchronizationContextScheduler")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.SynchronizationContextScheduler")] [assembly: SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "Will be a breaking change to rename.", Scope = "type", Target = "~T:Realms.Schema.Property")] @@ -34,7 +30,5 @@ [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Builder is not supposed to be extended by users", Scope = "member", Target = "~M:Realms.Schema.ObjectSchema.Builder.GetKey(Realms.Schema.Property)~System.String")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Builder is not supposed to be extended by users", Scope = "member", Target = "~M:Realms.Schema.RealmSchema.Builder.GetKey(Realms.Schema.ObjectSchema)~System.String")] [assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "In C# 9.0 we can use new() to instantiate objects and we don't need a space there", Scope = "module")] -[assembly: SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "The message handler is disposed by the http client", Scope = "type", Target = "~T:Realms.Sync.AppConfiguration")] [assembly: SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "The method creates and object, so the name is convenient.", Scope = "member", Target = "~M:Realms.RealmValue.Object(Realms.IRealmObjectBase)~Realms.RealmValue")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "The method is not supposed to be used by users", Scope = "member", Target = "~M:Realms.RealmValue.Object(Realms.IRealmObjectBase)~Realms.RealmValue")] -[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "This is a private event proxied through the public one", Scope = "member", Target = "~E:Realms.Sync.Session._propertyChanged")] diff --git a/Realm/Realm/Handles/AppHandle.EmailPassword.cs b/Realm/Realm/Handles/AppHandle.EmailPassword.cs deleted file mode 100644 index cd23cbf375..0000000000 --- a/Realm/Realm/Handles/AppHandle.EmailPassword.cs +++ /dev/null @@ -1,211 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Realms.Sync -{ - internal partial class AppHandle : StandaloneHandle - { - private static class EmailNativeMethods - { - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_register_user", CallingConvention = CallingConvention.Cdecl)] - public static extern void register_user(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string username, IntPtr username_len, - [MarshalAs(UnmanagedType.LPWStr)] string password, IntPtr password_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_confirm_user", CallingConvention = CallingConvention.Cdecl)] - public static extern void confirm_user(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string token, IntPtr token_len, - [MarshalAs(UnmanagedType.LPWStr)] string token_id, IntPtr token_id_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_resend_confirmation_email", CallingConvention = CallingConvention.Cdecl)] - public static extern void resend_confirmation_email(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string email, IntPtr email_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_retry_custom_confirmation", CallingConvention = CallingConvention.Cdecl)] - public static extern void retry_custom_comfirmation(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string email, IntPtr email_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_send_reset_password_email", CallingConvention = CallingConvention.Cdecl)] - public static extern void send_reset_password_email(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string email, IntPtr email_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_reset_password", CallingConvention = CallingConvention.Cdecl)] - public static extern void reset_password(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string password, IntPtr password_len, - [MarshalAs(UnmanagedType.LPWStr)] string token, IntPtr token_len, - [MarshalAs(UnmanagedType.LPWStr)] string token_id, IntPtr token_id_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_email_call_reset_password_function", CallingConvention = CallingConvention.Cdecl)] - public static extern void call_reset_password_function(AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string username, IntPtr username_len, - [MarshalAs(UnmanagedType.LPWStr)] string password, IntPtr password_len, - [MarshalAs(UnmanagedType.LPWStr)] string function_args, IntPtr function_args_len, - IntPtr tcs_ptr, out NativeException ex); - } - - public readonly EmailPasswordApi EmailPassword; - - public class EmailPasswordApi - { - private readonly AppHandle _appHandle; - - public EmailPasswordApi(AppHandle handle) - { - _appHandle = handle; - } - - public async Task RegisterUserAsync(string username, string password) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.register_user(_appHandle, username, (IntPtr)username.Length, password, (IntPtr)password.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task ConfirmUserAsync(string token, string tokenId) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.confirm_user(_appHandle, token, (IntPtr)token.Length, tokenId, (IntPtr)tokenId.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task ResendConfirmationEmailAsync(string email) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.resend_confirmation_email(_appHandle, email, (IntPtr)email.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task RetryCustomConfirmationAsync(string email) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.retry_custom_comfirmation(_appHandle, email, (IntPtr)email.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task SendResetPasswordEmailAsync(string username) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.send_reset_password_email(_appHandle, username, (IntPtr)username.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task ResetPasswordAsync(string password, string token, string tokenId) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.reset_password( - _appHandle, - password, (IntPtr)password.Length, - token, (IntPtr)token.Length, - tokenId, (IntPtr)tokenId.Length, - GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task CallResetPasswordFunctionAsync(string username, string password, string functionArgs) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - EmailNativeMethods.call_reset_password_function(_appHandle, - username, (IntPtr)username.Length, - password, (IntPtr)password.Length, - functionArgs, (IntPtr)functionArgs.Length, - GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - } - } -} diff --git a/Realm/Realm/Handles/AppHandle.cs b/Realm/Realm/Handles/AppHandle.cs deleted file mode 100644 index 2a4746cc3e..0000000000 --- a/Realm/Realm/Handles/AppHandle.cs +++ /dev/null @@ -1,479 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Realms.Native; -using Realms.PlatformHelpers; -using Realms.Sync.Exceptions; -using Realms.Sync.Native; - -namespace Realms.Sync -{ - internal partial class AppHandle : StandaloneHandle - { - private static readonly List _appHandles = new(); - - private static class NativeMethods - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UserCallback(IntPtr tcs_ptr, IntPtr user_ptr, AppError error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void VoidTaskCallback(IntPtr tcs_ptr, AppError error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void StringCallback(IntPtr tcs_ptr, PrimitiveValue response, AppError error); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void ApiKeysCallback(IntPtr tcs_ptr, /* UserApiKey[] */ IntPtr api_keys, IntPtr api_keys_len, AppError error); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_initialize", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr initialize( - [MarshalAs(UnmanagedType.LPWStr)] string framework, IntPtr framework_len, - [MarshalAs(UnmanagedType.LPWStr)] string framework_version, IntPtr framework_version_len, - [MarshalAs(UnmanagedType.LPWStr)] string sdk_version, IntPtr sdk_version_len, - [MarshalAs(UnmanagedType.LPWStr)] string platform_version, IntPtr platform_version_len, - [MarshalAs(UnmanagedType.LPWStr)] string device_name, IntPtr device_name_len, - [MarshalAs(UnmanagedType.LPWStr)] string device_version, IntPtr device_version_len, - [MarshalAs(UnmanagedType.LPWStr)] string bundle_id, IntPtr bundle_id_len, - UserCallback user_callback, VoidTaskCallback void_callback, StringCallback string_callback, ApiKeysCallback api_keys_callback); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_create", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr create_app(Native.AppConfiguration app_config, byte[]? encryptionKey, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy(IntPtr syncUserHandle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_sync_immediately_run_file_actions", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool immediately_run_file_actions(AppHandle app, [MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr path_len, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_sync_reconnect", CallingConvention = CallingConvention.Cdecl)] - public static extern void reconnect(AppHandle app); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_current_user", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_current_user(AppHandle app, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_logged_in_users", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_logged_in_users(AppHandle app, [Out] IntPtr[] users, IntPtr bufsize, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_switch_user", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr switch_user(AppHandle app, SyncUserHandle user, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_login_user", CallingConvention = CallingConvention.Cdecl)] - public static extern void login_user(AppHandle app, Native.Credentials credentials, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_remove_user", CallingConvention = CallingConvention.Cdecl)] - public static extern void remove_user(AppHandle app, SyncUserHandle user, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_delete_user", CallingConvention = CallingConvention.Cdecl)] - public static extern void delete_user(AppHandle app, SyncUserHandle user, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_reset_for_testing", CallingConvention = CallingConvention.Cdecl)] - public static extern void reset_for_testing(AppHandle app); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_user_for_testing", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_user_for_testing( - AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string id_buf, IntPtr id_len, - [MarshalAs(UnmanagedType.LPWStr)] string refresh_token_buf, IntPtr refresh_token_len, - [MarshalAs(UnmanagedType.LPWStr)] string access_token_buf, IntPtr access_token_len, - out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_set_fake_sync_route_for_testing", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_fake_sync_route_for_testing(AppHandle app, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_clear_cached_apps", CallingConvention = CallingConvention.Cdecl)] - public static extern void clear_cached_apps(out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_base_file_path", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_base_file_path(AppHandle app, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_base_uri", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_base_uri(AppHandle app, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_id", CallingConvention = CallingConvention.Cdecl)] - public static extern StringValue get_id(AppHandle app, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_is_same_instance", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool is_same_instance(AppHandle lhs, AppHandle rhs, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_get_default_url", CallingConvention = CallingConvention.Cdecl)] - public static extern StringValue get_default_url(out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_app_update_base_url", CallingConvention = CallingConvention.Cdecl)] - public static extern void update_base_uri(AppHandle appHandle, - [MarshalAs(UnmanagedType.LPWStr)] string url_buf, IntPtr url_len, - IntPtr tcs_ptr, - out NativeException ex); - } - - static AppHandle() - { - NativeCommon.Initialize(); - } - - public static Uri DefaultBaseUri - { - get - { - var value = NativeMethods.get_default_url(out var ex); - ex.ThrowIfNecessary(); - return new(value!); - } - } - - public static void Initialize() - { - NativeMethods.UserCallback userLogin = HandleUserCallback; - NativeMethods.VoidTaskCallback taskCallback = HandleTaskCompletion; - NativeMethods.StringCallback stringCallback = HandleStringCallback; - NativeMethods.ApiKeysCallback apiKeysCallback = HandleApiKeysCallback; - - GCHandle.Alloc(userLogin); - GCHandle.Alloc(taskCallback); - GCHandle.Alloc(stringCallback); - GCHandle.Alloc(apiKeysCallback); - - var frameworkName = InteropConfig.FrameworkName; - var frameworkVersion = Environment.Version.ToString(); - - // TODO: https://github.com/realm/realm-dotnet/issues/2218 this doesn't handle prerelease versions. - var sdkVersion = InteropConfig.SDKVersion.ToString(3); - - var platformVersion = Environment.OSVersion.Version.ToString(); - - if (!string.IsNullOrEmpty(Environment.OSVersion.ServicePack)) - { - platformVersion += $" {Environment.OSVersion.ServicePack}"; - } - - string deviceName; - string deviceVersion; - string bundleId; - try - { - deviceName = Platform.DeviceInfo.Name; - deviceVersion = Platform.DeviceInfo.Version; - bundleId = Platform.BundleId; - } - catch - { - // If we can't get the device info, don't crash the app. - deviceName = Platform.Unknown; - deviceVersion = Platform.Unknown; - bundleId = Platform.Unknown; - -#if DEBUG - throw; -#endif - } - - NativeMethods.initialize( - frameworkName, frameworkName.IntPtrLength(), - frameworkVersion, frameworkVersion.IntPtrLength(), - sdkVersion, sdkVersion.IntPtrLength(), - platformVersion, platformVersion.IntPtrLength(), - deviceName, deviceName.IntPtrLength(), - deviceVersion, deviceVersion.IntPtrLength(), - bundleId, bundleId.IntPtrLength(), - userLogin, taskCallback, stringCallback, apiKeysCallback); - } - - internal AppHandle(IntPtr handle) : base(handle) - { - EmailPassword = new EmailPasswordApi(this); - - lock (_appHandles) - { - _appHandles.RemoveAll(a => !a.IsAlive); - _appHandles.Add(new WeakReference(this)); - } - } - - public static AppHandle CreateApp(Native.AppConfiguration config, byte[]? encryptionKey) - { - var handle = NativeMethods.create_app(config, encryptionKey, out var ex); - ex.ThrowIfNecessary(); - return new AppHandle(handle); - } - - public static void ForceCloseHandles(bool clearNativeCache = false) - { - lock (_appHandles) - { - foreach (var weakHandle in _appHandles) - { - var appHandle = (AppHandle?)weakHandle.Target; - appHandle?.Close(); - } - - _appHandles.Clear(); - } - - if (clearNativeCache) - { - NativeMethods.clear_cached_apps(out var ex); - ex.ThrowIfNecessary(); - } - } - - public bool ImmediatelyRunFileActions(string path) - { - var result = NativeMethods.immediately_run_file_actions(this, path, (IntPtr)path.Length, out var ex); - ex.ThrowIfNecessary(); - - return result; - } - - public void Reconnect() - { - NativeMethods.reconnect(this); - } - - public bool TryGetCurrentUser([MaybeNullWhen(false)] out SyncUserHandle userHandle) - { - var userPtr = NativeMethods.get_current_user(this, out var ex); - ex.ThrowIfNecessary(); - - if (userPtr == IntPtr.Zero) - { - userHandle = null; - return false; - } - - userHandle = new SyncUserHandle(userPtr); - return true; - } - - public IEnumerable GetAllLoggedInUsers() - { - return MarshalHelpers.GetCollection((IntPtr[] buf, IntPtr len, out NativeException ex) => NativeMethods.get_logged_in_users(this, buf, len, out ex), bufferSize: 8) - .Select(h => new SyncUserHandle(h)); - } - - public void SwitchUser(SyncUserHandle user) - { - NativeMethods.switch_user(this, user, out var ex); - ex.ThrowIfNecessary(); - } - - public async Task LogInAsync(Native.Credentials credentials) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - NativeMethods.login_user(this, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - return await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task RemoveAsync(SyncUserHandle user) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - NativeMethods.remove_user(this, user, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task DeleteUserAsync(SyncUserHandle user) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - NativeMethods.delete_user(this, user, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public string GetBaseFilePath() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_base_file_path(this, buffer, length, out ex); - })!; - } - - public Uri GetBaseUri() - { - var uriString = MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_base_uri(this, buffer, length, out ex); - })!; - - return new Uri(uriString); - } - - public async Task UpdateBaseUriAsync(Uri? newUri) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - var url = newUri?.ToString().TrimEnd('/') ?? string.Empty; - NativeMethods.update_base_uri(this, url, (IntPtr)url.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public string GetId() - { - var value = NativeMethods.get_id(this, out var ex); - ex.ThrowIfNecessary(); - return value!; - } - - public bool IsSameInstance(AppHandle other) - { - var result = NativeMethods.is_same_instance(this, other, out var ex); - ex.ThrowIfNecessary(); - return result; - } - - protected override void Unbind() => NativeMethods.destroy(handle); - - #region Testing - public void ResetForTesting() - { - NativeMethods.reset_for_testing(this); - } - - public SyncUserHandle GetUserForTesting(string id, string refreshToken, string accessToken) - { - var result = NativeMethods.get_user_for_testing( - this, - id, (IntPtr)id.Length, - refreshToken, (IntPtr)refreshToken.Length, - accessToken, (IntPtr)accessToken.Length, - out var ex); - ex.ThrowIfNecessary(); - return new SyncUserHandle(result); - } - - public void SetFakeSyncRouteForTesting() - { - NativeMethods.set_fake_sync_route_for_testing(this, out var ex); - ex.ThrowIfNecessary(); - } - - #endregion - - [MonoPInvokeCallback(typeof(NativeMethods.UserCallback))] - private static void HandleUserCallback(IntPtr tcs_ptr, IntPtr user_ptr, AppError error) - { - var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - var tcs = (TaskCompletionSource)tcsHandle.Target!; - if (error.is_null) - { - var userHandle = new SyncUserHandle(user_ptr); - tcs.TrySetResult(userHandle); - } - else - { - tcs.TrySetException(new AppException(error)); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.VoidTaskCallback))] - private static void HandleTaskCompletion(IntPtr tcs_ptr, AppError error) - { - var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - var tcs = (TaskCompletionSource)tcsHandle.Target!; - if (error.is_null) - { - tcs.TrySetResult(); - } - else - { - tcs.TrySetException(new AppException(error)); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.StringCallback))] - private static void HandleStringCallback(IntPtr tcs_ptr, PrimitiveValue response, AppError error) - { - var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - var tcs = (TaskCompletionSource)tcsHandle.Target!; - if (error.is_null) - { - tcs.TrySetResult(response.AsString()); - } - else - { - tcs.TrySetException(new AppException(error)); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.ApiKeysCallback))] - private static void HandleApiKeysCallback(IntPtr tcs_ptr, IntPtr api_keys, IntPtr api_keys_len, AppError error) - { - var tcsHandle = GCHandle.FromIntPtr(tcs_ptr); - var tcs = (TaskCompletionSource)tcsHandle.Target!; - if (error.is_null) - { - var result = new ApiKey[api_keys_len.ToInt32()]; - for (var i = 0; i < api_keys_len.ToInt32(); i++) - { - var nativeKey = Marshal.PtrToStructure(IntPtr.Add(api_keys, i * UserApiKey.Size)); - result[i] = new ApiKey(nativeKey); - } - - tcs.TrySetResult(result); - } - else - { - tcs.TrySetException(new AppException(error)); - } - } - } -} diff --git a/Realm/Realm/Handles/AsyncOpenTaskHandle.cs b/Realm/Realm/Handles/AsyncOpenTaskHandle.cs deleted file mode 100644 index 876c9b6dcb..0000000000 --- a/Realm/Realm/Handles/AsyncOpenTaskHandle.cs +++ /dev/null @@ -1,93 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Realm Inc. -// -// 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; -using System.Collections.Concurrent; -using System.Runtime.InteropServices; - -namespace Realms -{ - internal class AsyncOpenTaskHandle : StandaloneHandle - { - private static ConcurrentDictionary _handles = new(); - - private static class NativeMethods - { - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_asyncopentask_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy(IntPtr asyncTaskHandle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_asyncopentask_cancel", CallingConvention = CallingConvention.Cdecl)] - public static extern void cancel(AsyncOpenTaskHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_asyncopentask_register_progress_notifier", CallingConvention = CallingConvention.Cdecl)] - public static extern ulong register_progress_notifier(AsyncOpenTaskHandle handle, IntPtr token_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_asyncopentask_unregister_progress_notifier", CallingConvention = CallingConvention.Cdecl)] - public static extern void unregister_progress_notifier(AsyncOpenTaskHandle handle, ulong token, out NativeException ex); - } - - public AsyncOpenTaskHandle(IntPtr handle) : base(handle) - { - _handles.TryAdd(this, true); - } - - public void Cancel() - { - NativeMethods.cancel(this, out var ex); - ex.ThrowIfNecessary(); - } - - protected override void Unbind() - { - _handles.TryRemove(this, out _); - - NativeMethods.destroy(handle); - } - - /// - /// Cancels all in-flight async open tasks. This should only be used when the domain is being torn down. - /// The case this handles is: - /// 1. GetInstanceAsync. - /// 2. Domain Reload wipes all coordinator caches. - /// 3. AsyncOpen completes, calls back into managed (because s_can_call_managed is true again). - /// 4. Undefined behavior as the state from before the domain reload is no longer valid. - /// - /// This fixes the issue reported in https://github.com/realm/realm-dotnet/issues/3344. - public static void CancelInFlightTasks() - { - var keys = _handles.Keys; - foreach (var value in keys) - { - value.Cancel(); - } - } - - public ulong RegisterProgressNotifier(GCHandle managedHandle) - { - var token = NativeMethods.register_progress_notifier(this, GCHandle.ToIntPtr(managedHandle), out var ex); - ex.ThrowIfNecessary(); - return token; - } - - public void UnregisterProgressNotifier(ulong token) - { - NativeMethods.unregister_progress_notifier(this, token, out var ex); - ex.ThrowIfNecessary(); - } - } -} diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index ae8f098cd4..6fd7601d78 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -187,7 +187,7 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value { switch (value.AsIRealmObject()) { - case IRealmObject realmObj when !realmObj.IsManaged: + case IRealmObject { IsManaged: false } realmObj: realm.Add(realmObj); break; case IEmbeddedObject embeddedObj: @@ -213,14 +213,6 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value var embeddedHandle = CreateEmbeddedObjectForProperty(propertyName, metadata); realm.ManageEmbedded(embeddedObj, embeddedHandle); return; - - // Asymmetric objects can't reach this path unless the user explicitly sets them as - // a RealmValue property on the object. - // This is because: - // * For plain asymmetric objects (not contained within a RealmValue), the weaver - // raises a compilation error since asymmetric objects can't be linked to. - case IAsymmetricObject: - throw new NotSupportedException($"Asymmetric objects cannot be linked to and cannot be contained in a RealmValue. Attempted to set {value} to {metadata.Schema.Name}.{propertyName}"); } } else if (value.Type.IsCollection()) @@ -236,8 +228,6 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value case RealmValueType.Dictionary: CollectionHelpers.PopulateCollection(realm, new DictionaryHandle(Root!, collectionPtr), value); break; - default: - break; } return; diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs deleted file mode 100644 index 060f5daf9a..0000000000 --- a/Realm/Realm/Handles/SessionHandle.cs +++ /dev/null @@ -1,472 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Realms.Exceptions; -using Realms.Exceptions.Sync; -using Realms.Logging; -using Realms.Native; -using Realms.Sync.ErrorHandling; -using Realms.Sync.Exceptions; -using Realms.Sync.Native; - -namespace Realms.Sync -{ - internal class SessionHandle : RealmHandle - { - private static class NativeMethods - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionErrorCallback(IntPtr session_handle_ptr, - SyncError error, - IntPtr managed_sync_config_handle); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionProgressCallback(IntPtr progress_token_ptr, double progressEstimate); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionWaitCallback(IntPtr task_completion_source, int error_code, PrimitiveValue message); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void SessionPropertyChangedCallback(IntPtr managed_session, NotifiableProperty property); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate IntPtr NotifyBeforeClientReset(IntPtr before_frozen, IntPtr managed_sync_config_handle); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate IntPtr NotifyAfterClientReset(IntPtr before_frozen, IntPtr after, IntPtr managed_sync_config_handle, bool did_recover); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_install_callbacks", CallingConvention = CallingConvention.Cdecl)] - public static extern void install_syncsession_callbacks(SessionErrorCallback error_callback, - SessionProgressCallback progress_callback, - SessionWaitCallback wait_callback, - SessionPropertyChangedCallback property_changed_callback, - NotifyBeforeClientReset notify_before, - NotifyAfterClientReset notify_after); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_get_user", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_user(SessionHandle session); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_get_state", CallingConvention = CallingConvention.Cdecl)] - public static extern SessionState get_state(SessionHandle session, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_get_connection_state", CallingConvention = CallingConvention.Cdecl)] - public static extern ConnectionState get_connection_state(SessionHandle session, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_get_path", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_path(SessionHandle session, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_get_raw_pointer", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_raw_pointer(SessionHandle session); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy(IntPtr handle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_register_progress_notifier", CallingConvention = CallingConvention.Cdecl)] - public static extern ulong register_progress_notifier(SessionHandle session, - IntPtr token_ptr, - ProgressDirection direction, - [MarshalAs(UnmanagedType.U1)] bool is_streaming, - out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_unregister_progress_notifier", CallingConvention = CallingConvention.Cdecl)] - public static extern void unregister_progress_notifier(SessionHandle session, ulong token, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_register_property_changed_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern SessionNotificationToken register_property_changed_callback(SessionHandle session, IntPtr managed_session_handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_unregister_property_changed_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern void unregister_property_changed_callback(IntPtr session, SessionNotificationToken token, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_wait", CallingConvention = CallingConvention.Cdecl)] - public static extern void wait(SessionHandle session, IntPtr task_completion_source, ProgressDirection direction, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_report_error_for_testing", CallingConvention = CallingConvention.Cdecl)] - public static extern void report_error_for_testing(SessionHandle session, int error_code, [MarshalAs(UnmanagedType.LPWStr)] string message, IntPtr message_len, [MarshalAs(UnmanagedType.U1)] bool is_fatal, int action); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_stop", CallingConvention = CallingConvention.Cdecl)] - public static extern void stop(SessionHandle session, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_shutdown_and_wait", CallingConvention = CallingConvention.Cdecl)] - public static extern void shutdown_and_wait(SessionHandle session, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_start", CallingConvention = CallingConvention.Cdecl)] - public static extern void start(SessionHandle session, out NativeException ex); - } - - private SessionNotificationToken? _notificationToken; - - public override bool ForceRootOwnership => true; - - [Preserve] - public SessionHandle(SharedRealmHandle? root, IntPtr handle) : base(root, handle) - { - } - - public static void Initialize() - { - NativeMethods.SessionErrorCallback error = HandleSessionError; - NativeMethods.SessionProgressCallback progress = HandleSessionProgress; - NativeMethods.SessionWaitCallback wait = HandleSessionWaitCallback; - NativeMethods.SessionPropertyChangedCallback propertyChanged = HandleSessionPropertyChangedCallback; - NativeMethods.NotifyBeforeClientReset beforeReset = NotifyBeforeClientReset; - NativeMethods.NotifyAfterClientReset afterReset = NotifyAfterClientReset; - - GCHandle.Alloc(error); - GCHandle.Alloc(progress); - GCHandle.Alloc(wait); - GCHandle.Alloc(propertyChanged); - GCHandle.Alloc(beforeReset); - GCHandle.Alloc(afterReset); - - NativeMethods.install_syncsession_callbacks(error, progress, wait, propertyChanged, beforeReset, afterReset); - } - - public SyncUserHandle GetUser() - { - var ptr = NativeMethods.get_user(this); - if (ptr == IntPtr.Zero) - { - throw new RealmException("Unable to obtain user for session. This likely means the session is being torn down."); - } - - return new(ptr); - } - - public SessionState GetState() - { - var state = NativeMethods.get_state(this, out var ex); - ex.ThrowIfNecessary(); - return state; - } - - public ConnectionState GetConnectionState() - { - var connectionState = NativeMethods.get_connection_state(this, out var ex); - ex.ThrowIfNecessary(); - return connectionState; - } - - public string GetPath() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_path(this, buffer, length, out ex); - })!; - } - - public ulong RegisterProgressNotifier(GCHandle managedHandle, ProgressDirection direction, ProgressMode mode) - { - var isStreaming = mode == ProgressMode.ReportIndefinitely; - var token = NativeMethods.register_progress_notifier(this, GCHandle.ToIntPtr(managedHandle), direction, isStreaming, out var ex); - ex.ThrowIfNecessary(); - return token; - } - - public void UnregisterProgressNotifier(ulong token) - { - NativeMethods.unregister_progress_notifier(this, token, out var ex); - ex.ThrowIfNecessary(); - } - - public void SubscribeNotifications(Session session) - { - Debug.Assert(!_notificationToken.HasValue, $"{nameof(_notificationToken)} must be null before subscribing."); - - var managedSessionHandle = GCHandle.Alloc(session, GCHandleType.Weak); - var sessionPointer = GCHandle.ToIntPtr(managedSessionHandle); - _notificationToken = NativeMethods.register_property_changed_callback(this, sessionPointer, out var ex); - ex.ThrowIfNecessary(); - } - - public void UnsubscribeNotifications() - { - if (_notificationToken.HasValue) - { - // This needs to use the handle directly because it's being called in Unbind. At this point the SafeHandle is closed, which means we'll - // get an error if we attempted to marshal it to native. The raw pointer is fine though and we can use it. - NativeMethods.unregister_property_changed_callback(handle, _notificationToken.Value, out var ex); - _notificationToken = null; - ex.ThrowIfNecessary(); - } - } - - public Task WaitAsync(ProgressDirection direction, CancellationToken? cancellationToken) - { - var tcs = new TaskCompletionSource(); - if (cancellationToken?.IsCancellationRequested == true) - { - tcs.TrySetCanceled(cancellationToken.Value); - return tcs.Task; - } - - // The tcsHandles is freed in HandleSessionWaitCallback. It's important that we don't free it on cancellation - // as the cancellation doesn't really cancel the native wait operation. That will eventually complete and it needs - // to have the GCHandle at this point, otherwise we'll get a hard crash on Mono. - var tcsHandle = GCHandle.Alloc(tcs); - - cancellationToken?.Register(() => tcs.TrySetCanceled(cancellationToken.Value)); - - try - { - NativeMethods.wait(this, GCHandle.ToIntPtr(tcsHandle), direction, out var ex); - ex.ThrowIfNecessary(); - } - catch - { - // If we failed to register a waiter, we can free the handle as we won't get a native callback here anyway - tcsHandle.Free(); - throw; - } - - return tcs.Task; - } - - public IntPtr GetRawPointer() - { - return NativeMethods.get_raw_pointer(this); - } - - public void ReportErrorForTesting(int errorCode, string errorMessage, bool isFatal, ServerRequestsAction action) - { - NativeMethods.report_error_for_testing(this, errorCode, errorMessage, (IntPtr)errorMessage.Length, isFatal, (int)action); - } - - public void Stop() - { - NativeMethods.stop(this, out var ex); - ex.ThrowIfNecessary(); - } - - public void Start() - { - NativeMethods.start(this, out var ex); - ex.ThrowIfNecessary(); - } - - /// - /// Terminates the sync session and releases the Realm file it was using. - /// - public void ShutdownAndWait() - { - NativeMethods.shutdown_and_wait(this, out var ex); - ex.ThrowIfNecessary(); - } - - public override void Unbind() - { - UnsubscribeNotifications(); - NativeMethods.destroy(handle); - } - - [MonoPInvokeCallback(typeof(NativeMethods.SessionErrorCallback))] - private static void HandleSessionError(IntPtr sessionHandlePtr, SyncError error, IntPtr managedSyncConfigurationBaseHandle) - { - try - { - // Filter out end of input, which the client seems to have started reporting - if (error.error_code == (ErrorCode)1) - { - return; - } - - using var handle = new SessionHandle(null, sessionHandlePtr); - var session = new Session(handle); - string messageString = error.message!; - var syncConfigHandle = GCHandle.FromIntPtr(managedSyncConfigurationBaseHandle); - var syncConfig = (SyncConfigurationBase)syncConfigHandle.Target!; - - if (error.is_client_reset) - { - var userInfo = error.user_info_pairs.ToEnumerable().ToDictionary(kvp => (string)kvp.Key!, kvp => (string?)kvp.Value); - var clientResetEx = new ClientResetException(session.User.App, messageString, error.error_code, userInfo); - - syncConfig.ClientResetHandler.ManualClientReset?.Invoke(clientResetEx); - return; - } - - SessionException exception; - if (error.error_code == ErrorCode.CompensatingWrite) - { - var compensatingWrites = error.compensating_writes - .ToEnumerable() - .Select(c => new CompensatingWriteInfo(c.object_name!, c.reason!, new RealmValue(c.primary_key))) - .ToArray(); - exception = new CompensatingWriteException(messageString, compensatingWrites); - } - else - { - exception = new SessionException(messageString, error.error_code); - } - - exception.HelpLink = error.log_url; - syncConfig.OnSessionError?.Invoke(session, exception); - } - catch (Exception ex) - { - RealmLogger.Default.Log(LogLevel.Warn, $"An error has occurred while handling a session error: {ex}"); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.NotifyBeforeClientReset))] - private static IntPtr NotifyBeforeClientReset(IntPtr beforeFrozen, IntPtr managedSyncConfigurationHandle) - { - SyncConfigurationBase? syncConfig = null; - - try - { - var syncConfigHandle = GCHandle.FromIntPtr(managedSyncConfigurationHandle); - syncConfig = (SyncConfigurationBase)syncConfigHandle.Target!; - - var cb = syncConfig.ClientResetHandler switch - { - DiscardUnsyncedChangesHandler handler => handler.OnBeforeReset, - RecoverUnsyncedChangesHandler handler => handler.OnBeforeReset, - RecoverOrDiscardUnsyncedChangesHandler handler => handler.OnBeforeReset, - _ => throw new NotSupportedException($"ClientResetHandlerBase of type {syncConfig.ClientResetHandler.GetType()} is not handled yet") - }; - - if (cb != null) - { - var schema = syncConfig.Schema; - using var realmBefore = new Realm(new UnownedRealmHandle(beforeFrozen), syncConfig, schema); - cb.Invoke(realmBefore); - } - - return IntPtr.Zero; - } - catch (Exception ex) - { - var handlerType = syncConfig is null ? "ClientResetHandler" : syncConfig.ClientResetHandler.GetType().Name; - RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnBeforeReset during a client reset: {ex}"); - - var exHandle = GCHandle.Alloc(ex); - return GCHandle.ToIntPtr(exHandle); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.NotifyAfterClientReset))] - private static IntPtr NotifyAfterClientReset(IntPtr beforeFrozen, IntPtr after, IntPtr managedSyncConfigurationHandle, bool didRecover) - { - SyncConfigurationBase? syncConfig = null; - - try - { - var syncConfigHandle = GCHandle.FromIntPtr(managedSyncConfigurationHandle); - syncConfig = (SyncConfigurationBase)syncConfigHandle.Target!; - - var cb = syncConfig.ClientResetHandler switch - { - DiscardUnsyncedChangesHandler handler => handler.OnAfterReset, - RecoverUnsyncedChangesHandler handler => handler.OnAfterReset, - RecoverOrDiscardUnsyncedChangesHandler handler => didRecover ? handler.OnAfterRecovery : handler.OnAfterDiscard, - _ => throw new NotSupportedException($"ClientResetHandlerBase of type {syncConfig.ClientResetHandler.GetType()} is not handled yet") - }; - - if (cb != null) - { - var schema = syncConfig.Schema; - using var realmBefore = new Realm(new UnownedRealmHandle(beforeFrozen), syncConfig, schema); - using var realmAfter = new Realm(new UnownedRealmHandle(after), syncConfig, schema); - cb.Invoke(realmBefore, realmAfter); - } - - return IntPtr.Zero; - } - catch (Exception ex) - { - var handlerType = syncConfig is null ? "ClientResetHandler" : syncConfig.ClientResetHandler.GetType().Name; - RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while executing {handlerType}.OnAfterReset during a client reset: {ex}"); - - var exHandle = GCHandle.Alloc(ex); - return GCHandle.ToIntPtr(exHandle); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.SessionProgressCallback))] - private static void HandleSessionProgress(IntPtr tokenPtr, double progressEstimate) - { - var token = (ProgressNotificationToken?)GCHandle.FromIntPtr(tokenPtr).Target; - token?.Notify(progressEstimate); - } - - [MonoPInvokeCallback(typeof(NativeMethods.SessionWaitCallback))] - private static void HandleSessionWaitCallback(IntPtr taskCompletionSource, int error_code, PrimitiveValue message) - { - var handle = GCHandle.FromIntPtr(taskCompletionSource); - var tcs = (TaskCompletionSource)handle.Target!; - - if (error_code == 0) - { - tcs.TrySetResult(); - } - else - { - var inner = new SessionException(message.AsString(), (ErrorCode)error_code); - const string OuterMessage = "A system error occurred while waiting for completion. See InnerException for more details"; - tcs.TrySetException(new RealmException(OuterMessage, inner)); - } - - handle.Free(); - } - - [MonoPInvokeCallback(typeof(NativeMethods.SessionPropertyChangedCallback))] - private static void HandleSessionPropertyChangedCallback(IntPtr managedSessionHandle, NotifiableProperty property) - { - try - { - if (managedSessionHandle == IntPtr.Zero) - { - return; - } - - var propertyName = property switch - { - NotifiableProperty.ConnectionState => nameof(Session.ConnectionState), - _ => throw new NotSupportedException($"Unexpected notifiable property value: {property}") - }; - var session = (Session?)GCHandle.FromIntPtr(managedSessionHandle).Target; - if (session is null) - { - // We're taking a weak handle to the session, so it's possible that it's been collected - return; - } - - ThreadPool.QueueUserWorkItem(_ => - { - session.RaisePropertyChanged(propertyName); - }); - } - catch (Exception ex) - { - RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while raising a property changed event: {ex}"); - } - } - - private enum NotifiableProperty : byte - { - ConnectionState = 0 - } - } -} diff --git a/Realm/Realm/Handles/SharedRealmHandle.cs b/Realm/Realm/Handles/SharedRealmHandle.cs index 17962d92b2..4bf2582a6c 100644 --- a/Realm/Realm/Handles/SharedRealmHandle.cs +++ b/Realm/Realm/Handles/SharedRealmHandle.cs @@ -27,7 +27,6 @@ using Realms.Logging; using Realms.Native; using Realms.Schema; -using Realms.Sync; namespace Realms { @@ -95,15 +94,6 @@ public struct CategoryNamesContainer [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_open", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr open(Configuration configuration, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_open_with_sync", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr open_with_sync(Configuration configuration, Sync.Native.SyncConfiguration sync_configuration, - out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_open_with_sync_async", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr open_with_sync_async(Configuration configuration, Sync.Native.SyncConfiguration sync_configuration, - IntPtr task_completion_source, - out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_set_managed_state_handle", CallingConvention = CallingConvention.Cdecl)] public static extern void set_managed_state_handle(SharedRealmHandle sharedRealm, IntPtr managedStateHandle, out NativeException ex); @@ -169,7 +159,7 @@ public static extern IntPtr open_with_sync_async(Configuration configuration, Sy public static extern IntPtr resolve_realm_reference(ThreadSafeReferenceHandle referenceHandle, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_write_copy", CallingConvention = CallingConvention.Cdecl)] - public static extern void write_copy(SharedRealmHandle sharedRealm, Configuration configuration, NativeBool useSync, out NativeException ex); + public static extern void write_copy(SharedRealmHandle sharedRealm, Configuration configuration, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_create_object", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_object(SharedRealmHandle sharedRealm, UInt32 table_key, out NativeException ex); @@ -223,15 +213,6 @@ public static extern void rename_property(SharedRealmHandle sharedRealm, [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_remove_all", CallingConvention = CallingConvention.Cdecl)] public static extern bool remove_all(SharedRealmHandle sharedRealm, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_sync_session", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_session(SharedRealmHandle realm, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_subscriptions", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_subscriptions(SharedRealmHandle realm, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_subscriptions_version", CallingConvention = CallingConvention.Cdecl)] - public static extern Int64 get_subscriptions_version(SharedRealmHandle realm, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_refresh_async", CallingConvention = CallingConvention.Cdecl)] public static extern bool refresh_async(SharedRealmHandle realm, IntPtr tcs_handle, out NativeException ex); @@ -423,21 +404,6 @@ public static SharedRealmHandle Open(Configuration configuration) return new SharedRealmHandle(result); } - public static SharedRealmHandle OpenWithSync(Configuration configuration, Sync.Native.SyncConfiguration syncConfiguration) - { - var result = NativeMethods.open_with_sync(configuration, syncConfiguration, out var nativeException); - nativeException.ThrowIfNecessary(); - - return new SharedRealmHandle(result); - } - - public static AsyncOpenTaskHandle OpenWithSyncAsync(Configuration configuration, Sync.Native.SyncConfiguration syncConfiguration, IntPtr tcsHandle) - { - var asyncTaskPtr = NativeMethods.open_with_sync_async(configuration, syncConfiguration, tcsHandle, out var nativeException); - nativeException.ThrowIfNecessary(); - return new AsyncOpenTaskHandle(asyncTaskPtr); - } - public static SharedRealmHandle ResolveFromReference(ThreadSafeReferenceHandle referenceHandle) { var result = NativeMethods.resolve_realm_reference(referenceHandle, out var nativeException); @@ -622,12 +588,10 @@ public IntPtr ResolveReference(ThreadSafeReference reference) public void WriteCopy(RealmConfigurationBase config) { - var useSync = config is SyncConfigurationBase; - using var arena = new Arena(); var nativeConfig = config.CreateNativeConfiguration(arena); - NativeMethods.write_copy(this, nativeConfig, useSync, out var nativeException); + NativeMethods.write_copy(this, nativeConfig, out var nativeException); nativeException.ThrowIfNecessary(); } @@ -750,27 +714,6 @@ public ResultsHandle CreateResults(TableKey tableKey) return new ResultsHandle(this, result); } - public SessionHandle GetSession() - { - var ptr = NativeMethods.get_session(this, out var ex); - ex.ThrowIfNecessary(); - return new SessionHandle(this, ptr); - } - - public SubscriptionSetHandle GetSubscriptions() - { - var ptr = NativeMethods.get_subscriptions(this, out var ex); - ex.ThrowIfNecessary(); - return new SubscriptionSetHandle(this, ptr); - } - - public long GetSubscriptionsVersion() - { - var result = NativeMethods.get_subscriptions_version(this, out var ex); - ex.ThrowIfNecessary(); - return result; - } - public async Task RefreshAsync() { var tcs = new TaskCompletionSource(); diff --git a/Realm/Realm/Handles/SubscriptionSetHandle.cs b/Realm/Realm/Handles/SubscriptionSetHandle.cs deleted file mode 100644 index f44aa5d5a7..0000000000 --- a/Realm/Realm/Handles/SubscriptionSetHandle.cs +++ /dev/null @@ -1,357 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using Realms.Native; -using Realms.Sync.Exceptions; - -namespace Realms.Sync -{ - internal class SubscriptionSetHandle : RealmHandle - { -#pragma warning disable IDE0049 // Use built-in type alias -#pragma warning disable SA1121 // Use built-in type alias - - private static class NativeMethods - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void StateWaitCallback(IntPtr task_completion_source, SubscriptionSetState new_state, StringValue message, ErrorCode error_code); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GetSubscriptionCallback(IntPtr managed_callback, Native.Subscription subscription); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_install_callbacks", CallingConvention = CallingConvention.Cdecl)] - public static extern void install_callbacks( - GetSubscriptionCallback get_subscription_callback, - StateWaitCallback state_wait_callback); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_get_count", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_count(SubscriptionSetHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_get_version", CallingConvention = CallingConvention.Cdecl)] - public static extern Int64 get_version(SubscriptionSetHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_get_state", CallingConvention = CallingConvention.Cdecl)] - public static extern SubscriptionSetState get_state(SubscriptionSetHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_get_at_index", CallingConvention = CallingConvention.Cdecl)] - public static extern void get_at_index(SubscriptionSetHandle handle, IntPtr index, IntPtr callback, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_find_by_name", CallingConvention = CallingConvention.Cdecl)] - public static extern void find_by_name(SubscriptionSetHandle handle, [MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr name_len, IntPtr callback, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_find_by_query", CallingConvention = CallingConvention.Cdecl)] - public static extern void find_by_query(SubscriptionSetHandle handle, ResultsHandle results, IntPtr callback, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_add_results", CallingConvention = CallingConvention.Cdecl)] - public static extern void add(SubscriptionSetHandle handle, ResultsHandle results, - [MarshalAs(UnmanagedType.LPWStr)] string? name, IntPtr name_len, - [MarshalAs(UnmanagedType.I1)] bool update_existing, IntPtr callback, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_remove", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - public static extern bool remove(SubscriptionSetHandle handle, [MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr name_len, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_remove_by_id", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - public static extern bool remove(SubscriptionSetHandle handle, PrimitiveValue id, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_remove_by_query", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr remove(SubscriptionSetHandle handle, ResultsHandle results, [MarshalAs(UnmanagedType.I1)] bool remove_named, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_remove_by_type", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr remove_by_type(SubscriptionSetHandle handle, [MarshalAs(UnmanagedType.LPWStr)] string type, IntPtr type_len, [MarshalAs(UnmanagedType.I1)] bool remove_named, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_remove_all", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr remove_all(SubscriptionSetHandle handle, [MarshalAs(UnmanagedType.I1)] bool remove_named, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy(IntPtr handle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_destroy_mutable", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy_mutable(IntPtr handle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_wait_for_state", CallingConvention = CallingConvention.Cdecl)] - public static extern void wait_for_state(SubscriptionSetHandle handle, IntPtr task_completion_source, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_begin_write", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr begin_write(SubscriptionSetHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_commit_write", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr commit_write(SubscriptionSetHandle handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_subscriptionset_get_error", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_error_message(SubscriptionSetHandle handle, IntPtr buffer, IntPtr buffer_length, [MarshalAs(UnmanagedType.U1)] out bool isNull, out NativeException ex); - } - -#pragma warning restore IDE0049 // Use built-in type alias -#pragma warning restore SA1121 // Use built-in type alias - - private delegate void GetSubscriptionBase(IntPtr callback, out NativeException ex); - - public override bool ForceRootOwnership => true; - - public static void Initialize() - { - NativeMethods.GetSubscriptionCallback getSubscription = OnGetSubscription; - NativeMethods.StateWaitCallback waitState = HandleStateWaitCallback; - - GCHandle.Alloc(getSubscription); - GCHandle.Alloc(waitState); - - NativeMethods.install_callbacks(getSubscription, waitState); - } - - public bool IsReadonly { get; } - - public SubscriptionSetHandle(SharedRealmHandle root, IntPtr handle, bool isReadonly = true) : base(root, handle) - { - IsReadonly = isReadonly; - } - - public int GetCount() - { - EnsureIsOpen(); - - var result = NativeMethods.get_count(this, out var ex); - ex.ThrowIfNecessary(); - return (int)result; - } - - public SubscriptionSetState GetState() - { - EnsureIsOpen(); - - var state = NativeMethods.get_state(this, out var ex); - ex.ThrowIfNecessary(); - return state; - } - - public long GetVersion() - { - EnsureIsOpen(); - - var result = NativeMethods.get_version(this, out var ex); - ex.ThrowIfNecessary(); - return result; - } - - public string? GetErrorMessage() - { - EnsureIsOpen(); - - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - NativeMethods.get_error_message(this, buffer, length, out isNull, out ex)); - } - - public SubscriptionSetHandle BeginWrite() - { - EnsureIsOpen(); - - var result = NativeMethods.begin_write(this, out var ex); - ex.ThrowIfNecessary(); - return new SubscriptionSetHandle(Root!, result, isReadonly: false); - } - - public SubscriptionSetHandle CommitWrite() - { - EnsureIsOpen(); - - var result = NativeMethods.commit_write(this, out var ex); - ex.ThrowIfNecessary(); - - return new SubscriptionSetHandle(Root!, result, isReadonly: true); - } - - public Subscription GetAtIndex(int index) - { - EnsureIsOpen(); - - return GetSubscriptionCore((IntPtr callback, out NativeException ex) => NativeMethods.get_at_index(this, (IntPtr)index, callback, out ex))!; - } - - public Subscription? Find(string name) - { - EnsureIsOpen(); - - return GetSubscriptionCore((IntPtr callback, out NativeException ex) => NativeMethods.find_by_name(this, name, name.IntPtrLength(), callback, out ex)); - } - - public Subscription? Find(ResultsHandle results) - { - EnsureIsOpen(); - - return GetSubscriptionCore((IntPtr callback, out NativeException ex) => NativeMethods.find_by_query(this, results, callback, out ex)); - } - - public Subscription Add(ResultsHandle results, SubscriptionOptions options) - { - EnsureIsOpen(); - - return GetSubscriptionCore((IntPtr callback, out NativeException ex) => NativeMethods.add(this, results, options.Name, options.Name.IntPtrLength(), options.UpdateExisting, callback, out ex))!; - } - - public bool Remove(string name) - { - EnsureIsOpen(); - - var result = NativeMethods.remove(this, name, name.IntPtrLength(), out var ex); - ex.ThrowIfNecessary(); - return result; - } - - public bool Remove(ObjectId id) - { - EnsureIsOpen(); - - var subId = PrimitiveValue.ObjectId(id); - var result = NativeMethods.remove(this, subId, out var ex); - ex.ThrowIfNecessary(); - return result; - } - - public int Remove(ResultsHandle results, bool removeNamed) - { - EnsureIsOpen(); - - var result = NativeMethods.remove(this, results, removeNamed, out var ex); - ex.ThrowIfNecessary(); - return (int)result; - } - - public int RemoveAll(string type, bool removeNamed) - { - EnsureIsOpen(); - - var result = NativeMethods.remove_by_type(this, type, type.IntPtrLength(), removeNamed, out var ex); - ex.ThrowIfNecessary(); - return (int)result; - } - - public int RemoveAll(bool removeNamed) - { - EnsureIsOpen(); - - var result = NativeMethods.remove_all(this, removeNamed, out var ex); - ex.ThrowIfNecessary(); - return (int)result; - } - - public async Task WaitForStateChangeAsync(CancellationToken? cancellationToken) - { - EnsureIsOpen(); - - var tcs = new TaskCompletionSource(); - if (cancellationToken?.IsCancellationRequested == true) - { - tcs.TrySetCanceled(cancellationToken.Value); - return await tcs.Task; - } - - // The tcsHandles is freed in HandleStateWaitCallback. It's important that we don't free it on cancellation - // as the cancellation doesn't really cancel the native wait operation. That will eventually complete and it needs - // to have the GCHandle at this point, otherwise we'll get a hard crash on Mono. - var tcsHandle = GCHandle.Alloc(tcs); - cancellationToken?.Register(() => tcs.TrySetCanceled(cancellationToken.Value)); - - try - { - NativeMethods.wait_for_state(this, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - } - catch - { - // If we failed to register a waiter, we can free the handle as we won't get a native callback here anyway - tcsHandle.Free(); - throw; - } - - return await tcs.Task; - } - - private static Subscription? GetSubscriptionCore(GetSubscriptionBase getter) - { - Subscription? result = null; - Action callback = sub => result = sub.ManagedSubscription; - var callbackHandle = GCHandle.Alloc(callback); - try - { - getter(GCHandle.ToIntPtr(callbackHandle), out var ex); - ex.ThrowIfNecessary(); - } - finally - { - callbackHandle.Free(); - } - - return result; - } - - public override void Unbind() - { - if (IsReadonly) - { - NativeMethods.destroy(handle); - } - else - { - NativeMethods.destroy_mutable(handle); - } - - handle = IntPtr.Zero; - } - - [MonoPInvokeCallback(typeof(NativeMethods.GetSubscriptionCallback))] - private static void OnGetSubscription(IntPtr managedCallbackPtr, Native.Subscription subscription) - { - var handle = GCHandle.FromIntPtr(managedCallbackPtr); - var callback = (Action)handle.Target!; - callback(subscription); - } - - [MonoPInvokeCallback(typeof(NativeMethods.StateWaitCallback))] - private static void HandleStateWaitCallback(IntPtr taskCompletionSource, SubscriptionSetState state, StringValue message, ErrorCode error_code) - { - var handle = GCHandle.FromIntPtr(taskCompletionSource); - var tcs = (TaskCompletionSource)handle.Target!; - - switch (error_code) - { - case 0: // No error - tcs.TrySetResult(state); - break; - case (ErrorCode)1027: // OperationAborted - tcs.TrySetException(new TaskCanceledException("The SubscriptionSet was closed before the wait could complete. This is likely because the Realm it belongs to was disposed.")); - break; - case ErrorCode.BadQuery: - case ErrorCode.SubscriptionFailed: - tcs.TrySetException(new SubscriptionException(message!)); - break; - default: - tcs.TrySetException(new SessionException((string?)message ?? "Unknown error", error_code)); - break; - } - - handle.Free(); - } - } -} diff --git a/Realm/Realm/Handles/SyncUserHandle.cs b/Realm/Realm/Handles/SyncUserHandle.cs deleted file mode 100644 index fc385365d1..0000000000 --- a/Realm/Realm/Handles/SyncUserHandle.cs +++ /dev/null @@ -1,470 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using MongoDB.Bson; -using Realms.Logging; -using Realms.Native; - -namespace Realms.Sync -{ - internal class SyncUserHandle : StandaloneHandle - { - private static class NativeMethods - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void UserChangedCallback(IntPtr managed_user); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_id", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_user_id(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_refresh_token", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_refresh_token(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_access_token", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_access_token(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_device_id", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_device_id(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_state", CallingConvention = CallingConvention.Cdecl)] - public static extern UserState get_state(SyncUserHandle user, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_profile_data", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_profile_data(SyncUserHandle user, UserProfileField field, - IntPtr buffer, IntPtr buffer_length, [MarshalAs(UnmanagedType.U1)] out bool isNull, - out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_custom_data", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_custom_data(SyncUserHandle user, IntPtr buffer, IntPtr buffer_length, - [MarshalAs(UnmanagedType.U1)] out bool isNull, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_app", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_app(SyncUserHandle user, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_log_out", CallingConvention = CallingConvention.Cdecl)] - public static extern void log_out(SyncUserHandle user, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_refresh_custom_data", CallingConvention = CallingConvention.Cdecl)] - public static extern void refresh_custom_data(SyncUserHandle user, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_destroy", CallingConvention = CallingConvention.Cdecl)] - public static extern void destroy(IntPtr syncuserHandle); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_call_function", CallingConvention = CallingConvention.Cdecl)] - public static extern void call_function(SyncUserHandle handle, AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string function_name, IntPtr function_name_len, - [MarshalAs(UnmanagedType.LPWStr)] string args, IntPtr args_len, - [MarshalAs(UnmanagedType.LPWStr)] string? service_name, IntPtr service_name_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_link_credentials", CallingConvention = CallingConvention.Cdecl)] - public static extern void link_credentials(SyncUserHandle handle, AppHandle app, Native.Credentials credentials, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_serialized_identities", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_identities(SyncUserHandle handle, IntPtr buffer, IntPtr bufsize, out NativeException ex); - - #region Api Keys - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_create", CallingConvention = CallingConvention.Cdecl)] - public static extern void create_api_key(SyncUserHandle handle, AppHandle app, - [MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr name_len, - IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_fetch", CallingConvention = CallingConvention.Cdecl)] - public static extern void fetch_api_key(SyncUserHandle handle, AppHandle app, PrimitiveValue id, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_fetch_all", CallingConvention = CallingConvention.Cdecl)] - public static extern void fetch_api_keys(SyncUserHandle handle, AppHandle app, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_delete", CallingConvention = CallingConvention.Cdecl)] - public static extern void delete_api_key(SyncUserHandle handle, AppHandle app, PrimitiveValue id, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_disable", CallingConvention = CallingConvention.Cdecl)] - public static extern void disable_api_key(SyncUserHandle handle, AppHandle app, PrimitiveValue id, IntPtr tcs_ptr, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_api_key_enable", CallingConvention = CallingConvention.Cdecl)] - public static extern void enable_api_key(SyncUserHandle handle, AppHandle app, PrimitiveValue id, IntPtr tcs_ptr, out NativeException ex); - - #endregion - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_path_for_realm", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_path_for_realm(SyncUserHandle handle, [MarshalAs(UnmanagedType.LPWStr)] string? partition, IntPtr partition_len, IntPtr buffer, IntPtr bufsize, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_register_changed_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr register_changed_callback(SyncUserHandle user, IntPtr managed_user_handle, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_unregister_property_changed_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern void unregister_changed_callback(IntPtr user, IntPtr token, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_install_callbacks", CallingConvention = CallingConvention.Cdecl)] - public static extern void install_syncuser_callbacks(UserChangedCallback changed_callback); - } - - private (IntPtr NotificationToken, GCHandle UserHandle)? _changeSubscriptionInfo; - - [Preserve] - public SyncUserHandle(IntPtr handle) : base(handle) - { - } - - public static void Initialize() - { - NativeMethods.UserChangedCallback changed = HandleUserChanged; - - GCHandle.Alloc(changed); - - NativeMethods.install_syncuser_callbacks(changed); - } - - public string GetUserId() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_user_id(this, buffer, length, out ex); - })!; - } - - public string GetRefreshToken() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_refresh_token(this, buffer, length, out ex); - })!; - } - - public string GetAccessToken() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_access_token(this, buffer, length, out ex); - })!; - } - - public string GetDeviceId() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_device_id(this, buffer, length, out ex); - })!; - } - - public UserState GetState() - { - var result = NativeMethods.get_state(this, out var ex); - ex.ThrowIfNecessary(); - return result; - } - - public bool TryGetApp([MaybeNullWhen(false)] out AppHandle appHandle) - { - var result = NativeMethods.get_app(this, out var ex); - ex.ThrowIfNecessary(); - - if (result == IntPtr.Zero) - { - appHandle = null; - return false; - } - - appHandle = new AppHandle(result); - return true; - } - - public void LogOut() - { - NativeMethods.log_out(this, out var ex); - ex.ThrowIfNecessary(); - } - - public async Task RefreshCustomDataAsync() - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - NativeMethods.refresh_custom_data(this, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public string? GetProfileData(UserProfileField field) - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) - => NativeMethods.get_profile_data(this, field, buffer, length, out isNull, out ex)); - } - - public string? GetCustomData() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) - => NativeMethods.get_custom_data(this, buffer, length, out isNull, out ex)); - } - - public async Task CallFunctionAsync(AppHandle app, string name, string args, string? service) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - NativeMethods.call_function(this, app, name, name.IntPtrLength(), args, args.IntPtrLength(), service, service.IntPtrLength(), GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - return await tcs.Task; // .NET Host locks the dll when there's an exception here (coming from native) - } - finally - { - tcsHandle.Free(); - } - } - - public async Task LinkCredentialsAsync(AppHandle app, Native.Credentials credentials) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - NativeMethods.link_credentials(this, app, credentials, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - return await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public string GetIdentities() - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr length, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_identities(this, buffer, length, out ex); - })!; - } - - #region Api Keys - - public async Task CreateApiKeyAsync(AppHandle app, string name) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - NativeMethods.create_api_key(this, app, name, (IntPtr)name.Length, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - var result = await tcs.Task; - - Debug.Assert(result.Length == 1, "The result of Create should be exactly 1 ApiKey."); - - return result.Single(); - } - finally - { - tcsHandle.Free(); - } - } - - public async Task FetchApiKeyAsync(AppHandle app, ObjectId id) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.fetch_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - var result = await tcs.Task; - - Debug.Assert(result is null || result.Length <= 1, "The result of the fetch operation should be either null, or an array of 0 or 1 elements."); - - return result is null || result.Length == 0 ? null : result.Single(); - } - finally - { - tcsHandle.Free(); - } - } - - public async Task FetchAllApiKeysAsync(AppHandle app) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - NativeMethods.fetch_api_keys(this, app, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - return await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task DeleteApiKeyAsync(AppHandle app, ObjectId id) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - try - { - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.delete_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task DisableApiKeyAsync(AppHandle app, ObjectId id) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.disable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - public async Task EnableApiKeyAsync(AppHandle app, ObjectId id) - { - var tcs = new TaskCompletionSource(); - var tcsHandle = GCHandle.Alloc(tcs); - - try - { - var primitiveId = PrimitiveValue.ObjectId(id); - NativeMethods.enable_api_key(this, app, primitiveId, GCHandle.ToIntPtr(tcsHandle), out var ex); - ex.ThrowIfNecessary(); - - await tcs.Task; - } - finally - { - tcsHandle.Free(); - } - } - - #endregion - - protected override void Unbind() - { - UnsubscribeNotifications(); - NativeMethods.destroy(handle); - } - - public string GetRealmPath(string? partition = null) - { - return MarshalHelpers.GetString((IntPtr buffer, IntPtr bufferLength, out bool isNull, out NativeException ex) => - { - isNull = false; - return NativeMethods.get_path_for_realm(this, partition, partition.IntPtrLength(), buffer, bufferLength, out ex); - })!; - } - - public void SubscribeNotifications(User user) - { - Debug.Assert(_changeSubscriptionInfo == null, $"{nameof(_changeSubscriptionInfo)} must be null before subscribing."); - - var managedUserHandle = GCHandle.Alloc(user, GCHandleType.Weak); - var userPointer = GCHandle.ToIntPtr(managedUserHandle); - var token = NativeMethods.register_changed_callback(this, userPointer, out var ex); - ex.ThrowIfNecessary(); - - _changeSubscriptionInfo = (token, managedUserHandle); - } - - public void UnsubscribeNotifications() - { - if (_changeSubscriptionInfo != null) - { - // This needs to use the handle directly because it's being called in Unbind. At this point the SafeHandle is closed, which means we'll - // get an error if we attempted to marshal it to native. The raw pointer is fine though and we can use it. - NativeMethods.unregister_changed_callback(handle, _changeSubscriptionInfo.Value.NotificationToken, out var ex); - ex.ThrowIfNecessary(); - - _changeSubscriptionInfo.Value.UserHandle.Free(); - _changeSubscriptionInfo = null; - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.UserChangedCallback))] - private static void HandleUserChanged(IntPtr managedUserHandle) - { - try - { - if (managedUserHandle == IntPtr.Zero) - { - return; - } - - var user = (User?)GCHandle.FromIntPtr(managedUserHandle).Target; - if (user is null) - { - // We're taking a weak handle to the user, so it's possible that it's been collected - return; - } - - ThreadPool.QueueUserWorkItem(_ => - { - user.RaiseChanged(); - }); - } - catch (Exception ex) - { - RealmLogger.Default.Log(LogLevel.Error, $"An error has occurred while raising User.Changed event: {ex}"); - } - } - } -} diff --git a/Realm/Realm/Helpers/RealmObjectSerializer.cs b/Realm/Realm/Helpers/RealmObjectSerializer.cs index d90165ae37..acfe8a3350 100644 --- a/Realm/Realm/Helpers/RealmObjectSerializer.cs +++ b/Realm/Realm/Helpers/RealmObjectSerializer.cs @@ -304,7 +304,7 @@ private static void WriteArray(BsonSerializationContext context, BsonSer var type = typeof(TValue); Action serialize = type switch { - _ when type.IsRealmObject() || type.IsAsymmetricObject() => RealmObjectSerializer.LookupSerializer(type)!.SerializeId, + _ when type.IsRealmObject() => RealmObjectSerializer.LookupSerializer(type)!.SerializeId, _ when type.IsEmbeddedObject() => RealmObjectSerializer.LookupSerializer(type)!.Serialize, _ => BsonSerializer.LookupSerializer().Serialize }; @@ -325,7 +325,7 @@ protected void WriteDictionary(BsonSerializationContext context, BsonSer var type = typeof(TValue); Action serialize = type switch { - _ when type.IsRealmObject() || type.IsAsymmetricObject() => RealmObjectSerializer.LookupSerializer(type)!.SerializeId, + _ when type.IsRealmObject() => RealmObjectSerializer.LookupSerializer(type)!.SerializeId, _ when type.IsEmbeddedObject() => RealmObjectSerializer.LookupSerializer(type)!.Serialize, _ => BsonSerializer.LookupSerializer().Serialize }; diff --git a/Realm/Realm/Helpers/SerializationHelper.cs b/Realm/Realm/Helpers/SerializationHelper.cs index 0734cbdb69..a5c48b4f20 100644 --- a/Realm/Realm/Helpers/SerializationHelper.cs +++ b/Realm/Realm/Helpers/SerializationHelper.cs @@ -28,7 +28,6 @@ using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; using Realms.Serialization; -using Realms.Sync; namespace Realms.Helpers { @@ -80,8 +79,6 @@ internal static void PreserveSerializers() _ = new StringSerializer(); _ = new ByteArraySerializer(); - _ = new EnumSerializer(); - _ = new ArraySerializer(); _ = new ArraySerializer(); _ = new ArraySerializer(); diff --git a/Realm/Realm/Linq/IRealmCollection.cs b/Realm/Realm/Linq/IRealmCollection.cs index 3ea30ec5d8..e5020cec7f 100644 --- a/Realm/Realm/Linq/IRealmCollection.cs +++ b/Realm/Realm/Linq/IRealmCollection.cs @@ -86,7 +86,7 @@ public interface IRealmCollection : IReadOnlyList, INotifyCollectionCh /// /// Gets the , describing the persisted properties of the - /// , , or instances + /// or instances /// contained in the collection. If the collection contains primitive values, will /// be null. /// diff --git a/Realm/Realm/Linq/QueryMethods.cs b/Realm/Realm/Linq/QueryMethods.cs index 0e6558292c..4c34d86357 100644 --- a/Realm/Realm/Linq/QueryMethods.cs +++ b/Realm/Realm/Linq/QueryMethods.cs @@ -118,9 +118,6 @@ public static bool FullTextSearch(string? str, string terms) /// } /// } ///
- /// - /// Note that if you're using Sync, the name of the embedded object type must match exactly the title of - /// the embedded object defined in the GeoJson Schema on the server. ///
public static bool GeoWithin(IEmbeddedObject? point, GeoShapeBase geoShape) { diff --git a/Realm/Realm/Linq/RealmResultsVisitor.cs b/Realm/Realm/Linq/RealmResultsVisitor.cs index 41b4b16112..bc0394244b 100644 --- a/Realm/Realm/Linq/RealmResultsVisitor.cs +++ b/Realm/Realm/Linq/RealmResultsVisitor.cs @@ -59,16 +59,8 @@ internal static class String { internal static readonly LazyMethod Contains = Capture(s => s.Contains(string.Empty)); -#pragma warning disable CS0618 // Type or member is obsolete - internal static readonly LazyMethod InstanceContainsStringComparison = Capture(s => s.Contains(string.Empty, StringComparison.Ordinal)); -#pragma warning restore CS0618 // Type or member is obsolete - internal static readonly LazyMethod ContainsStringComparison = Capture(s => QueryMethods.Contains(s, string.Empty, StringComparison.Ordinal)); -#pragma warning disable CS0618 // Type or member is obsolete - internal static readonly LazyMethod LegacyLike = Capture(s => s.Like(string.Empty, true)); -#pragma warning restore CS0618 // Type or member is obsolete - internal static readonly LazyMethod Like = Capture(s => QueryMethods.Like(s, string.Empty, true)); internal static readonly LazyMethod FullTextSearch = Capture(s => QueryMethods.FullTextSearch(s, string.Empty)); @@ -400,7 +392,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node) { queryMethod = (q, r, p, v) => q.StringEqual(r, p, v, GetComparisonCaseSensitive(node)); } - else if (AreMethodsSame(node.Method, Methods.String.Like.Value) || AreMethodsSame(node.Method, Methods.String.LegacyLike.Value)) + else if (AreMethodsSame(node.Method, Methods.String.Like.Value)) { member = node.Arguments[0] as MemberExpression; stringArgumentIndex = 1; @@ -488,15 +480,6 @@ private static bool AreMethodsSame(MethodInfo first, MethodInfo second) private static bool IsStringContainsWithComparison(MethodInfo method, out int stringArgumentIndex) { -#if !NETCOREAPP2_1_OR_GREATER - if (AreMethodsSame(method, Methods.String.InstanceContainsStringComparison.Value)) - { - // This is an extension method, so the string to compare against is at position 1. - stringArgumentIndex = 1; - return true; - } -#endif - if (AreMethodsSame(method, Methods.String.ContainsStringComparison.Value)) { stringArgumentIndex = 1; diff --git a/Realm/Realm/Logging/LogCategory.cs b/Realm/Realm/Logging/LogCategory.cs index 75095f31ba..58eb8b578a 100644 --- a/Realm/Realm/Logging/LogCategory.cs +++ b/Realm/Realm/Logging/LogCategory.cs @@ -35,20 +35,12 @@ namespace Realms.Logging /// │ ├─► Query /// │ ├─► Object /// │ └─► Notification - /// ├─► Sync - /// │ ├─► Client - /// │ │ ├─► Session - /// │ │ ├─► Changeset - /// │ │ ├─► Network - /// │ │ └─► Reset - /// │ └─► Server - /// ├─► App /// └─► SDK /// /// /// /// - /// LogCategory.Realm.Sync.Client + /// LogCategory.Realm.Storage.Transaction /// /// public class LogCategory @@ -71,14 +63,6 @@ public class LogCategory { Realm.Storage.Query.Name, Realm.Storage.Query }, { Realm.Storage.Object.Name, Realm.Storage.Object }, { Realm.Storage.Notification.Name, Realm.Storage.Notification }, - { Realm.Sync.Name, Realm.Sync }, - { Realm.Sync.Client.Name, Realm.Sync.Client }, - { Realm.Sync.Client.Session.Name, Realm.Sync.Client.Session }, - { Realm.Sync.Client.Changeset.Name, Realm.Sync.Client.Changeset }, - { Realm.Sync.Client.Network.Name, Realm.Sync.Client.Network }, - { Realm.Sync.Client.Reset.Name, Realm.Sync.Client.Reset }, - { Realm.Sync.Server.Name, Realm.Sync.Server }, - { Realm.App.Name, Realm.App }, { Realm.SDK.Name, Realm.SDK }, }; @@ -107,16 +91,6 @@ public class RealmLogCategory : LogCategory /// public StorageLogCategory Storage { get; } - /// - /// Gets the category for receiving log messages pertaining to Atlas Device Sync. - /// - public SyncLogCategory Sync { get; } - - /// - /// Gets the category for receiving log messages pertaining to Atlas App. - /// - public LogCategory App { get; } - /// /// Gets the category for receiving log messages pertaining to the SDK. /// @@ -125,8 +99,6 @@ public class RealmLogCategory : LogCategory internal RealmLogCategory() : base("Realm", null) { Storage = new StorageLogCategory(this); - Sync = new SyncLogCategory(this); - App = new LogCategory("App", this); SDK = new LogCategory("SDK", this); } } @@ -166,62 +138,5 @@ internal StorageLogCategory(LogCategory parent) : base("Storage", parent) Notification = new LogCategory("Notification", this); } } - - /// - /// The category for receiving log messages pertaining to Atlas Device Sync. - /// - public class SyncLogCategory : LogCategory - { - /// - /// Gets the category for receiving log messages pertaining to sync client operations. - /// - public ClientLogCategory Client { get; } - - /// - /// Gets the category for receiving log messages pertaining to sync server operations. - /// - public LogCategory Server { get; } - - internal SyncLogCategory(LogCategory parent) : base("Sync", parent) - { - Client = new ClientLogCategory(this); - Server = new LogCategory("Server", this); - } - } - - /// - /// The category for receiving log messages pertaining to sync client operations. - /// - public class ClientLogCategory : LogCategory - { - /// - /// Gets the category for receiving log messages pertaining to the sync session. - /// - public LogCategory Session { get; } - - /// - /// Gets the category for receiving log messages when receiving, uploading, and - /// integrating changesets. - /// - public LogCategory Changeset { get; } - - /// - /// Gets the category for receiving log messages pertaining to low-level network activity. - /// - public LogCategory Network { get; } - - /// - /// Gets the category for receiving log messages when there are client reset operations. - /// - public LogCategory Reset { get; } - - internal ClientLogCategory(LogCategory parent) : base("Client", parent) - { - Session = new LogCategory("Session", this); - Changeset = new LogCategory("Changeset", this); - Network = new LogCategory("Network", this); - Reset = new LogCategory("Reset", this); - } - } } } diff --git a/Realm/Realm/Logging/Logger.cs b/Realm/Realm/Logging/RealmLogger.cs similarity index 81% rename from Realm/Realm/Logging/Logger.cs rename to Realm/Realm/Logging/RealmLogger.cs index de3d7648e5..157b99de19 100644 --- a/Realm/Realm/Logging/Logger.cs +++ b/Realm/Realm/Logging/RealmLogger.cs @@ -26,24 +26,6 @@ namespace Realms.Logging { - /// - [Obsolete("Use RealmLogger instead. If using a custom logger, RealmLogger.LogImpl() additionally receives the log category.")] - public abstract class Logger : RealmLogger - { - /// - /// The internal implementation being called from . - /// - /// The criticality level for the message. - /// The message to log. - protected abstract void LogImpl(LogLevel level, string message); - - /// - protected override void LogImpl(LogLevel level, LogCategory category, string message) - { - LogImpl(level, message); - } - } - /// /// A logger that logs messages originating from Realm. The default logger can be replaced by setting . ///
@@ -94,16 +76,6 @@ public abstract class RealmLogger /// public static RealmLogger Function(Action logFunction) => new FunctionLogger((level, category, message) => logFunction(FormatLog(level, category, message))); - /// - /// Gets a that proxies Log calls to the supplied function. - /// - /// Function to proxy log calls to. - /// - /// A instance that will invoke for each message. - /// - [Obsolete("Use Function(Action logFunction).")] - public static RealmLogger Function(Action logFunction) => new FunctionLogger((level, _, message) => logFunction(level, message)); - /// /// Gets a that proxies Log calls to the supplied function. /// @@ -113,20 +85,6 @@ public abstract class RealmLogger /// public static RealmLogger Function(Action logFunction) => new FunctionLogger(logFunction); - /// - /// Gets or sets the verbosity of log messages for all log categories via . - /// - /// The log level for Realm-originating messages. - [Obsolete("Use GetLogLevel() and SetLogLevel().")] - public static LogLevel LogLevel - { - get => GetLogLevel(); - set - { - SetLogLevel(value); - } - } - /// /// Gets the verbosity of log messages for the given category. /// @@ -232,37 +190,25 @@ protected override void LogImpl(LogLevel level, LogCategory category, string mes } } - private class FileLogger : RealmLogger + private class FileLogger(string filePath, Encoding? encoding = null) + : RealmLogger { private readonly object _locker = new(); - private readonly string _filePath; - private readonly Encoding _encoding; - - public FileLogger(string filePath, Encoding? encoding = null) - { - _filePath = filePath; - _encoding = encoding ?? Encoding.UTF8; - } + private readonly Encoding _encoding = encoding ?? Encoding.UTF8; protected override void LogImpl(LogLevel level, LogCategory category, string message) { lock (_locker) { - System.IO.File.AppendAllText(_filePath, FormatLog(level, category, message) + Environment.NewLine, _encoding); + System.IO.File.AppendAllText(filePath, FormatLog(level, category, message) + Environment.NewLine, _encoding); } } } - private class FunctionLogger : RealmLogger + private class FunctionLogger(Action logFunction) + : RealmLogger { - private readonly Action _logFunction; - - public FunctionLogger(Action logFunction) - { - _logFunction = logFunction; - } - - protected override void LogImpl(LogLevel level, LogCategory category, string message) => _logFunction(level, category, message); + protected override void LogImpl(LogLevel level, LogCategory category, string message) => logFunction(level, category, message); } private class NullLogger : RealmLogger diff --git a/Realm/Realm/Native/AppConfiguration.cs b/Realm/Realm/Native/AppConfiguration.cs deleted file mode 100644 index 65362d0ccd..0000000000 --- a/Realm/Realm/Native/AppConfiguration.cs +++ /dev/null @@ -1,119 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; - -namespace Realms.Sync.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct AppConfiguration - { - [MarshalAs(UnmanagedType.LPWStr)] - private string app_id; - private IntPtr app_id_len; - - internal string AppId - { - set - { - app_id = value; - app_id_len = (IntPtr)value.Length; - } - } - - [MarshalAs(UnmanagedType.LPWStr)] - private string base_file_path; - private IntPtr base_file_path_len; - - internal string BaseFilePath - { - set - { - base_file_path = value; - base_file_path_len = (IntPtr)value.Length; - } - } - - [MarshalAs(UnmanagedType.LPWStr)] - private string base_url; - private IntPtr base_url_len; - - internal string BaseUrl - { - set - { - base_url = value; - base_url_len = value.IntPtrLength(); - } - } - - internal UInt64 default_request_timeout_ms; - - private MetadataPersistenceMode metadata_persistence; - - [MarshalAs(UnmanagedType.U1)] - private bool metadata_persistence_has_value; - - internal MetadataPersistenceMode? MetadataPersistence - { - set - { - metadata_persistence = value.HasValue ? value.Value : default; - metadata_persistence_has_value = value.HasValue; - } - } - - internal IntPtr managed_http_client; - - internal IntPtr managed_websocket_provider; - - internal SyncTimeoutOptions sync_timeout_options; - - [MarshalAs(UnmanagedType.U1)] - internal bool use_cache; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SyncTimeoutOptions - { - internal UInt64 sync_connect_timeout_ms; - - internal UInt64 sync_connection_linger_time_ms; - - internal UInt64 sync_ping_keep_alive_period_ms; - - internal UInt64 sync_pong_keep_alive_timeout_ms; - - internal UInt64 sync_fast_reconnect_limit; - - internal ReconnectBackoffOptions reconnect_backoff_options; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct ReconnectBackoffOptions - { - internal UInt64 max_resumption_delay_interval_ms; - - internal UInt64 resumption_delay_interval_ms; - - internal int resumption_delay_backoff_multiplier; - - internal int delay_jitter_divisor; - } -} diff --git a/Realm/Realm/Native/AppError.cs b/Realm/Realm/Native/AppError.cs deleted file mode 100644 index c2655d0c4e..0000000000 --- a/Realm/Realm/Native/AppError.cs +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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.Runtime.InteropServices; -using Realms.Native; - -namespace Realms.Sync.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct AppError - { - [MarshalAs(UnmanagedType.U1)] - public bool is_null; - - private PrimitiveValue message; - - private PrimitiveValue error_category; - - private PrimitiveValue logs_link; - - public int http_status_code; - - public string? Message => message.Type == RealmValueType.Null ? null : message.AsString(); - - public string? ErrorCategory => error_category.Type == RealmValueType.Null ? null : error_category.AsString(); - - public string? LogsLink => logs_link.Type == RealmValueType.Null ? null : logs_link.AsString(); - } -} diff --git a/Realm/Realm/Native/Credentials.cs b/Realm/Realm/Native/Credentials.cs deleted file mode 100644 index 2a4d9e5f97..0000000000 --- a/Realm/Realm/Native/Credentials.cs +++ /dev/null @@ -1,55 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using static Realms.Sync.Credentials; - -namespace Realms.Sync.Native -{ - internal struct Credentials - { - internal AuthProvider provider; - - [MarshalAs(UnmanagedType.LPWStr)] - private string? token; - private IntPtr token_len; - - internal string? Token - { - set - { - token = value; - token_len = value.IntPtrLength(); - } - } - - [MarshalAs(UnmanagedType.LPWStr)] - private string? additional_info; - private IntPtr additional_info_len; - - internal string? AdditionalInfo - { - set - { - additional_info = value; - additional_info_len = value.IntPtrLength(); - } - } - } -} diff --git a/Realm/Realm/Native/HttpClientTransport.cs b/Realm/Realm/Native/HttpClientTransport.cs deleted file mode 100644 index c435dbd915..0000000000 --- a/Realm/Realm/Native/HttpClientTransport.cs +++ /dev/null @@ -1,234 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net.Http; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Realms.Native -{ - internal static class HttpClientTransport - { - private enum CustomErrorCode - { - NoError = 0, - HttpClientDisposed = 997, - UnknownHttp = 998, - Unknown = 999, - Timeout = 1000, - } - -#pragma warning disable SA1300 // Element should begin with upper-case letter - - private enum NativeHttpMethod - { - get, - post, - patch, - put, - del - } - - [StructLayout(LayoutKind.Sequential)] - private readonly struct HttpClientRequest - { - public readonly NativeHttpMethod method; - - private readonly StringValue url; - - public readonly UInt64 timeout_ms; - - public readonly MarshaledVector> headers; - - private readonly StringValue body; - - private readonly IntPtr managed_http_client; - - public string Url => url!; - - public string? Body => body; - - public HttpClient HttpClient - { - get - { - if (managed_http_client != IntPtr.Zero && - GCHandle.FromIntPtr(managed_http_client).Target is HttpClient client) - { - return client; - } - - throw new ObjectDisposedException("HttpClient has been disposed, most likely because the app has been closed."); - } - } - } - - [StructLayout(LayoutKind.Sequential)] - private struct HttpClientResponse - { - public Int32 http_status_code; - - public CustomErrorCode custom_status_code; - - public MarshaledVector> headers; - - public StringValue body; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void execute_request(HttpClientRequest request, IntPtr callback_ptr); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_http_transport_install_callbacks", CallingConvention = CallingConvention.Cdecl)] - private static extern void install_callbacks(execute_request execute); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_http_transport_respond", CallingConvention = CallingConvention.Cdecl)] - private static extern void respond(HttpClientResponse response, IntPtr callback_ptr); - -#pragma warning restore SA1300 // Element should begin with upper-case letter - - internal static void Initialize() - { - execute_request execute = ExecuteRequest; - - GCHandle.Alloc(execute); - - install_callbacks(execute); - } - - private static HttpRequestMessage BuildRequest(HttpClientRequest request) - { - var message = new HttpRequestMessage(request.method.ToHttpMethod(), request.Url); - foreach (var header in request.headers) - { - message.Headers.TryAddWithoutValidation(header.Key!, header.Value); - } - - if (request.method != NativeHttpMethod.get) - { - message.Content = new StringContent(request.Body!, Encoding.UTF8, "application/json"); - } - - return message; - } - - [MonoPInvokeCallback(typeof(execute_request))] - private static async void ExecuteRequest(HttpClientRequest request, IntPtr callback) - { - try - { - using var arena = new Arena(); - try - { - var httpClient = request.HttpClient; - - using var message = BuildRequest(request); - using var cts = new CancellationTokenSource((int)request.timeout_ms); - - var response = await httpClient.SendAsync(message, cts.Token).ConfigureAwait(false); - - var headers = response.Headers.Concat(response.Content.Headers) - .Select(h => new MarshaledPair(StringValue.AllocateFrom(h.Key, arena), StringValue.AllocateFrom(h.Value.FirstOrDefault(), arena))) - .ToArray(); - - var nativeResponse = new HttpClientResponse - { - http_status_code = (int)response.StatusCode, - headers = MarshaledVector>.AllocateFrom(headers, arena), - body = StringValue.AllocateFrom(await response.Content.ReadAsStringAsync().ConfigureAwait(false), arena), - }; - - respond(nativeResponse, callback); - } - catch (HttpRequestException rex) - { - var sb = new StringBuilder("An unexpected error occurred while sending the request"); - - // We're doing this because the message for the top-level exception is usually pretty useless. - // If there's inner exception, we want to skip it and directly go for the more specific messages. - var innerEx = rex.InnerException ?? rex; - while (innerEx != null) - { - sb.Append($": {innerEx.Message}"); - innerEx = innerEx.InnerException; - } - - var nativeResponse = new HttpClientResponse - { - custom_status_code = CustomErrorCode.UnknownHttp, - body = StringValue.AllocateFrom(sb.ToString(), arena), - }; - - respond(nativeResponse, callback); - } - catch (TaskCanceledException) - { - var nativeResponse = new HttpClientResponse - { - custom_status_code = CustomErrorCode.Timeout, - body = StringValue.AllocateFrom($"Operation failed to complete within {request.timeout_ms} ms.", arena), - }; - - respond(nativeResponse, callback); - } - catch (ObjectDisposedException ode) - { - var nativeResponse = new HttpClientResponse - { - custom_status_code = CustomErrorCode.HttpClientDisposed, - body = StringValue.AllocateFrom(ode.Message, arena), - }; - - respond(nativeResponse, callback); - } - catch (Exception ex) - { - var nativeResponse = new HttpClientResponse - { - custom_status_code = CustomErrorCode.Unknown, - body = StringValue.AllocateFrom(ex.Message, arena), - }; - - respond(nativeResponse, callback); - } - } - catch (Exception outerEx) - { - Debug.WriteLine($"Unexpected error occurred while trying to respond to a request: {outerEx}"); - } - } - - private static HttpMethod ToHttpMethod(this NativeHttpMethod nativeMethod) - { - return nativeMethod switch - { - NativeHttpMethod.get => HttpMethod.Get, - NativeHttpMethod.post => HttpMethod.Post, - NativeHttpMethod.patch => new HttpMethod("PATCH"), - NativeHttpMethod.put => HttpMethod.Put, - NativeHttpMethod.del => HttpMethod.Delete, - _ => throw new NotSupportedException($"Unsupported HTTP method: {nativeMethod}") - }; - } - } -} diff --git a/Realm/Realm/Native/NativeCommon.cs b/Realm/Realm/Native/NativeCommon.cs index 9dc192fc9e..f389d9e1b2 100644 --- a/Realm/Realm/Native/NativeCommon.cs +++ b/Realm/Realm/Native/NativeCommon.cs @@ -23,8 +23,6 @@ using System.Threading; using Realms.Helpers; using Realms.Logging; -using Realms.Native; -using Realms.Sync; #if !NET5_0_OR_GREATER using System.IO; #endif @@ -74,11 +72,6 @@ internal static void Initialize() SynchronizationContextScheduler.Initialize(); SharedRealmHandle.Initialize(); - SessionHandle.Initialize(); - SyncUserHandle.Initialize(); - HttpClientTransport.Initialize(); - AppHandle.Initialize(); - SubscriptionSetHandle.Initialize(); SerializationHelper.Initialize(); } @@ -101,8 +94,6 @@ public static void CleanupNativeResources(string reason) var sw = new Stopwatch(); sw.Start(); - AppHandle.ForceCloseHandles(); - AsyncOpenTaskHandle.CancelInFlightTasks(); SharedRealmHandle.ForceCloseNativeRealms(); sw.Stop(); diff --git a/Realm/Realm/Native/SessionNotificationToken.cs b/Realm/Realm/Native/SessionNotificationToken.cs deleted file mode 100644 index 3af87b1313..0000000000 --- a/Realm/Realm/Native/SessionNotificationToken.cs +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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.Runtime.InteropServices; - -namespace Realms.Sync.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct SessionNotificationToken - { - internal ulong connection_state; - } -} diff --git a/Realm/Realm/Native/Subscription.cs b/Realm/Realm/Native/Subscription.cs deleted file mode 100644 index f45665afe4..0000000000 --- a/Realm/Realm/Native/Subscription.cs +++ /dev/null @@ -1,61 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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.Runtime.InteropServices; -using Realms.Native; -using ManagedSubscription = Realms.Sync.Subscription; - -namespace Realms.Sync.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct Subscription - { - private PrimitiveValue id; - - private PrimitiveValue name; - - private PrimitiveValue object_type; - - private PrimitiveValue query; - - private PrimitiveValue created_at; - - private PrimitiveValue updated_at; - - [MarshalAs(UnmanagedType.I1)] - private bool has_value; - - public ManagedSubscription? ManagedSubscription - { - get - { - if (!has_value) - { - return null; - } - - return new(id: id.AsObjectId(), - name: name.Type == RealmValueType.Null ? null : name.AsString(), - objectType: object_type.AsString(), - query: query.AsString(), - createdAt: created_at.AsDate(), - updatedAt: updated_at.AsDate()); - } - } - } -} diff --git a/Realm/Realm/Native/SyncConfiguration.cs b/Realm/Realm/Native/SyncConfiguration.cs deleted file mode 100644 index 18146d3b20..0000000000 --- a/Realm/Realm/Native/SyncConfiguration.cs +++ /dev/null @@ -1,63 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; - -namespace Realms.Sync.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct SyncConfiguration - { - private IntPtr sync_user_ptr; - - internal SyncUserHandle SyncUserHandle - { - set - { - sync_user_ptr = value.DangerousGetHandle(); - } - } - - [MarshalAs(UnmanagedType.LPWStr)] - private string partition; - - private IntPtr partition_len; - - internal string Partition - { - set - { - partition = value; - partition_len = (IntPtr)value.Length; - } - } - - internal SessionStopPolicy session_stop_policy; - - internal SchemaMode schema_mode; - - [MarshalAs(UnmanagedType.I1)] - internal bool is_flexible_sync; - - internal ClientResyncMode client_resync_mode; - - [MarshalAs(UnmanagedType.I1)] - internal bool cancel_waits_on_nonfatal_error; - } -} diff --git a/Realm/Realm/Native/SyncError.cs b/Realm/Realm/Native/SyncError.cs deleted file mode 100644 index 09ecae53a5..0000000000 --- a/Realm/Realm/Native/SyncError.cs +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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.Generic; -using System.Runtime.InteropServices; -using Realms.Sync.Exceptions; - -namespace Realms.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct SyncError - { - public ErrorCode error_code; - - public StringValue message; - - public StringValue log_url; - - [MarshalAs(UnmanagedType.U1)] - public bool is_client_reset; - - public MarshaledVector> user_info_pairs; - - public MarshaledVector compensating_writes; - - [StructLayout(LayoutKind.Sequential)] - internal struct CompensatingWriteInfo - { - public StringValue reason; - public StringValue object_name; - public PrimitiveValue primary_key; - } - } -} diff --git a/Realm/Realm/Native/SyncSocketProvider.EventLoop.cs b/Realm/Realm/Native/SyncSocketProvider.EventLoop.cs deleted file mode 100644 index d74a5f5161..0000000000 --- a/Realm/Realm/Native/SyncSocketProvider.EventLoop.cs +++ /dev/null @@ -1,124 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Realms.Logging; - -namespace Realms.Native; - -internal partial class SyncSocketProvider -{ - private class Timer - { - private readonly CancellationTokenSource _cts = new(); - - internal Timer(TimeSpan delay, IntPtr nativeCallback, ChannelWriter workQueue) - { - RealmLogger.Default.Log(LogLevel.Trace, $"Creating timer with delay {delay} and target {nativeCallback}."); - var cancellationToken = _cts.Token; - Task.Delay(delay, cancellationToken).ContinueWith(async _ => - { - await workQueue.WriteAsync(new Work(nativeCallback, cancellationToken)); - }); - } - - internal void Cancel() - { - RealmLogger.Default.Log(LogLevel.Trace, $"Canceling timer."); - _cts.Cancel(); - _cts.Dispose(); - } - - private class Work(IntPtr nativeCallback, CancellationToken cancellationToken) - : IWork - { - public void Execute() - { - var status = Status.OK; - if (cancellationToken.IsCancellationRequested) - { - status = new(ErrorCode.OperationAborted, "Timer canceled"); - } - - RunCallback(nativeCallback, status); - } - } - } - - // Belongs to SyncSocketProvider. When Native destroys the Provider we need to stop executing - // enqueued work, but we need to release all the callbacks we copied on the heap. - private class EventLoopWork(IntPtr nativeCallback, CancellationToken cancellationToken) - : IWork - { - public void Execute() - { - if (cancellationToken.IsCancellationRequested) - { - RealmLogger.Default.Log(LogLevel.Trace, "Deleting EventLoopWork callback only because event loop was cancelled."); - NativeMethods.delete_callback(nativeCallback); - return; - } - - RunCallback(nativeCallback, Status.OK); - } - } - - private static void RunCallback(IntPtr nativeCallback, Status status) - { - RealmLogger.Default.Log(LogLevel.Trace, $"SyncSocketProvider running native callback {nativeCallback} with status {status.Code} \"{status.Reason}\"."); - - using var arena = new Arena(); - NativeMethods.run_callback(nativeCallback, status.Code, StringValue.AllocateFrom(status.Reason, arena)); - } - - private async Task PostWorkAsync(IntPtr nativeCallback) - { - RealmLogger.Default.Log(LogLevel.Trace, "Posting work to SyncSocketProvider event loop."); - await _workQueue.Writer.WriteAsync(new EventLoopWork(nativeCallback, _cts.Token)); - } - - private async partial Task WorkThread() - { - RealmLogger.Default.Log(LogLevel.Trace, "Starting SyncSocketProvider event loop."); - try - { - while (await _workQueue.Reader.WaitToReadAsync()) - { - while (_workQueue.Reader.TryRead(out var work)) - { - work.Execute(); - } - } - } - catch (Exception e) - { - RealmLogger.Default.Log(LogLevel.Error, $"Error occurred in SyncSocketProvider event loop {e.GetType().FullName}: {e.Message}"); - if (!string.IsNullOrEmpty(e.StackTrace)) - { - RealmLogger.Default.Log(LogLevel.Trace, e.StackTrace); - } - - throw; - } - - RealmLogger.Default.Log(LogLevel.Trace, "Exiting SyncSocketProvider event loop."); - } -} diff --git a/Realm/Realm/Native/SyncSocketProvider.Native.cs b/Realm/Realm/Native/SyncSocketProvider.Native.cs deleted file mode 100644 index dfc1f9e1b2..0000000000 --- a/Realm/Realm/Native/SyncSocketProvider.Native.cs +++ /dev/null @@ -1,98 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Net.WebSockets; -using System.Runtime.InteropServices; - -namespace Realms.Native; - -internal partial class SyncSocketProvider -{ - // additional websocket close status codes that Sync understands - private const int RLM_ERR_WEBSOCKET_CONNECTION_FAILED = 4401; - private const int RLM_ERR_WEBSOCKET_READ_ERROR = 4402; - private const int RLM_ERR_WEBSOCKET_WRITE_ERROR = 4403; - - // equivalent to ErrorCodes::Error in - public enum ErrorCode : int - { - Ok = 0, - RuntimeError = 1000, - OperationAborted = 1027 - } - - [StructLayout(LayoutKind.Sequential)] - public readonly struct Endpoint - { - public readonly StringValue address; - - public readonly ushort port; - - public readonly StringValue path; - - public readonly MarshaledVector protocols; - - public readonly NativeBool is_ssl; - } - - private static class NativeMethods - { - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void post_work(IntPtr socket_provider, IntPtr native_callback); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void provider_dispose(IntPtr managed_provider); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate IntPtr create_timer(IntPtr socket_provider, UInt64 delay_miliseconds, IntPtr native_callback); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void cancel_timer(IntPtr timer); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate IntPtr websocket_connect(IntPtr socket_provider, IntPtr observer, Endpoint endpoint); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void websocket_write(IntPtr managed_websocket, BinaryValue data, IntPtr native_callback); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void websocket_close(IntPtr managed_websocket); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_install_callbacks", CallingConvention = CallingConvention.Cdecl)] - public static extern void install_callbacks(post_work post, provider_dispose dispose, create_timer create_timer, cancel_timer cancel_timer, websocket_connect connect, websocket_write write, websocket_close close); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_run_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern void run_callback(IntPtr native_callback, ErrorCode result, StringValue reason); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_delete_callback", CallingConvention = CallingConvention.Cdecl)] - public static extern void delete_callback(IntPtr native_callback); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_observer_connected_handler", CallingConvention = CallingConvention.Cdecl)] - public static extern void observer_connected_handler(IntPtr observer, StringValue protocol); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_observer_error_handler", CallingConvention = CallingConvention.Cdecl)] - public static extern void observer_error_handler(IntPtr observer); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_observer_binary_message_received", CallingConvention = CallingConvention.Cdecl)] - public static extern void observer_binary_message_received(IntPtr observer, BinaryValue data); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_websocket_observer_closed_handler", CallingConvention = CallingConvention.Cdecl)] - public static extern void observer_closed_handler(IntPtr observer, NativeBool was_clean, WebSocketCloseStatus status, StringValue reason); - } -} diff --git a/Realm/Realm/Native/SyncSocketProvider.WebSocket.cs b/Realm/Realm/Native/SyncSocketProvider.WebSocket.cs deleted file mode 100644 index 5aa91cc811..0000000000 --- a/Realm/Realm/Native/SyncSocketProvider.WebSocket.cs +++ /dev/null @@ -1,264 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Buffers; -using System.IO; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Realms.Logging; - -namespace Realms.Native; - -internal partial class SyncSocketProvider -{ - private class Socket : IDisposable - { - private readonly ClientWebSocket _webSocket; - private readonly IntPtr _observer; - private readonly ChannelWriter _workQueue; - private readonly CancellationTokenSource _cts = new(); - private readonly CancellationToken _cancellationToken; - - private readonly Uri _uri; - private readonly Task _readThread; - - private MemoryStream _receiveBuffer = new(); - - internal Socket(ClientWebSocket webSocket, IntPtr observer, ChannelWriter workQueue, Uri uri) - { - RealmLogger.Default.Log(LogLevel.Trace, $"Creating a WebSocket to {uri.GetLeftPart(UriPartial.Path)}"); - _webSocket = webSocket; - _observer = observer; - _workQueue = workQueue; - _uri = uri; - _cancellationToken = _cts.Token; - _readThread = Task.Run(ReadThread); - } - - private async Task ReadThread() - { - RealmLogger.Default.Log(LogLevel.Trace, "Entering WebSocket event loop."); - - try - { - await _webSocket.ConnectAsync(_uri, _cancellationToken); - await _workQueue.WriteAsync(new WebSocketConnectedWork(_webSocket.SubProtocol!, _observer, _cancellationToken)); - } - catch (Exception e) - { - var builder = new StringBuilder(); - FormatExceptionForLogging(e, builder); - RealmLogger.Default.Log(LogLevel.Error, $"Error establishing WebSocket connection {builder}"); - - await _workQueue.WriteAsync(new WebSocketClosedWork(false, (WebSocketCloseStatus)RLM_ERR_WEBSOCKET_CONNECTION_FAILED, e.Message, _observer, _cancellationToken)); - return; - } - - var buffer = new byte[32 * 1024]; - while (_webSocket.State == WebSocketState.Open) - { - try - { - var result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), _cancellationToken); - switch (result.MessageType) - { - case WebSocketMessageType.Binary: - await _receiveBuffer.WriteAsync(buffer, 0, result.Count); - if (result.EndOfMessage) - { - var currentBuffer = _receiveBuffer; - _receiveBuffer = new MemoryStream(); - await _workQueue.WriteAsync(new BinaryMessageReceivedWork(currentBuffer, _observer, _cancellationToken)); - } - - break; - case WebSocketMessageType.Close: - RealmLogger.Default.Log(LogLevel.Trace, $"WebSocket closed with status {result.CloseStatus!.Value} and description \"{result.CloseStatusDescription}\""); - await _workQueue.WriteAsync(new WebSocketClosedWork(clean: true, result.CloseStatus!.Value, result.CloseStatusDescription!, _observer, _cancellationToken)); - break; - default: - RealmLogger.Default.Log(LogLevel.Trace, $"Received unexpected text WebSocket message: {Encoding.UTF8.GetString(buffer, 0, result.Count)}"); - break; - } - } - catch (Exception e) - { - var builder = new StringBuilder(); - FormatExceptionForLogging(e, builder); - RealmLogger.Default.Log(LogLevel.Error, $"Error reading from WebSocket {builder}"); - - await _workQueue.WriteAsync(new WebSocketClosedWork(false, (WebSocketCloseStatus)RLM_ERR_WEBSOCKET_READ_ERROR, e.Message, _observer, _cancellationToken)); - return; - } - } - } - - public async void Write(BinaryValue data, IntPtr native_callback) - { - if (_webSocket.State == WebSocketState.Aborted || _cancellationToken.IsCancellationRequested) - { - NativeMethods.delete_callback(native_callback); - return; - } - - var buffer = data.AsBytes(usePooledArray: true); - - try - { - await _webSocket.SendAsync(new(buffer), WebSocketMessageType.Binary, true, _cancellationToken); - } - catch (Exception e) - { - var builder = new StringBuilder(); - FormatExceptionForLogging(e, builder); - RealmLogger.Default.Log(LogLevel.Error, $"Error writing to WebSocket {builder}"); - - // in case of errors notify the websocket observer and just dispose the callback - await _workQueue.WriteAsync(new WebSocketClosedWork(false, (WebSocketCloseStatus)RLM_ERR_WEBSOCKET_WRITE_ERROR, e.Message, _observer, _cancellationToken)); - NativeMethods.delete_callback(native_callback); - return; - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - await _workQueue.WriteAsync(new EventLoopWork(native_callback, _cancellationToken)); - } - - public async void Dispose() - { - _cts.Cancel(); - - if (_webSocket.State == WebSocketState.Open) - { - try - { - // If the websocket is still open close it, but throw away any close errors. - await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default); - } - catch - { - } - } - - _webSocket.Dispose(); - _receiveBuffer.Dispose(); - _cts.Dispose(); - RealmLogger.Default.Log(LogLevel.Trace, "Disposing WebSocket."); - - try - { - await _readThread; - } - catch(TaskCanceledException) - { - } - catch(ChannelClosedException) - { - } - } - - private static void FormatExceptionForLogging(Exception ex, StringBuilder builder, int nesting = 0) - { - var indentation = new string('\t', nesting); - builder.Append(indentation); - - builder.AppendFormat("{0}: {1}", ex.GetType().FullName, ex.Message); - builder.AppendLine(); - if (RealmLogger.GetLogLevel(LogCategory.Realm.SDK) >= LogLevel.Trace && !string.IsNullOrEmpty(ex.StackTrace)) - { - builder.Append(indentation); - var indentedTrace = ex.StackTrace.Replace(Environment.NewLine, Environment.NewLine + indentation); - builder.AppendLine(indentedTrace); - } - - if (ex is AggregateException aggregateException) - { - foreach (var inner in aggregateException.InnerExceptions) - { - FormatExceptionForLogging(inner, builder, nesting + 1); - } - } - else if (ex.InnerException is { } inner) - { - FormatExceptionForLogging(inner, builder, nesting + 1); - } - } - } - - private abstract class WebSocketWork(IntPtr observer, CancellationToken cancellationToken) - : IWork - { - // Belongs to the Socket and canceled when Native destroys the socket. - // If it's canceled we shouldn't call any observer methods. - private readonly CancellationToken _cancellationToken = cancellationToken; - - protected abstract void Execute(IntPtr observer); - - void IWork.Execute() - { - if (!_cancellationToken.IsCancellationRequested) - { - Execute(observer); - } - } - } - - private sealed class WebSocketConnectedWork(string protocol, IntPtr observer, CancellationToken cancellationToken) - : WebSocketWork(observer, cancellationToken) - { - protected override void Execute(IntPtr observer) - { - using var arena = new Arena(); - NativeMethods.observer_connected_handler(observer, StringValue.AllocateFrom(protocol, arena)); - } - } - - private sealed class BinaryMessageReceivedWork(MemoryStream receiveBuffer, IntPtr observer, CancellationToken cancellationToken) - : WebSocketWork(observer, cancellationToken) - { - protected unsafe override void Execute(IntPtr observer) - { - using var buffer = receiveBuffer; - fixed (byte* data = buffer.GetBuffer()) - { - NativeMethods.observer_binary_message_received(observer, new() { data = data, size = (IntPtr)buffer.Length }); - } - } - } - - private sealed class WebSocketClosedWork(bool clean, WebSocketCloseStatus status, string description, IntPtr observer, CancellationToken cancellationToken) - : WebSocketWork(observer, cancellationToken) - { - protected override void Execute(IntPtr observer) - { - if (!clean) - { - NativeMethods.observer_error_handler(observer); - } - - using var arena = new Arena(); - NativeMethods.observer_closed_handler(observer, clean, status, StringValue.AllocateFrom(description, arena)); - } - } -} diff --git a/Realm/Realm/Native/SyncSocketProvider.cs b/Realm/Realm/Native/SyncSocketProvider.cs deleted file mode 100644 index e281847a9f..0000000000 --- a/Realm/Realm/Native/SyncSocketProvider.cs +++ /dev/null @@ -1,175 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; -using System.Linq; -using System.Net.WebSockets; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Realms.Logging; - -namespace Realms.Native; - -internal partial class SyncSocketProvider : IDisposable -{ - private static void PostWork(IntPtr managed_provider, IntPtr native_callback) - { - var provider = (SyncSocketProvider)GCHandle.FromIntPtr(managed_provider).Target!; - _ = provider.PostWorkAsync(native_callback); - } - - [MonoPInvokeCallback(typeof(NativeMethods.provider_dispose))] - private static void ProviderDispose(IntPtr managed_provider) - { - var handle = GCHandle.FromIntPtr(managed_provider); - ((SyncSocketProvider)handle.Target!).Dispose(); - handle.Free(); - } - - [MonoPInvokeCallback(typeof(NativeMethods.create_timer))] - private static IntPtr CreateTimer(IntPtr managed_provider, ulong delay_milliseconds, IntPtr native_callback) - { - var provider = (SyncSocketProvider)GCHandle.FromIntPtr(managed_provider).Target!; - var timer = new Timer(TimeSpan.FromMilliseconds(delay_milliseconds), native_callback, provider._workQueue); - return GCHandle.ToIntPtr(GCHandle.Alloc(timer)); - } - - [MonoPInvokeCallback(typeof(NativeMethods.cancel_timer))] - private static void CancelTimer(IntPtr managed_timer) - { - var handle = GCHandle.FromIntPtr(managed_timer); - try - { - ((Timer)handle.Target!).Cancel(); - } - finally - { - handle.Free(); - } - } - - [MonoPInvokeCallback(typeof(NativeMethods.websocket_connect))] - private static IntPtr WebSocketConnect(IntPtr managed_provider, IntPtr observer, Endpoint endpoint) - { - var provider = (SyncSocketProvider)GCHandle.FromIntPtr(managed_provider).Target!; - var webSocket = new ClientWebSocket(); - foreach (string? subProtocol in endpoint.protocols) - { - webSocket.Options.AddSubProtocol(subProtocol!); - } - - provider._onWebSocketConnection?.Invoke(webSocket.Options); - - var builder = new UriBuilder - { - Scheme = endpoint.is_ssl ? "wss" : "ws", - Host = endpoint.address!, - Port = endpoint.port - }; - - if (endpoint.path) - { - var pathAndQuery = ((string)endpoint.path)!.Split('?'); - builder.Path = pathAndQuery.ElementAtOrDefault(0) ?? string.Empty; - builder.Query = pathAndQuery.ElementAtOrDefault(1) ?? String.Empty; - } - - var socket = new Socket(webSocket, observer, provider._workQueue, builder.Uri); - return GCHandle.ToIntPtr(GCHandle.Alloc(socket)); - } - - [MonoPInvokeCallback(typeof(NativeMethods.websocket_write))] - private static void WebSocketWrite(IntPtr managed_socket, BinaryValue data, IntPtr native_callback) - { - var socket = (Socket)GCHandle.FromIntPtr(managed_socket).Target!; - socket.Write(data, native_callback); - } - - [MonoPInvokeCallback(typeof(NativeMethods.websocket_close))] - private static void WebSocketClose(IntPtr managed_websocket) - { - var handle = GCHandle.FromIntPtr(managed_websocket); - ((Socket)handle.Target!).Dispose(); - handle.Free(); - } - - static SyncSocketProvider() - { - NativeMethods.post_work post = PostWork; - NativeMethods.provider_dispose dispose = ProviderDispose; - NativeMethods.create_timer create_timer = CreateTimer; - NativeMethods.cancel_timer cancel_timer = CancelTimer; - NativeMethods.websocket_connect websocket_connect = WebSocketConnect; - NativeMethods.websocket_write websocket_write = WebSocketWrite; - NativeMethods.websocket_close websocket_close = WebSocketClose; - - GCHandle.Alloc(post); - GCHandle.Alloc(dispose); - GCHandle.Alloc(create_timer); - GCHandle.Alloc(cancel_timer); - GCHandle.Alloc(websocket_connect); - GCHandle.Alloc(websocket_write); - GCHandle.Alloc(websocket_close); - - NativeMethods.install_callbacks(post, dispose, create_timer, cancel_timer, websocket_connect, websocket_write, websocket_close); - } - - private struct Status(ErrorCode code, string? reason) - { - internal readonly string? Reason = reason; - internal readonly ErrorCode Code = code; - internal static readonly Status OK = new(ErrorCode.Ok, null); - } - - /// - /// Basic unit of work for the provider's event loop. - /// - private interface IWork - { - /// - /// Execute the outstanding work. - /// - void Execute(); - } - - private readonly Channel _workQueue; - private readonly Task _workThread; - private readonly Action? _onWebSocketConnection; - private readonly CancellationTokenSource _cts = new(); - - internal SyncSocketProvider(Action? onWebSocketConnection) - { - RealmLogger.Default.Log(LogLevel.Debug, "Creating SyncSocketProvider."); - _onWebSocketConnection = onWebSocketConnection; - _workQueue = Channel.CreateUnbounded(new() { SingleReader = true }); - _workThread = Task.Run(WorkThread); - } - - private partial Task WorkThread(); - - public void Dispose() - { - RealmLogger.Default.Log(LogLevel.Debug, "Destroying SyncSocketProvider."); - _workQueue.Writer.Complete(); - _cts.Cancel(); - _cts.Dispose(); - _workThread.GetAwaiter().GetResult(); - } -} diff --git a/Realm/Realm/Native/UserApiKey.cs b/Realm/Realm/Native/UserApiKey.cs deleted file mode 100644 index 3f27423d27..0000000000 --- a/Realm/Realm/Native/UserApiKey.cs +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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.Runtime.InteropServices; -using MongoDB.Bson; - -namespace Realms.Native -{ - [StructLayout(LayoutKind.Sequential)] - internal struct UserApiKey - { - internal static readonly int Size = Marshal.SizeOf(); - - private PrimitiveValue id; - - private PrimitiveValue key; - - private PrimitiveValue name; - - [MarshalAs(UnmanagedType.U1)] - public bool disabled; - - public ObjectId Id => ObjectId.Parse(id.AsString()); - - public string? Key => key.Type == RealmValueType.Null ? null : key.AsString(); - - public string Name => name.AsString(); - } -} diff --git a/Realm/Realm/Native/UserProfileField.cs b/Realm/Realm/Native/UserProfileField.cs deleted file mode 100644 index 14d2f7f5f0..0000000000 --- a/Realm/Realm/Native/UserProfileField.cs +++ /dev/null @@ -1,33 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Native -{ - internal enum UserProfileField : byte - { - Name, - Email, - PictureUrl, - FirstName, - LastName, - Gender, - Birthday, - MinAge, - MaxAge, - } -} diff --git a/Realm/Realm/Realm.cs b/Realm/Realm/Realm.cs index 1aa7bdb83a..07d549462a 100644 --- a/Realm/Realm/Realm.cs +++ b/Realm/Realm/Realm.cs @@ -33,7 +33,6 @@ using Realms.Logging; using Realms.Native; using Realms.Schema; -using Realms.Sync; using Realms.Weaving; namespace Realms @@ -108,9 +107,7 @@ public static Realm GetInstance(RealmConfigurationBase? config = null) /// Factory for asynchronously obtaining a instance. ///
/// - /// If the configuration is , the realm will be downloaded and fully - /// synchronized with the server prior to the completion of the returned Task object. - /// Otherwise this method will perform any migrations on a background thread before returning an + /// This method will perform any migrations on a background thread before returning an /// opened instance to the calling thread. /// /// @@ -140,13 +137,6 @@ public static Task GetInstanceAsync(RealmConfigurationBase? config = null public static bool Compact(RealmConfigurationBase? config = null) { using var realm = GetInstance(config); - if (config is SyncConfigurationBase) - { - // For synchronized Realms, shutdown the session, otherwise Compact will fail. - var session = realm.SyncSession; - session.CloseHandle(waitForShutdown: true); - } - return realm.SharedRealmHandle.Compact(); } @@ -166,28 +156,9 @@ public static void DeleteRealm(RealmConfigurationBase configuration) SharedRealmHandle.DeleteFiles(configuration.DatabasePath); } - /// - /// Sets the serializer to use the legacy serialization. - /// - /// - /// In version 12.0.0 it was introduced a new automatic serialization and deserialization of Realm classes when using methods - /// on , without the need to annotate classes with attributes. - /// This new serialization changed the default serializer for various types ( for instance), so - /// if you need to call this method if you prefer to use the old serialization. - /// Please remember to call this method before any kind of serialization is needed, otherwise it is not guaranteed to work as expected. - /// - [Obsolete("It is recommended to use new serialization.")] - public static void SetLegacySerialization() - { - SerializationHelper.SetLegacySerialization(); - } - #endregion static - private WeakReference? _subscriptionRef; - private State _state; - private SessionProvider? _sessionProvider; private Transaction? _activeTransaction; internal readonly SharedRealmHandle SharedRealmHandle; @@ -239,74 +210,6 @@ public bool IsInTransaction /// The Realm's configuration. public RealmConfigurationBase Config { get; } - /// - /// Gets the for this . - /// - /// - /// Thrown if the Realm has not been opened with a or - /// . - /// - /// - /// The that is responsible for synchronizing with MongoDB Atlas - /// if the Realm instance was created with a or - /// . If this is a local or in-memory Realm, a - /// will be thrown. - /// - public Session SyncSession - { - get - { - ThrowIfDisposed(); - - if (Config is not SyncConfigurationBase) - { - throw new NotSupportedException("Realm.SyncSession is only valid for synchronized Realms (i.e. ones that are opened with FlexibleSyncConfiguration or PartitionSyncConfiguration)."); - } - - _sessionProvider ??= new SessionProvider(SharedRealmHandle); - - return _sessionProvider.GetSession(); - } - } - - /// - /// Gets the representing the active subscriptions for this . - /// - /// Thrown if the Realm has not been opened with a . - /// - /// The containing the query subscriptions that the server is using to decide which objects to - /// synchronize with the local . If the Realm was not created with a , - /// this will throw a . - /// - public SubscriptionSet Subscriptions - { - get - { - ThrowIfDisposed(); - - if (Config is not FlexibleSyncConfiguration) - { - throw new NotSupportedException("Realm.Subscriptions is only valid for flexible sync Realms (i.e. ones that are opened with FlexibleSyncConfiguration)."); - } - - // If the last subscription ref is alive and its version matches the current subscription - // version, we return it. Otherwise, we create a new set and replace the existing one. - if (_subscriptionRef != null && _subscriptionRef.TryGetTarget(out var existingSet)) - { - var currentVersion = SharedRealmHandle.GetSubscriptionsVersion(); - if (existingSet.Version >= currentVersion) - { - return existingSet; - } - } - - var handle = SharedRealmHandle.GetSubscriptions(); - var set = new SubscriptionSet(handle); - _subscriptionRef = new WeakReference(set); - return set; - } - } - internal Realm(SharedRealmHandle sharedRealmHandle, RealmConfigurationBase config, RealmSchema schema, RealmMetadata? metadata = null, bool isInMigration = false) { Config = config; @@ -611,75 +514,6 @@ public void Add(IEnumerable objs, bool update = false) } } - /// - /// This will start managing an which has been created as a standalone object. - /// - /// - /// The Type T must not only be a but also have been processed by the Fody weaver, - /// so it has persistent properties. - /// - /// Must be a standalone , null not allowed. - /// - /// If you invoke this when there is no write active on the . - /// - /// - /// You can't manage an object with more than one . - /// - /// - /// If the object is already managed by this , this method does nothing. - /// This method modifies the object in-place, - /// meaning that after it has run, will be managed. - /// Once an becomes managed dereferencing any property - /// of the original reference throws an exception. - /// - public void Add(T obj) - where T : IAsymmetricObject - { - ThrowIfDisposed(); - Argument.NotNull(obj, nameof(obj)); - Argument.Ensure(!obj.IsManaged, $"{nameof(obj)} must not be already managed by a Realm.", nameof(obj)); - - AddInternal(obj, obj.GetType(), update: false); - } - - /// - /// Add a collection of standalone AsymmetricObjects to this . - /// - /// - /// The Type T must not only be a but also have been processed by the Fody weaver, - /// so it has persistent properties. - /// - /// A collection of instances that will be added to this . - /// - /// If you invoke this when there is no write active on the . - /// - /// - /// You can't manage an object with more than one . - /// - /// - /// If the collection contains items that are already managed by this , they will be ignored. - /// This method modifies the objects in-place, meaning that after it has run, all items in the collection will be managed. - /// Once an becomes managed and the transaction is committed, - /// dereferencing any property of the original reference throw an exception. - /// Hence, none of the properties of the elements in the collection can be dereferenced anymore after the transaction. - /// - public void Add(IEnumerable objs) - where T : IAsymmetricObject - { - ThrowIfDisposed(); - Argument.NotNull(objs, nameof(objs)); - foreach (var obj in objs) - { - Argument.Ensure(obj != null, $"{nameof(objs)} must not contain null values.", nameof(objs)); - Argument.Ensure(!obj.IsManaged, $"{nameof(objs)} must not contain already managed objects by a Realm.", nameof(objs)); - } - - foreach (var obj in objs) - { - AddInternal(obj, obj.GetType(), update: false); - } - } - internal void ManageEmbedded(IEmbeddedObject obj, ObjectHandle handle) { var objectType = obj.GetType(); @@ -1336,33 +1170,12 @@ public void RemoveAll() /// 1. The destination file cannot already exist. /// 2. When using a local Realm and this is called from within a transaction it writes the current data, /// and not the data as it was when the last transaction was committed. - /// 3. When using Sync, it is required that all local changes are synchronized with the server before the copy can be written. - /// This is to be sure that the file can be used as a starting point for a newly installed application. - /// The function will throw if there are pending uploads. - /// 4. Writing a copy to a flexible sync realm is not supported unless flexible sync is already enabled. - /// 5 Changing from flexible sync sync to partition based sync is not supported. - /// 6. Changing the partition to synchronize on is not supported. /// /// Configuration, specifying the path and optionally the encryption key for the copy. public void WriteCopy(RealmConfigurationBase config) { Argument.NotNull(config, nameof(config)); - if (config is FlexibleSyncConfiguration && Config is not FlexibleSyncConfiguration) - { - throw new NotSupportedException("Writing a copy to a flexible sync realm is not supported unless flexible sync is already enabled"); - } - - if (config is PartitionSyncConfiguration && Config is FlexibleSyncConfiguration) - { - throw new NotSupportedException("Changing from flexible sync sync to partition based sync is not supported when writing a Realm copy."); - } - - if (Config is PartitionSyncConfiguration originalConfig && config is PartitionSyncConfiguration copiedConfig && originalConfig.Partition != copiedConfig.Partition) - { - throw new NotSupportedException($"Changing the partition to synchronize on is not supported when writing a Realm copy. Original partition: {originalConfig.Partition}, passed partition: {copiedConfig.Partition}"); - } - SharedRealmHandle.WriteCopy(config); } @@ -1388,47 +1201,6 @@ internal void ExecuteOutsideTransaction(Action action) #endregion Transactions - internal class SessionProvider - { - private readonly SharedRealmHandle _sharedRealmHandle; - private WeakReference? _weakSessionRef; - private Session? _strongSessionRef; - - public SessionProvider(SharedRealmHandle sharedRealmHandle) - { - _sharedRealmHandle = sharedRealmHandle; - } - - public Session GetSession() - { - if(_strongSessionRef?.IsClosed == false) - { - return _strongSessionRef; - } - - if (_weakSessionRef?.TryGetTarget(out var targetSession) == true && !targetSession.IsClosed) - { - return targetSession; - } - - var session = new Session(_sharedRealmHandle.GetSession(), OnSessionSubscribed, OnSessionUnsubscribed); - - _weakSessionRef = new WeakReference(session); - - return session; - } - - private void OnSessionSubscribed(Session session) - { - _strongSessionRef = session; - } - - private void OnSessionUnsubscribed() - { - _strongSessionRef = null; - } - } - internal class RealmMetadata { private readonly Dictionary stringToRealmObjectMetadataDict; @@ -1828,7 +1600,6 @@ public IQueryable All(string className) Argument.Ensure(_realm.Metadata.TryGetValue(className, out var metadata), $"The class {className} is not in the limited set of classes for this realm", nameof(className)); Argument.Ensure(metadata.Schema.BaseType != ObjectSchema.ObjectType.EmbeddedObject, $"The class {className} represents an embedded object and thus cannot be queried directly.", nameof(className)); - Argument.Ensure(metadata.Schema.BaseType != ObjectSchema.ObjectType.AsymmetricObject, $"The class {className} represents an asymmetric object and thus cannot be queried.", nameof(className)); return new RealmResults(_realm, metadata); } diff --git a/Realm/Realm/Realm.csproj b/Realm/Realm/Realm.csproj index 93e7caa304..a5c7743deb 100644 --- a/Realm/Realm/Realm.csproj +++ b/Realm/Realm/Realm.csproj @@ -42,7 +42,6 @@ false - false diff --git a/Realm/Realm/Schema/ObjectSchema.cs b/Realm/Realm/Schema/ObjectSchema.cs index ac45931e91..444cdea49b 100644 --- a/Realm/Realm/Schema/ObjectSchema.cs +++ b/Realm/Realm/Schema/ObjectSchema.cs @@ -50,11 +50,6 @@ public enum ObjectType : byte /// The value represents a schema type. /// EmbeddedObject = 1, - - /// - /// The value represents a schema type. - /// - AsymmetricObject = 2, } private static readonly ConcurrentDictionary _cache = new(); @@ -209,8 +204,8 @@ public Builder(string name, ObjectType schemaType = ObjectType.RealmObject) /// provided . /// /// - /// The that will be used to populate the builder. It must be a , - /// an , or an inheritor. + /// The that will be used to populate the builder. It must be a or + /// an inheritor. /// /// /// If you want to use strongly typed API, such as Realm.Add<T> or @@ -253,8 +248,8 @@ public Builder(string name, ObjectType schemaType = ObjectType.RealmObject) public Builder(Type type) { Argument.NotNull(type, nameof(type)); - Argument.Ensure(type.IsRealmObject() || type.IsEmbeddedObject() || type.IsAsymmetricObject(), - $"The class {type.FullName} must descend directly from either RealmObject, EmbeddedObject, or AsymmetricObject", nameof(type)); + Argument.Ensure(type.IsRealmObject() || type.IsEmbeddedObject(), + $"The class {type.FullName} must descend directly from either RealmObject or EmbeddedObject", nameof(type)); RealmSchemaType = type.GetRealmSchemaType(); diff --git a/Realm/Realm/Schema/RealmSchema.cs b/Realm/Realm/Schema/RealmSchema.cs index 4aa37d4362..d5cba540ba 100644 --- a/Realm/Realm/Schema/RealmSchema.cs +++ b/Realm/Realm/Schema/RealmSchema.cs @@ -32,7 +32,7 @@ namespace Realms.Schema /// dynamically, by evaluating a Realm from disk. To construct a new instance, use the /// RealmSchema.Builder API. ///
- /// By default this will be all the s, s and s + /// By default, this will be all the s and s /// in all your assemblies. Unless you restrict with . Just because a given class may /// be stored in a Realm doesn't imply much overhead. There will be a small amount of metadata but objects only start to /// take up space once written. @@ -77,9 +77,9 @@ public static void AddDefaultTypes(IEnumerable types) foreach (var type in types) { - if (!type.IsRealmObject() && !type.IsEmbeddedObject() && !type.IsAsymmetricObject()) + if (!type.IsRealmObject() && !type.IsEmbeddedObject()) { - throw new ArgumentException($"The type {type.FullName} must inherit directly from RealmObject, AsymmetricObject or EmbeddedObject to be used in the Realm schema."); + throw new ArgumentException($"The type {type.FullName} must inherit directly from RealmObject or EmbeddedObject to be used in the Realm schema."); } if (_defaultTypes.Add(type) && diff --git a/Realm/Realm/Sync/ApiKey.cs b/Realm/Realm/Sync/ApiKey.cs deleted file mode 100644 index 3e3cf59ea2..0000000000 --- a/Realm/Realm/Sync/ApiKey.cs +++ /dev/null @@ -1,86 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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 MongoDB.Bson; -using Realms.Native; - -namespace Realms.Sync -{ - /// - /// A class representing an API key for a . It can be used to represent the user when logging in - /// instead of their regular credentials. These keys are created or fetched through . - ///
- /// An API key's is only available when the key is created and cannot be obtained after that. - /// This means that it's the caller's responsibility to safely store an API key's value upon creation. - ///
- /// API Key Authentication Docs - public class ApiKey - { - /// - /// Gets the unique identifier for this key. - /// - /// The id uniquely identifying the key. - [Preserve] - public ObjectId Id { get; } - - /// - /// Gets the name of the key. - /// - /// The friendly name of the key, specified when calling . - [Preserve] - public string Name { get; } - - /// - /// Gets the value for the key. This is only returned when the key is created. After that, it will always be null. - /// - /// The value of the key that needs to be provided when constructing . - [Preserve] - public string? Value { get; } - - /// - /// Gets a value indicating whether or not this key is currently enabled. - /// - /// true if the key is enabled; false otherwise. - [Preserve] - public bool IsEnabled { get; } - - /// - public override bool Equals(object? obj) => (obj is ApiKey key) && key.Id == Id; - - /// - /// Gets the hash code. - /// - /// The hash code. - public override int GetHashCode() => Id.GetHashCode(); - - /// - /// Returns a string representation of the value. - /// - /// A string representation of the value. - public override string ToString() => $"ApiKey {Name} ({Id})"; - - [Preserve] - internal ApiKey(UserApiKey nativeKey) - { - Id = nativeKey.Id; - Name = nativeKey.Name; - Value = nativeKey.Key; - IsEnabled = !nativeKey.disabled; - } - } -} diff --git a/Realm/Realm/Sync/App.cs b/Realm/Realm/Sync/App.cs deleted file mode 100644 index 6e8aff0f81..0000000000 --- a/Realm/Realm/Sync/App.cs +++ /dev/null @@ -1,521 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net.Http; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Realms.Helpers; -using Realms.Native; - -namespace Realms.Sync -{ - /// - /// An is the main client-side entry point for interacting with a Atlas App Services application. - ///

- /// The App can be used to: - ///
- /// - /// - /// Register uses and perform various user-related operations through authentication providers (e.g. , ). - /// - /// - /// Synchronize data between the local device and a remote Realm App with Synchronized Realms (using ). - /// - /// - /// Invoke Realm App functions with Functions (using ). - /// - /// - /// Access remote data from MongoDB databases with a (using ). - /// - /// - ///
- /// To create an app that is linked with a remote Realm App initialize Realm and configure the App as shown below: - /// - /// var appConfig = new AppConfiguration("my-realm-app-id"); - /// var app = new App(appConfig); - /// - /// After configuring the App you can start managing users, configure Synchronized Realms, call remote Realm Functions, and access remote data through Mongo Collections. - ///
- /// To register a new user and/or login with an existing user do as shown below: - /// - /// await app.EmailPassword.RegisterUserAsync("foo@bar.com", "password"); - /// // Login with existing user - /// var user = app.LoginAsync(Credentials.EmailPassword("foo@bar.com", "password"); - /// - /// With an authorized user you can synchronize data between the local device and the remote Realm App by opening a Realm with a as indicated below: - /// - /// var syncConfig = new PartitionSyncConfiguration("some-partition-value", user); - /// using var realm = await Realm.GetInstanceAsync(syncConfig); - /// - /// realm.Write(() => - /// { - /// realm.Add(...); - /// }); - /// - /// await realm.GetSession().WaitForUploadAsync(); - /// - /// You can call remote Realm functions as shown below: - /// - /// var result = await user.Functions.CallAsync<int>("sum", 1, 2, 3, 4, 5); - /// - /// And access collections from the remote Realm App as shown here: - /// - /// var client = user.GetMongoClient("atlas-service"); - /// var db = client.GetDatabase("my-db"); - /// var collection = db.GetCollection("foos"); - /// var foosCount = await collection.CountAsync(); - /// - ///
- public class App - { - internal readonly AppHandle Handle; - - /// - /// Gets a instance that exposes API for interacting with the synchronization client for this . - /// - /// A instance scoped to this . - public SyncClient Sync => new(this); - - /// - /// Gets a instance that exposes functionality related to users either being created or logged in using - /// the provider. - /// - /// An instance scoped to this . - public EmailPasswordClient EmailPasswordAuth => new(this); - - /// - /// Gets the currently user. If none exists, null is returned. - /// - /// Valid user or null to indicate nobody logged in. - public User? CurrentUser => Handle.TryGetCurrentUser(out var userHandle) ? new User(userHandle, this) : null; - - /// - /// Gets all currently logged in users. - /// - /// An array of valid logged in users. - public User[] AllUsers => Handle.GetAllLoggedInUsers() - .Select(handle => new User(handle, this)) - .ToArray(); - - /// - /// Gets the root folder relative to which all local data for this application will be stored. This data includes - /// metadata for users and synchronized Realms. - /// - /// The app's base path. - /// - public string BaseFilePath => Handle.GetBaseFilePath(); - - /// - /// Gets the base url for this Realm application. - /// - /// The app's base url. - /// - public Uri BaseUri => Handle.GetBaseUri(); - - /// - /// Gets the unique app id that identifies the Realm application. - /// - /// The Atlas App Services App's id. - public string Id => Handle.GetId(); - - internal App(AppHandle handle) - { - Handle = handle; - } - - /// - /// A factory method for creating an app with a particular . - /// - /// The , specifying key parameters for the app behavior. - /// An instance can now be used to login users, call functions, or open synchronized Realms. - public static App Create(AppConfiguration config) - { - Argument.NotNull(config, nameof(config)); - var syncTimeouts = config.SyncTimeoutOptions; - - if (config.MetadataPersistenceMode.HasValue) - { - if (config.MetadataPersistenceMode == MetadataPersistenceMode.Encrypted && config.MetadataEncryptionKey == null) - { - throw new ArgumentException($"{nameof(AppConfiguration.MetadataEncryptionKey)} must be set when {nameof(AppConfiguration.MetadataPersistenceMode)} is set to {nameof(MetadataPersistenceMode.Encrypted)}."); - } - - if (config.MetadataPersistenceMode != MetadataPersistenceMode.Encrypted && config.MetadataEncryptionKey != null) - { - throw new ArgumentException($"{nameof(AppConfiguration.MetadataPersistenceMode)} must be set to {nameof(MetadataPersistenceMode.Encrypted)} when {nameof(AppConfiguration.MetadataEncryptionKey)} is set."); - } - } - - var httpClient = config.HttpClientHandler is null ? new HttpClient() : new HttpClient(config.HttpClientHandler); - var clientHandle = GCHandle.Alloc(httpClient); - var nativeConfig = new Native.AppConfiguration - { - AppId = config.AppId, - BaseFilePath = config.BaseFilePath, - BaseUrl = config.BaseUri.ToString().TrimEnd('/'), - MetadataPersistence = config.MetadataPersistenceMode, - default_request_timeout_ms = (ulong)config.DefaultRequestTimeout.TotalMilliseconds, - managed_http_client = GCHandle.ToIntPtr(clientHandle), - sync_timeout_options = new Native.SyncTimeoutOptions - { - sync_connection_linger_time_ms = (ulong)syncTimeouts.ConnectionLingerTime.TotalMilliseconds, - sync_connect_timeout_ms = (ulong)syncTimeouts.ConnectTimeout.TotalMilliseconds, - sync_fast_reconnect_limit = (ulong)syncTimeouts.FastReconnectLimit.TotalMilliseconds, - sync_ping_keep_alive_period_ms = (ulong)syncTimeouts.PingKeepAlivePeriod.TotalMilliseconds, - sync_pong_keep_alive_timeout_ms = (ulong)syncTimeouts.PongKeepAliveTimeout.TotalMilliseconds, - reconnect_backoff_options = new Native.ReconnectBackoffOptions - { - max_resumption_delay_interval_ms = (ulong)syncTimeouts.ReconnectBackoffOptions.MaxReconnectDelayInterval.TotalMilliseconds, - resumption_delay_interval_ms = (ulong)syncTimeouts.ReconnectBackoffOptions.ReconnectDelayInterval.TotalMilliseconds, - resumption_delay_backoff_multiplier = syncTimeouts.ReconnectBackoffOptions.ReconnectDelayBackoffMultiplier, - delay_jitter_divisor = syncTimeouts.ReconnectBackoffOptions.DelayJitterDivisor - } - }, - use_cache = config.UseAppCache, - }; - - if (config.UseManagedWebSockets) - { - var provider = new SyncSocketProvider(config.OnSyncWebSocketConnection); - nativeConfig.managed_websocket_provider = GCHandle.ToIntPtr(GCHandle.Alloc(provider)); - } - else if (config.OnSyncWebSocketConnection is not null) - { - throw new ArgumentException($"{nameof(AppConfiguration.OnSyncWebSocketConnection)} cannot be used unless {nameof(AppConfiguration.UseManagedWebSockets)} is enabled."); - } - - var handle = AppHandle.CreateApp(nativeConfig, config.MetadataEncryptionKey); - return new App(handle); - } - - /// - /// A factory method for creating an app with a particular app Id. - /// - /// - /// This is a convenience method that creates an with the default parameters and the provided - /// and invokes . - /// - /// The application id of the Atlas App Services Application. - /// An instance can now be used to login users, call functions, or open synchronized Realms. - public static App Create(string appId) => Create(new AppConfiguration(appId)); - - /// - /// Logs in as a user with the given credentials associated with an authentication provider. - /// - /// - /// The last logged in user will be saved as . If there was already a current user, - /// that user is still logged in and can be found in the list returned by . It is also - /// possible to switch between which user is considered the current user by using . - /// - /// The representing the type of login. - /// - /// An awaitable that represents the asynchronous LogIn operation. - public async Task LogInAsync(Credentials credentials) - { - Argument.NotNull(credentials, nameof(credentials)); - - var handle = await Handle.LogInAsync(credentials.ToNative()); - - return new User(handle, this); - } - - /// - /// Switches the to the one specified in . - /// - /// The new current user. - public void SwitchUser(User user) - { - Argument.NotNull(user, nameof(user)); - - Handle.SwitchUser(user.Handle); - } - - /// - /// Removes a user and their local data from the device. If the user is logged in, they will be logged out in the process. - /// - /// - /// This is client operation and will not delete any data stored on the server for that user. - /// - /// The user to log out and remove. - /// - /// An awaitable that represents the asynchronous RemoveUser operation. Successful completion indicates that the user has been logged out, - /// their local data - removed, and the user's - revoked on the server. - /// - public Task RemoveUserAsync(User user) - { - Argument.NotNull(user, nameof(user)); - - return Handle.RemoveAsync(user.Handle); - } - - /// - /// Deletes a user from the server. The user is also removed from the device together with their local data. If the user is logged in, they will be logged out in the process. - /// - /// The user to remove from the server. - /// - /// An awaitable that represents the asynchronous deletion operation. - /// Successful completion indicates that the user has been removed, logged out and their local data has been removed. - /// - public Task DeleteUserFromServerAsync(User user) - { - Argument.NotNull(user, nameof(user)); - - return Handle.DeleteUserAsync(user.Handle); - } - - /// - /// Temporarily overrides the value from - /// with a new value used for communicating with the server. - /// - /// - /// The new uri that will be used for communicating with the server. If set to null, the base uri will - /// be reset to its default value. - /// - /// An awaitable that represents the asynchronous operation. - /// - /// The App will revert to using the value in [AppConfiguration] when it is restarted. - ///
- /// This API must be called after sync sessions have been manually stopped and at a point - /// where the server at is reachable. Once the base uri has been - /// updated, sync sessions should be resumed and the user needs to reauthenticate. - ///
- /// This API is experimental and subject to change without a major version increase. - ///
- [Experimental("Rlm001", UrlFormat = "www.mongodb.com/docs/atlas/app-services/edge-server/connect/#roaming-between-edge-servers")] - public Task UpdateBaseUriAsync(Uri? newUri) => Handle.UpdateBaseUriAsync(newUri); - - /// - public override bool Equals(object? obj) - { - if (obj is not App app) - { - return false; - } - - return Handle.IsSameInstance(app.Handle); - } - - /// - public override int GetHashCode() => Id.GetHashCode(); - - /// - /// Determines whether two instances are equal. - /// - /// The first app to compare. - /// The second app to compare. - /// true if the two instances are equal; false otherwise. - public static bool operator ==(App? app1, App? app2) - { - if (app1 is null || app2 is null) - { - return app1 is null && app2 is null; - } - - return app1.Equals(app2); - } - - /// - /// Determines whether two instances are different. - /// - /// The first app to compare. - /// The second app to compare. - /// true if the two instances are different; false otherwise. - public static bool operator !=(App? app1, App? app2) => !(app1 == app2); - - /// - /// A sync manager, handling synchronization of local Realm with MongoDB Atlas. It is always scoped to a - /// particular app and can only be accessed via . - /// - public readonly struct SyncClient - { - private readonly App _app; - - internal SyncClient(App app) - { - _app = app; - } - - /// - /// Attempt to reconnect all Sync sessions for the app. - /// - /// - /// Realm will automatically detect when a device gets connectivity after being offline and resume syncing. - /// However, some of these checks are performed using incremental backoff, which means that there are cases - /// when automatic reconnection doesn't happen immediately. In those cases, it can be beneficial to call - /// this method manually, which will force all sessions to attempt to reconnect and in the process, reset - /// any timers, that are used for incremental backoff. - /// - public void Reconnect() - { - _app.Handle.Reconnect(); - } - } - - /// - /// A class, encapsulating functionality for users, logged in with the provider. - /// It is always scoped to a particular app and can only be accessed via . - /// - public readonly struct EmailPasswordClient - { - private readonly App _app; - - internal EmailPasswordClient(App app) - { - _app = app; - } - - /// - /// Registers a new user with the given email and password. - /// - /// - /// The email to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for - /// the confirmation email. - /// - /// The password to associate with the email. The password must be between 6 and 128 characters long. - /// - /// An awaitable representing the asynchronous RegisterUser operation. Successful completion indicates that the user has been - /// created on the server and can now be logged in calling with . - /// - public Task RegisterUserAsync(string email, string password) - { - Argument.NotNullOrEmpty(email, nameof(email)); - Argument.NotNullOrEmpty(password, nameof(password)); - - return _app.Handle.EmailPassword.RegisterUserAsync(email, password); - } - - /// - /// Confirms a user with the given token and token id. These are typically included in the email the user received - /// after registering. - /// - /// - /// While confirmation typically happens in a web app, mobile applications that have deep linking enabled can intercept the url - /// and complete the user confirmation flow in the app itself. - /// - /// The confirmation token. - /// The id of the confirmation token. - /// - /// An awaitable representing the asynchronous ConfirmUser operation. Successful completion indicates that the user has been - /// confirmed on the server. - /// - public Task ConfirmUserAsync(string token, string tokenId) - { - Argument.NotNullOrEmpty(token, nameof(token)); - Argument.NotNullOrEmpty(tokenId, nameof(tokenId)); - - return _app.Handle.EmailPassword.ConfirmUserAsync(token, tokenId); - } - - /// - /// Resends the confirmation email for a user to the given email. - /// - /// The email of the user. - /// - /// An awaitable representing the asynchronous request to the server that a confirmation email is sent. Successful - /// completion indicates that the server has accepted the request and will send a confirmation email to the specified address - /// if a user with that email exists. - /// - public Task ResendConfirmationEmailAsync(string email) - { - Argument.NotNullOrEmpty(email, nameof(email)); - - return _app.Handle.EmailPassword.ResendConfirmationEmailAsync(email); - } - - /// - /// Rerun the custom confirmation function for the given mail. - /// - /// The email of the user. - /// - /// An awaitable representing the asynchronous request to the server that the custom confirmation function is run again. Successful - /// completion indicates that the user has been confirmed on the server. - /// - public Task RetryCustomConfirmationAsync(string email) - { - Argument.NotNullOrEmpty(email, nameof(email)); - - return _app.Handle.EmailPassword.RetryCustomConfirmationAsync(email); - } - - /// - /// Sends a password reset email to the specified address. - /// - /// the email of the user. - /// - /// An awaitable representing the asynchronous request to the server that a reset password email is sent. Successful - /// completion indicates that the server has accepted the request and will send a password reset email to the specified - /// address if a user with that email exists. - /// - public Task SendResetPasswordEmailAsync(string email) - { - Argument.NotNullOrEmpty(email, nameof(email)); - - return _app.Handle.EmailPassword.SendResetPasswordEmailAsync(email); - } - - /// - /// Completes the reset password flow by providing the desired new password. - /// - /// - /// While the reset password flow is typically completed in the web app, mobile applications that have deep linking enabled can intercept the url - /// and complete the password reset flow in the app itself. - /// - /// The new password for the user. - /// The password reset token that was sent to the user's email address. - /// The password reset token id that was sent together with the to the user's email address. - /// - /// An awaitable representing the asynchronous request that a user's password is reset. Successful completion indicates that the user's password has been - /// reset and they can now use the new password to create credentials and call to login. - /// - public Task ResetPasswordAsync(string password, string token, string tokenId) - { - Argument.NotNullOrEmpty(token, nameof(token)); - Argument.NotNullOrEmpty(tokenId, nameof(tokenId)); - Argument.NotNullOrEmpty(password, nameof(password)); - - return _app.Handle.EmailPassword.ResetPasswordAsync(password, token, tokenId); - } - - /// - /// Calls the reset password function, configured on the server. - /// - /// The email of the user. - /// The new password of the user. - /// - /// Any additional arguments provided to the reset function. All arguments must be serializable to JSON - /// compatible values. - /// - /// - /// An awaitable representing the asynchronous request to call a password reset function. Successful completion indicates - /// that the user's password has been change and they can now use the new password to create - /// credentials and call to login. - /// - public Task CallResetPasswordFunctionAsync(string email, string password, params object?[] functionArgs) - { - Argument.NotNullOrEmpty(email, nameof(email)); - Argument.NotNullOrEmpty(password, nameof(password)); - Argument.NotNull(functionArgs, nameof(functionArgs)); - - return _app.Handle.EmailPassword.CallResetPasswordFunctionAsync(email, password, functionArgs.ToNativeJson()); - } - } - } -} diff --git a/Realm/Realm/Sync/AppConfiguration.cs b/Realm/Realm/Sync/AppConfiguration.cs deleted file mode 100644 index 2340eeb7ce..0000000000 --- a/Realm/Realm/Sync/AppConfiguration.cs +++ /dev/null @@ -1,183 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Net.Http; -using System.Net.WebSockets; -using Realms.Helpers; - -namespace Realms.Sync -{ - /// - /// A class exposing configuration options for a . - /// - public class AppConfiguration - { - private string? _baseFilePath; - - private byte[]? _metadataEncryptionKey; - - /// - /// Gets the unique app id that identifies the Realm application. - /// - /// The Atlas App Services App's id. - public string AppId { get; } - - /// - /// Gets or sets the root folder relative to which all local data for this application will be stored. This data includes - /// metadata for users and synchronized Realms. - /// - /// The app's base path. - public string BaseFilePath - { - get => _baseFilePath ?? InteropConfig.GetDefaultStorageFolder("Could not determine a writable folder to store app files (such as metadata and Realm files). When constructing the app, set AppConfiguration.BaseFilePath to an absolute path where the app is allowed to write."); - set - { - Argument.NotNull(value, nameof(value)); - _baseFilePath = value; - } - } - - /// - /// Gets or sets the base url for this Realm application. - /// - /// - /// This only needs to be set if for some reason your application isn't hosted on services.cloud.mongodb.com. - /// This is typically the case when synchronizing with an edge server. - /// - /// The app's base url. - public Uri BaseUri { get; set; } = AppHandle.DefaultBaseUri; - - /// - /// Gets or sets the local app's name. - /// - /// The friendly name identifying the current client application. - [Obsolete("This property has no effect and will be removed in a future version.")] - public string? LocalAppName { get; set; } - - /// - /// Gets or sets the local app's version. - /// - /// The client application's version. - /// - [Obsolete("This property has no effect and will be removed in a future version.")] - public string? LocalAppVersion { get; set; } - - /// - /// Gets or sets the persistence mode for user metadata on this device. - /// - /// - /// The default value is for iOS devices and - /// for all other platforms. On iOS we integrate with the system keychain to generate and store a random encryption key the first time the app - /// is launched. On other platforms, needs to be set if is - /// specified. - /// - /// The user metadata persistence mode. - public MetadataPersistenceMode? MetadataPersistenceMode { get; set; } - - /// - /// Gets or sets the encryption key for user metadata on this device. - /// - /// - /// This will not change the encryption key for individual Realms. This should still be set in - /// when opening the . - /// - /// The user metadata encryption key. - public byte[]? MetadataEncryptionKey - { - get => _metadataEncryptionKey; - set - { - if (value != null && value.Length != 64) - { - throw new FormatException("EncryptionKey must be 64 bytes"); - } - - _metadataEncryptionKey = value; - } - } - - /// - /// Gets or sets the default request timeout for HTTP requests to MongoDB Atlas. Default is 1 minute. - /// - /// The default HTTP request timeout. - public TimeSpan DefaultRequestTimeout { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Gets or sets the that will be used - /// for the http requests to MongoDB Atlas. - /// - /// The http client handler that configures things like certificates and proxy settings. - /// - /// You can use this to override the default http client handler and configure settings like proxies, - /// client certificates, and cookies. While these are not required to connect to MongoDB Atlas under - /// normal circumstances, they can be useful if client devices are behind a corporate firewall or use - /// a more complex networking setup. - /// - public HttpMessageHandler? HttpClientHandler { get; set; } - - /// - /// Gets or sets the delegate that will be used to configure outgoing WebSocket connections to MongoDB Atlas. - /// - /// The delegate that will be used to configure outgoing WebSocket connections. - /// - /// You can use this to modify the default behavior. Normally this is not - /// required to connect to MongoDB Atlas, but it can be useful if client devices are behind a corporate firewall - /// or use a more complex networking setup. Requires that is set to true. - /// - public Action? OnSyncWebSocketConnection { get; set; } - - /// - /// Gets or sets a value indicating whether the .NET WebSocket client or the built-in Realm WebSocket client - /// will be used for Sync traffic. - /// - /// true to use ; false to use the built-in Realm WebSocket client. - /// The default value is false, but this will change in a future version. - /// - public bool UseManagedWebSockets { get; set; } - - /// - /// Gets or sets the options for the assorted types of connection timeouts for sync connections - /// opened for this app. - /// - /// The sync timeout options applied to synchronized Realms. - public SyncTimeoutOptions SyncTimeoutOptions { get; set; } = new(); - - /// - /// Gets or sets a value indicating whether to cache app instances created with this configuration. - /// - /// - /// When an app is created using , the default behavior is - /// to get or add the instance from a cache keyed on the app id. This has certain - /// performance benefits when calling multiple times. - /// - /// true if the app should be cached; false otherwise. Default value is true. - public bool UseAppCache { get; set; } = true; - - /// - /// Initializes a new instance of the class with the specified . - /// - /// The Atlas App Services App id. - public AppConfiguration(string appId) - { - Argument.NotNullOrEmpty(appId, nameof(appId)); - - AppId = appId; - } - } -} diff --git a/Realm/Realm/Sync/ClientResyncMode.cs b/Realm/Realm/Sync/ClientResyncMode.cs deleted file mode 100644 index 1c4e928803..0000000000 --- a/Realm/Realm/Sync/ClientResyncMode.cs +++ /dev/null @@ -1,28 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - internal enum ClientResyncMode : byte - { - Manual = 0, - Discard = 1, - Recover = 2, - RecoverOrDiscard = 3, - } -} diff --git a/Realm/Realm/Sync/ConnectionState.cs b/Realm/Realm/Sync/ConnectionState.cs deleted file mode 100644 index f6ea4b8682..0000000000 --- a/Realm/Realm/Sync/ConnectionState.cs +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The current connection state of a sync session object. - /// - public enum ConnectionState : byte - { - /// - /// The session is disconnected from Atlas Device Sync. - /// - Disconnected = 0, - - /// - /// The session is connecting to the Atlas Device Sync. - /// - Connecting = 1, - - /// - /// The session is connected to the Atlas Device Sync. - /// - Connected = 2 - } -} diff --git a/Realm/Realm/Sync/Credentials.cs b/Realm/Realm/Sync/Credentials.cs deleted file mode 100644 index 6e303eeca1..0000000000 --- a/Realm/Realm/Sync/Credentials.cs +++ /dev/null @@ -1,251 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using Realms.Helpers; - -namespace Realms.Sync -{ - /// - /// A class, representing the credentials used for authenticating a . - /// - public class Credentials - { - /// - /// An enum containing the possible authentication providers. These have to manually be enabled for - /// your app before they can be used. - /// - /// Authentication Providers Docs - public enum AuthProvider - { - /// - /// Mechanism for authenticating without credentials. - /// - Anonymous = 0, - - /// - /// OAuth2-based mechanism for logging in with an existing Facebook account. - /// - Facebook = 2, - - /// - /// Mechanism for logging in with an existing Google account using an auth code or Id token. - /// - Google = 3, - - /// - /// OAuth2-based mechanism for logging in with an Apple ID. - /// - Apple = 4, - - /// - /// Allow users to log in with JWT-based credentials generated by a service external to Realm. - /// - JWT = 5, - - /// - /// Mechanism for authenticating with an email and a password. - /// - EmailPassword = 6, - - /// - /// Allow users to log in with arbitrary credentials according to custom authentication logic that you define - /// on the server. - /// - Function = 7, - - /// - /// Mechanism for logging in with API keys generated by the client SDK. - /// - ApiKey = 8, - - /// - /// Mechanism for logging in with API keys generated in the server UI. - /// - [Obsolete("Use ApiKey instead.")] - ServerApiKey = 9, - - /// - /// A provider that is not among the well known provider types. This is most likely the result of the server - /// introducing a new provider type that this version of the SDK doesn't know about. - /// - Unknown = 999, - } - - /// - /// Creates credentials representing an anonymous user. - /// - /// - /// A value indicating whether anonymous users should be reused. Passing true means that multiple calls to - /// app.LoginAsync(Credentials.Anonymous()) will return the same anonymous user as long as that user hasn't - /// logged out. If is false, every time you call , - /// a new user will be created on the server. - /// - /// A Credentials that can be used to authenticate an anonymous user. - /// Anonymous Authentication Docs - public static Credentials Anonymous(bool reuseExisting = true) => new(AuthProvider.Anonymous, additionalInfo: reuseExisting.ToString().ToLower()); - - /// - /// Creates credentials representing a login using a Facebook access token. - /// - /// The OAuth 2.0 access token representing the Facebook user. - /// A Credentials that can be used to authenticate a user with Facebook. - /// Facebook Authentication Docs - public static Credentials Facebook(string accessToken) - { - Argument.NotNull(accessToken, nameof(accessToken)); - - return new Credentials(AuthProvider.Facebook, accessToken); - } - - /// - /// Creates credentials representing a login using a Google account. - /// - /// The credential representing the Google user. - /// The type of the credential. - /// A Credentials that can be used to authenticate a user with Google. - /// Google Authentication Docs - public static Credentials Google(string credential, GoogleCredentialType type) - { - Argument.NotNull(credential, nameof(credential)); - return new Credentials(AuthProvider.Google, credential, type.ToString()); - } - - /// - /// Creates credentials representing a login using an Apple ID access token. - /// - /// The OAuth 2.0 access token representing the user's Apple ID. - /// A Credentials that can be used to authenticate a user via an Apple ID. - /// Apple ID Authentication Docs - public static Credentials Apple(string accessToken) - { - Argument.NotNull(accessToken, nameof(accessToken)); - - return new Credentials(AuthProvider.Apple, accessToken); - } - - /// - /// Creates credentials representing a login using a JWT Token. - /// - /// The custom JWT token representing the user. - /// A Credentials that can be used to authenticate a user with a custom JWT Token. - /// Custom JWT Authentication Docs - public static Credentials JWT(string customToken) - { - Argument.NotNull(customToken, nameof(customToken)); - - return new Credentials(AuthProvider.JWT, customToken); - } - - /// - /// Creates credentials representing a login using an email and password. - /// - /// The user's email. - /// The user's password. - /// A Credentials that can be used to authenticate a user with their email and password. - /// - /// A user can login with email and password only after they've registered their account and verified their - /// email. To register an email/password user via the SDK, use . - /// To verify an email from the SDK, use . The email/password - /// provider can also be configured to automatically confirm users or to run a custom confirmation function upon - /// user registration. - /// - /// Email/Password Authentication Docs - public static Credentials EmailPassword(string email, string password) - { - Argument.NotNullOrEmpty(email, nameof(email)); - Argument.NotNullOrEmpty(password, nameof(password)); - - return new Credentials(AuthProvider.EmailPassword, email, password); - } - - /// - /// Creates credentials representing a login with Realm function. - /// - /// The payload that will be passed as an argument to the server function. - /// A Credentials that can be used to authenticate a user by invoking a server function. - /// - /// The payload object will be serialized and parsed when invoking the Realm function. This means that - /// unserializable values, such as references to functions or cyclic object graphs will not work. - /// Additionally, the names of the fields/properties must match exactly the names that your function - /// expects. - /// - /// Custom Function Authentication Docs - public static Credentials Function(object payload) - { - return new Credentials(AuthProvider.Function, payload.ToNativeJson()); - } - - /// - /// Creates credentials representing a login using an API key generated by a client SDK. - /// - /// The API key to use for login. - /// A Credentials that can be used to authenticate user with an API key. - /// API Key Authentication Docs - public static Credentials ApiKey(string key) - { - Argument.NotNull(key, nameof(key)); - - return new Credentials(AuthProvider.ApiKey, key); - } - - /// - /// Creates credentials representing a login using an API key generated in the server UI. - /// - /// The server API key to use for login. - /// A Credentials that can be used to authenticate user with an API key. - /// API Key Authentication Docs - [Obsolete("Use Credentials.ApiKey instead.")] - public static Credentials ServerApiKey(string serverApiKey) - { - Argument.NotNull(serverApiKey, nameof(serverApiKey)); - - return new Credentials(AuthProvider.ServerApiKey, serverApiKey); - } - - /// - /// Gets a value indicating which these Credentials are using. - /// - /// The these credentials use. - [Preserve] - public AuthProvider Provider { get; } - - [Preserve] - internal string? Token { get; } - - [Preserve] - internal string? AdditionalInfo { get; } - - private Credentials(AuthProvider provider, string? token = null, string? additionalInfo = null) - { - Provider = provider; - Token = token; - AdditionalInfo = additionalInfo; - } - - internal Native.Credentials ToNative() - { - return new Native.Credentials - { - provider = Provider, - Token = Token, - AdditionalInfo = AdditionalInfo, - }; - } - } -} diff --git a/Realm/Realm/Sync/ErrorHandling/ClientResetHandlerBase.cs b/Realm/Realm/Sync/ErrorHandling/ClientResetHandlerBase.cs deleted file mode 100644 index 2e16c16c74..0000000000 --- a/Realm/Realm/Sync/ErrorHandling/ClientResetHandlerBase.cs +++ /dev/null @@ -1,77 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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 Realms.Sync.Exceptions; - -namespace Realms.Sync.ErrorHandling -{ - /// - /// The base class for the different types of client reset handlers. The possible implementations are , - /// , and . - /// To use either of them, create a new instance and assign it to on the configuration - /// you use to open the synchronized instance. - /// - /// Client Resets - .NET SDK - public abstract class ClientResetHandlerBase - { - /// - /// Callback triggered when a Client Reset error happens in a synchronized Realm. - /// - /// - /// The specific that holds useful data to be used when trying to manually recover from a client reset. - /// - public delegate void ClientResetCallback(ClientResetException clientResetException); - - /// - /// Callback that indicates a Client Reset is about to happen. - /// - /// - /// The frozen before the reset. - /// - /// - /// The lifetime of the Realm is tied to the callback, so don't store references to the Realm or objects - /// obtained from it for use outside of the callback. If you need to preserve the state as it was, use - /// to create a backup. - /// - public delegate void BeforeResetCallback(Realm beforeFrozen); - - /// - /// Callback that indicates a Client Reset has just happened. - /// - /// - /// The frozen as it was before the reset. - /// - /// - /// The after the client reset. In order to modify this realm a write transaction needs to be started. - /// - /// - /// The lifetime of the Realm instances supplied is tied to the callback, so don't store references to - /// the Realm or objects obtained from it for use outside of the callback. If you need to preserve the - /// state as it was, use to create a backup. - /// - public delegate void AfterResetCallback(Realm beforeFrozen, Realm after); - - internal abstract ClientResyncMode ClientResetMode { get; } - - internal ClientResetCallback? ManualClientReset { get; set; } - - internal ClientResetHandlerBase() - { - } - } -} diff --git a/Realm/Realm/Sync/ErrorHandling/DiscardUnsyncedChangesHandler.cs b/Realm/Realm/Sync/ErrorHandling/DiscardUnsyncedChangesHandler.cs deleted file mode 100644 index c1a4f69290..0000000000 --- a/Realm/Realm/Sync/ErrorHandling/DiscardUnsyncedChangesHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync.ErrorHandling -{ - /// - /// A client reset strategy where all the not yet synchronized data is automatically discarded and a fresh copy of the synchronized Realm is obtained. - /// - /// - /// The freshly downloaded copy of the synchronized Realm triggers all change notifications as a write transaction is internally simulated. - /// This strategy supplies three callbacks: , , and . - /// The first two are invoked just before and after the client reset has happened, - /// while the last one will be invoked in case an error occurs during the automated process and the system needs to fallback to a manual mode. - /// The overall recommendation for using this strategy is that using the three available callbacks should only be considered when: - /// 1. the user needs to be notified (in ) of an incoming potential data loss of unsynced data - /// 2. the user needs to be notified (in ) that the reset process has completed - /// 3. advanced use cases for data-sensitive applications where the developer wants to recover in the most appropriate way the unsynced data - /// 4. backup the whole realm before the client reset happens (in ). Such backup could, for example, be used to restore the unsynced data (see 3.) - /// - /// Client Resets - .NET SDK - public sealed class DiscardUnsyncedChangesHandler : ClientResetHandlerBase - { - /// - /// Gets or sets the callback that indicates a Client Reset is about to happen. - /// - /// Callback invoked right before a Client Reset. - public BeforeResetCallback? OnBeforeReset { get; set; } - - /// - /// Gets or sets the callback that indicates a Client Reset just happened. - /// - /// Callback invoked right after a Client Reset. - public AfterResetCallback? OnAfterReset { get; set; } - - internal override ClientResyncMode ClientResetMode => ClientResyncMode.Discard; - - /// - /// Gets or sets the callback triggered when an error has occurred that makes the operation unable to complete, for example in the case of a destructive schema change. - /// - /// Callback invoked if automatic Client Reset handling fails. - public ClientResetCallback? ManualResetFallback - { - get => ManualClientReset; - set => ManualClientReset = value; - } - } -} diff --git a/Realm/Realm/Sync/ErrorHandling/ManualRecoveryHandler.cs b/Realm/Realm/Sync/ErrorHandling/ManualRecoveryHandler.cs deleted file mode 100644 index 7cd4e9c5d5..0000000000 --- a/Realm/Realm/Sync/ErrorHandling/ManualRecoveryHandler.cs +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync.ErrorHandling -{ - /// - /// A client reset strategy where the user needs to fully take care of a client reset. - /// - /// Client Resets - .NET SDK - public sealed class ManualRecoveryHandler : ClientResetHandlerBase - { - /// - /// Gets the callback to manually handle a Client Reset. - /// A Client Reset should be handled as quickly as possible as any further changes to the Realm will not be synchronized with the server and - /// must be moved manually from the backup Realm to the new one. - /// - /// Callback invoked on Client Reset. - public ClientResetCallback OnClientReset => ManualClientReset!; - - internal override ClientResyncMode ClientResetMode => ClientResyncMode.Manual; - - /// - /// Initializes a new instance of the class with the supplied client reset handler. - /// - /// - /// Callback triggered when a manual client reset happens. - /// - public ManualRecoveryHandler(ClientResetCallback onClientReset) - { - ManualClientReset = onClientReset; - } - } -} diff --git a/Realm/Realm/Sync/ErrorHandling/RecoverOrDiscardUnsyncedChangesHandler.cs b/Realm/Realm/Sync/ErrorHandling/RecoverOrDiscardUnsyncedChangesHandler.cs deleted file mode 100644 index 32a6fa51b8..0000000000 --- a/Realm/Realm/Sync/ErrorHandling/RecoverOrDiscardUnsyncedChangesHandler.cs +++ /dev/null @@ -1,89 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync.ErrorHandling -{ - /// - /// A client reset strategy that attempts to automatically recover any unsynchronized changes. - /// If that fails, this handler falls back to the discard unsynced changes strategy. - /// - /// - /// The automatic recovery fails when a client that is configured for recovery is flagged on the server "as not allowed to execute automatic recovery". - /// In this situation this strategy falls back to the discard unsynced one. - /// To reiterate what it does: it discards all the unsynced local changes and uses the latest realm that is available on the remote sync server. - /// You can read more about the automatic merge rules at Client Resets - .NET SDK. - /// The automatic recovery mechanism creates write transactions meaning that all the changes that take place - /// are properly propagated through the standard Realm's change notifications. - /// The strategy supplies four callbacks: , , - /// and . - /// , is invoked just before the client reset happens. - /// , is invoke if and only if an automatic client reset succeeded. The callback is never called - /// if the automatic client reset fails. - /// is invoked if and only if an automatic client reset failed and instead the discard unsynced one succeeded. - /// The callback is never called if the discard unsynced client reset fails. - /// is invoked whenever an error occurs in either of the recovery strategies and the system needs to fallback to a manual mode. - /// The overall recommendation for using this strategy is that using the three available callbacks should only be considered when: - /// 1. The user needs to be notified (in ) of an incoming potential data loss - /// of unsynced data as a result of a merge or a complete discard of unsynced local changes - /// 2. The user needs to be notified (in or ) - /// that the reset process has completed - /// 3. Advanced use cases for data-sensitive applications where the developer wants - /// to recover in the most appropriate way the unsynced data - /// 4. Backup the whole realm before the client reset happens (in ). - /// Such backup could, for example, be used to restore the unsynced data (see 3.) - /// - /// Client Resets - .NET SDK - public sealed class RecoverOrDiscardUnsyncedChangesHandler : ClientResetHandlerBase - { - internal override ClientResyncMode ClientResetMode => ClientResyncMode.RecoverOrDiscard; - - /// - /// Gets or sets the callback that indicates a Client Reset is about to happen. - /// - /// Callback invoked right before a Client Reset. - public BeforeResetCallback? OnBeforeReset { get; set; } - - /// - /// Gets or sets the callback that indicates that an automatic Client Reset just happened. - /// - /// Callback invoked right after a Client Reset. - public AfterResetCallback? OnAfterRecovery { get; set; } - - /// - /// Gets or sets the callback that indicates that the discard unsynced changes fallback for a Client Reset just happened. - /// - /// - /// When a Client Reset with automatic recovery is attempted but the client is not allowed to use such strategy by the server, - /// then a Client Reset is re-tried with the fallback discard unsynced changes strategy. If this second attempt succeeds, - /// the callback is called. - /// - /// Callback invoked right after a Client Reset that fell back to discard unsynced changes. - public AfterResetCallback? OnAfterDiscard { get; set; } - - /// - /// Gets or sets the callback triggered when an error has occurred that makes the operation unable to complete, - /// for example in the case of a destructive schema change. - /// - /// Callback invoked if automatic Client Reset handling fails. - public ClientResetCallback? ManualResetFallback - { - get => ManualClientReset; - set => ManualClientReset = value; - } - } -} diff --git a/Realm/Realm/Sync/ErrorHandling/RecoverUnsyncedChangesHandler.cs b/Realm/Realm/Sync/ErrorHandling/RecoverUnsyncedChangesHandler.cs deleted file mode 100644 index 289c952029..0000000000 --- a/Realm/Realm/Sync/ErrorHandling/RecoverUnsyncedChangesHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2022 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync.ErrorHandling -{ - /// - /// A client reset strategy that attempts to automatically recover any unsynchronized changes. - /// - /// - /// You can read more about the automatic merge rules at Client Resets - .NET SDK. - /// The automatic recovery mechanism creates write transactions meaning that all the changes that take place - /// are properly propagated through the standard Realm's change notifications. - /// The strategy supplies three callbacks: , and . - /// The first two are invoked just before and after the client reset has happened, while the last one is invoked - /// in case an error occurs during the automated process and the system needs to fallback to a manual mode. - /// The overall recommendation for using this strategy is that using the three available callbacks should only be considered when: - /// 1. The user needs to be notified (in ) of an incoming potential data loss - /// of unsynced data as a result of a merge or a complete discard of local changes - /// 2. The user needs to be notified (in ) that the reset process has completed - /// 3. Advanced use cases for data-sensitive applications where the developer wants - /// to recover in the most appropriate way the unsynced data - /// 4. Backup the whole realm before the client reset happens (in ). - /// Such backup could, for example, be used to restore the unsynced data (see 3.) - /// - /// Client Resets - .NET SDK - public sealed class RecoverUnsyncedChangesHandler : ClientResetHandlerBase - { - internal override ClientResyncMode ClientResetMode => ClientResyncMode.Recover; - - /// - /// Gets or sets the callback that indicates a Client Reset is about to happen. - /// - /// Callback invoked right before a Client Reset. - public BeforeResetCallback? OnBeforeReset { get; set; } - - /// - /// Gets or sets the callback that indicates a Client Reset just happened. - /// - /// Callback invoked right after a Client Reset. - public AfterResetCallback? OnAfterReset { get; set; } - - /// - /// Gets or sets the callback triggered when an error has occurred that makes the operation unable to complete, - /// for example in the case of a destructive schema change. - /// - /// Callback invoked if automatic Client Reset handling fails. - public ClientResetCallback? ManualResetFallback - { - get => ManualClientReset; - set => ManualClientReset = value; - } - } -} diff --git a/Realm/Realm/Sync/FlexibleSync/Subscription.cs b/Realm/Realm/Sync/FlexibleSync/Subscription.cs deleted file mode 100644 index 89e6261528..0000000000 --- a/Realm/Realm/Sync/FlexibleSync/Subscription.cs +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using MongoDB.Bson; - -namespace Realms.Sync -{ - /// - /// A class representing a single query subscription. The server will continuously - /// evaluate the that the app subscribed to and will send data - /// that matches it as well as remove data that no longer does. - /// - public class Subscription - { - internal ObjectId Id { get; } - - /// - /// Gets the name of the subscription if one was provided at creation time. - /// If no name was provided, then this will return null. - /// - /// The subscription's name. - public string? Name { get; } - - /// - /// Gets the type of objects this subscription refers to. - /// - /// - /// If your types are remapped using , the value - /// returned will be the mapped-to value - i.e. the one that Realm uses internally - /// rather than the name of the C# class. - /// - /// The object type for the subscription. - public string ObjectType { get; } - - /// - /// Gets the query that describes the subscription. Objects matched by the query - /// will be sent to the device by the server. - /// - /// The subscription query. - public string Query { get; } - - /// - /// Gets a value indicating when this subscription was created. - /// - /// The creation date/time of the subscription. - public DateTimeOffset CreatedAt { get; } - - /// - /// Gets a value indicating when this subscription was last updated. - /// - /// The date/time of the last update to the subscription. - public DateTimeOffset UpdatedAt { get; } - - internal Subscription(ObjectId id, string? name, string objectType, string query, DateTimeOffset createdAt, DateTimeOffset updatedAt) - { - Id = id; - Name = name; - ObjectType = objectType; - Query = query; - CreatedAt = createdAt; - UpdatedAt = updatedAt; - } - } -} diff --git a/Realm/Realm/Sync/FlexibleSync/SubscriptionOptions.cs b/Realm/Realm/Sync/FlexibleSync/SubscriptionOptions.cs deleted file mode 100644 index 5d2d14e479..0000000000 --- a/Realm/Realm/Sync/FlexibleSync/SubscriptionOptions.cs +++ /dev/null @@ -1,55 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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.Linq; - -namespace Realms.Sync; - -/// -/// A class providing various options to . -/// All the properties in this class are optional. -/// -public class SubscriptionOptions -{ - /// - /// Gets or sets name of the subscription that is being added. This will - /// be reflected in . If not specified, - /// an automatic name will be generated from the query. - /// - /// The subscription's name. - public string? Name { get; set; } - - /// - /// Gets or sets a value indicating whether the operation should update - /// an existing subscription with the same name. The default is true. - /// - /// - /// true if - /// should have UPSERT semantics, false if you need it to be strictly an INSERT. - /// - /// - /// Adding a subscription with the same name and query string is a no-op, regardless - /// of the value of . This means that if - /// is not specified, - /// will always succeed since the name is derived from the query string. If - /// is set to a non-null value and is set to false, - /// may throw an exception - /// if the subscription set contains a subscription with the same name, but a different query string. - /// - public bool UpdateExisting { get; set; } = true; -} \ No newline at end of file diff --git a/Realm/Realm/Sync/FlexibleSync/SubscriptionSet.cs b/Realm/Realm/Sync/FlexibleSync/SubscriptionSet.cs deleted file mode 100644 index 130e59d07f..0000000000 --- a/Realm/Realm/Sync/FlexibleSync/SubscriptionSet.cs +++ /dev/null @@ -1,395 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Realms.Helpers; -using Realms.Sync.Exceptions; - -namespace Realms.Sync -{ - /// - /// A collection representing the set of active subscriptions for a - /// instance. This is used in combination with to - /// declare the set of queries you want to synchronize with the server. You can access and - /// read the subscription set freely, but mutating it must happen in an - /// block. - /// - /// - /// Any changes to the subscription set will be persisted locally and be available the next - /// time the application starts up - i.e. it's not necessary to subscribe for the same query - /// every time. Updating the subscription set can be done while offline, and only the latest - /// update will be sent to the server whenever connectivity is restored. - ///
- /// It is strongly recommended that you batch updates as much as possible and request the - /// dataset your application needs upfront. Updating the set of active subscriptions for a - /// Realm is an expensive operation serverside, even if there's very little data that needs - /// downloading. - ///
- public class SubscriptionSet : IReadOnlyList - { - private SubscriptionSetHandle _handle; - - /// - /// Gets the number of elements in the collection. - /// - /// The number of elements in the collection. - public int Count => _handle.GetCount(); - - /// - /// Gets the state of the subscription set. - /// - /// The subscription set's state. - public SubscriptionSetState State => _handle.GetState(); - - internal long Version => _handle.GetVersion(); - - /// - /// Gets the error associated with this subscription set, if any. This will - /// only be non-null if is . - /// - /// - /// The that provides more details for why the subscription set - /// was rejected by the server. - /// - public Exception? Error - { - get - { - var errorMessage = _handle.GetErrorMessage(); - return errorMessage == null ? null : new SubscriptionException(errorMessage); - } - } - - /// - /// Gets the at the specified index in the set. - /// - /// The zero-based index of the element to get. - /// The at the specified index in the set. - public Subscription this[int index] - { - get - { - if (index < 0) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - return _handle.GetAtIndex(index); - } - } - - internal SubscriptionSet(SubscriptionSetHandle handle) - { - _handle = handle; - } - - /// - /// Finds a subscription by name. - /// - /// The name of the subscription. - /// - /// A instance where is equal to - /// if the subscription set contains a subscription with the provided name; - /// null otherwise. - /// - public Subscription? Find(string name) => _handle.Find(name); - - /// - /// Finds a subscription by query. - /// - /// The type of objects in the query. - /// The query describing the subscription. - /// - /// A instance where matches - /// the provided ; null if the subscription set doesn't contain - /// a match. - /// - public Subscription? Find(IQueryable query) - where T : IRealmObject - { - var results = Argument.EnsureType>(query, $"{nameof(query)} must be a query obtained by calling Realm.All.", nameof(query)); - return _handle.Find(results.ResultsHandle); - } - - /// - /// Update the subscription set and send the request to the server in the background. - /// - /// - /// Calling is a prerequisite for - /// mutating the subscription set - e.g. by calling , - /// , or . - ///
- /// Calling this may update the content of this - e.g. if another - /// was called on a background thread or if the changed. - ///
- /// If you want to wait for the server to acknowledge and send back the data that matches the updated - /// subscriptions, use . - ///
- /// - /// Action to execute, adding or removing subscriptions to this set. - /// - public void Update(Action action) - { - EnsureReadonly(); - Argument.NotNull(action, nameof(action)); - - var oldHandle = _handle; - var writeableHandle = _handle.BeginWrite(); - - // We need to set the writable handle as the current subscription set handle - // to allow performing operations that modify it. - _handle = writeableHandle; - - try - { - action(); - - // Committing the write will generate a new readonly subscription set handle that - // we need to set to _handle. - _handle = _handle.CommitWrite(); - - oldHandle.Dispose(); - writeableHandle.Dispose(); - } - catch - { - // We need to immediately unbind the subscription set handle to rollback the transaction. If we - // don't do it, it will be unbound when the Realm closes or GC collects it which is too late. - // Using .Dispose here instead of .Unbind will only schedule it for unbinding which is not what - // we want. - writeableHandle.Unbind(); - - // If an error occurs - revert to the old subscription set handle. - _handle = oldHandle; - throw; - } - } - - /// - /// Adds a query to the set of active subscriptions. The query will be joined via an OR statement - /// with any existing queries for the same type. - /// - /// The type of objects in the query results. - /// The query that will be matched on the server. - /// - /// The subscription options controlling the name and/or the type of insert that will be performed. - /// - /// - /// Adding a query that already exists is a no-op and the existing subscription will be returned. - /// - /// The subscription that represents the specified query. - public Subscription Add(IQueryable query, SubscriptionOptions? options = null) - where T : IRealmObject - { - EnsureWritable(); - - var results = Argument.EnsureType>(query, $"{nameof(query)} must be a query obtained by calling Realm.All.", nameof(query)); - return _handle.Add(results.ResultsHandle, options ?? new()); - } - - /// - /// Removes a subscription with the specified . - /// - /// The name of the subscription to remove. - /// - /// true if the subscription existed in this subscription set and was removed; false otherwise. - /// - public bool Remove(string name) - { - EnsureWritable(); - - Argument.NotNullOrEmpty(name, nameof(name)); - return _handle.Remove(name); - } - - /// - /// Removes a subscription with the specified . - /// - /// The type of objects in the query results. - /// The query whose matching subscription should be removed. - /// A flag indicating whether to also remove named subscriptions. Default is false. - /// - /// true if the subscription existed in this subscription set and was removed; false otherwise. - /// - public int Remove(IQueryable query, bool removeNamed = false) - where T : IRealmObject - { - EnsureWritable(); - - var results = Argument.EnsureType>(query, $"{nameof(query)} must be a query obtained by calling Realm.All.", nameof(query)); - return _handle.Remove(results.ResultsHandle, removeNamed); - } - - /// - /// Removes the provided from this subscription set. - /// - /// The subscription to remove. - /// - /// true if the subscription existed in this subscription set and was removed; false otherwise. - /// - public bool Remove(Subscription subscription) - { - EnsureWritable(); - - Argument.NotNull(subscription, nameof(subscription)); - - return _handle.Remove(subscription.Id); - } - - /// - /// Removes all subscriptions for a specified type. - /// - /// The type of objects whose subscriptions should be removed. - /// A flag indicating whether to also remove named subscriptions. Default is false. - /// The number of subscriptions that existed for this type and were removed. - public int RemoveAll(bool removeNamed = false) - where T : IRealmObject => RemoveAll(typeof(T).GetMappedOrOriginalName(), removeNamed); - - /// - /// Removes all subscriptions for the provided . - /// - /// The name of the type whose subscriptions are to be removed. - /// A flag indicating whether to also remove named subscriptions. Default is false. - /// The number of subscriptions that existed for this type and were removed. - public int RemoveAll(string className, bool removeNamed = false) - { - EnsureWritable(); - - Argument.NotNullOrEmpty(className, nameof(className)); - return _handle.RemoveAll(className, removeNamed); - } - - /// - /// Removes all subscriptions from this subscription set. - /// - /// A flag indicating whether to also remove named subscriptions. Default is false. - /// The number of subscriptions that existed in the set and were removed. - public int RemoveAll(bool removeNamed = false) - { - EnsureWritable(); - - return _handle.RemoveAll(removeNamed); - } - - /// - /// Waits for the server to acknowledge the subscription set and return the matching objects. - /// - /// An optional cancellation token that can be used to cancel the wait operation. - /// - /// If the of the subscription set is - /// the returned will complete immediately. If the is - /// , the returned task will be immediately rejected with an - /// error. - ///
- /// If the change results in removing objects from the Realm - e.g. because subscriptions have been - /// removed, then those objects will have been removed prior to the returned task completing. - ///
- /// - /// An awaitable task, whose successful completion indicates that the server has processed the - /// subscription change and has sent all the data that matches the new subscriptions. - /// - public Task WaitForSynchronizationAsync(CancellationToken? cancellationToken = null) => _handle.WaitForStateChangeAsync(cancellationToken); - - /// - public IEnumerator GetEnumerator() => new Enumerator(this); - - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private void EnsureWritable() - { - if (_handle.IsReadonly) - { - throw new InvalidOperationException("You can't mutate the subscription set outside of a Update or UpdateAsync callback."); - } - } - - private void EnsureReadonly() - { - if (!_handle.IsReadonly) - { - throw new InvalidOperationException("You can't Update/UpdateAsync on a subscription set that is already being updated."); - } - } - - private class Enumerator : IEnumerator - { - private SubscriptionSet? _enumerating; - private int _index; - - internal Enumerator(SubscriptionSet parent) - { - _index = -1; - _enumerating = parent; - } - - public Subscription Current - { - get - { - ThrowIfDisposed(); - - return _enumerating[_index]; - } - } - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - ThrowIfDisposed(); - - var index = _index + 1; - if (index >= _enumerating.Count) - { - return false; - } - - _index = index; - return true; - } - - public void Reset() - { - _index = -1; // by definition BEFORE first item - } - - public void Dispose() - { - ThrowIfDisposed(); - - _enumerating = null; - } - - [MemberNotNull(nameof(_enumerating))] - private void ThrowIfDisposed() - { - if (_enumerating is null) - { - throw new ObjectDisposedException(nameof(Enumerator)); - } - } - } - } -} diff --git a/Realm/Realm/Sync/FlexibleSync/SubscriptionSetState.cs b/Realm/Realm/Sync/FlexibleSync/SubscriptionSetState.cs deleted file mode 100644 index e1b79860e1..0000000000 --- a/Realm/Realm/Sync/FlexibleSync/SubscriptionSetState.cs +++ /dev/null @@ -1,57 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// An enum representing the state of a Realm's subscription set. - /// - public enum SubscriptionSetState : byte - { - /// - /// The subscription update has been persisted locally, but the server hasn't - /// yet returned all the data that matched the updated subscription queries. - /// - Pending, - - /// - /// The server has acknowledged the subscription and sent all the data that - /// matched the subscription queries at the time the subscription set was - /// updated. The server is now in steady-state synchronization mode where it - /// will stream updates as they come. - /// - Complete, - - /// - /// The server has returned an error and synchronization is paused for this - /// Realm. To view the actual error, use . - /// You can still use to update the - /// subscriptions and, if the new update doesn't trigger an error, synchronization - /// will be restarted. - /// - Error, - - /// - /// The subscription set has been superseded by an updated one. This typically means - /// that someone has called on a different instance - /// of the . You should not use a superseded subscription set - /// and instead obtain a new instance by calling . - /// - Superseded, - } -} diff --git a/Realm/Realm/Sync/FlexibleSync/WaitForSyncMode.cs b/Realm/Realm/Sync/FlexibleSync/WaitForSyncMode.cs deleted file mode 100644 index e7e453354c..0000000000 --- a/Realm/Realm/Sync/FlexibleSync/WaitForSyncMode.cs +++ /dev/null @@ -1,69 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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.Specialized; -using System.Linq; -using System.Threading; - -namespace Realms.Sync; - -/// -/// An enum controlling when query.SubscribeAsync will -/// wait for synchronization before returning. -/// -/// -/// When the [Subscription] is created for the first time, data needs to be downloaded from the -/// server before it becomes available, so depending on whether you run the query against the local -/// database before or after this has happened, you query results might not look correct. -///
-/// This enum thus defines the behaviour of when the query is run, so it possible to make the -/// appropriate tradeoff between correctness and availability. -///
-public enum WaitForSyncMode -{ - /// - /// This mode will wait for the server data the first time a subscription is created before - /// returning the local query. Later calls to query.SubscribeAsync - /// will detect that the subscription already exist and return immediately. - ///
- /// This is the default mode. - ///
- FirstTime, - - /// - /// With this mode enabled, Realm will always download the latest server state before returning from - /// query.SubscribeAsync. This means that your - /// query result is always seeing the latest data, but it also requires the app to be online. - /// - /// - /// When using this mode, it is strongly advised to supply a to - /// query.SubscribeAsync to make sure your - /// app doesn't wait forever. - /// - Always, - - /// - /// With this mode enabled, Realm will always return as soon as the the subscription is created - /// while any server data is being downloaded in the background. This update is not atomic, which - /// means that if you subscribe to notifications using - /// - /// or - /// you might see multiple events being fired as the server sends objects matching the subscription. - /// - Never -} diff --git a/Realm/Realm/Sync/GoogleCredentialType.cs b/Realm/Realm/Sync/GoogleCredentialType.cs deleted file mode 100644 index b0881d3421..0000000000 --- a/Realm/Realm/Sync/GoogleCredentialType.cs +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The type of the Google credential. - /// - public enum GoogleCredentialType - { - /// - /// A credential representing an auth code. - /// - AuthCode, - - /// - /// A credential representing an Id token. - /// - IdToken, - } -} diff --git a/Realm/Realm/Sync/MetadataPersistenceMode.cs b/Realm/Realm/Sync/MetadataPersistenceMode.cs deleted file mode 100644 index c324c6b415..0000000000 --- a/Realm/Realm/Sync/MetadataPersistenceMode.cs +++ /dev/null @@ -1,42 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// Enumeration that specifies how and if logged-in objects are persisted - /// across application launches. - /// - public enum MetadataPersistenceMode - { - /// - /// Persist objects, but do not encrypt them. - /// - NotEncrypted = 0, - - /// - /// Persist objects in an encrypted store. - /// - Encrypted, - - /// - /// Do not persist objects. - /// - Disabled - } -} diff --git a/Realm/Realm/Sync/MongoClient.cs b/Realm/Realm/Sync/MongoClient.cs deleted file mode 100644 index dd5f7dcb04..0000000000 --- a/Realm/Realm/Sync/MongoClient.cs +++ /dev/null @@ -1,586 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization.Attributes; -using Realms.Helpers; -using Realms.Schema; - -namespace Realms.Sync -{ - /// - /// The remote MongoClient used for working with data in MongoDB remotely via Realm. - /// - public class MongoClient - { - private static readonly char[] _invalidChars = new[] { '\0', '.' }; - - private static readonly ConcurrentDictionary _schemas = new(); - - private User User { get; } - - /// - /// Gets the service name for this client. - /// - /// The name of the remote MongoDB service. - public string ServiceName { get; } - - internal MongoClient(User user, string serviceName) - { - User = user; - ServiceName = serviceName; - } - - /// - /// Gets a collection of documents from MongoDB that can be deserialized in Realm objects. - /// - /// - /// This method is only supported for source-generated classes - i.e. ones that inherit from - /// rather than . - /// The collection and database name are automatically derived from the Realm object class. - /// - /// The Realm object type that matches the shape of the documents in the collection. - /// A instance that exposes an API for CRUD operations on its contents. - public Collection GetCollection() - where TRealmObject : class, IRealmObjectBase - { - var schema = _schemas.GetOrAdd(typeof(TRealmObject), - type => (ObjectSchema?)type.GetField("RealmSchema", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) - ?? throw new NotSupportedException("This method is only supported for source-generated classes - i.e. ones that inherit from IRealmObject rather than RealmObject.")); - - return new Collection(this, schema.Name); - } - - /// - /// Gets a instance for the given database name. - /// - /// The name of the database to retrieve. - /// A instance that exposes an API for querying its collections. - public Database GetDatabase(string name) - { - Argument.Ensure(IsNameValid(name), "Database names must be non-empty and not contain '.' or the null character.", nameof(name)); - - return new Database(this, name); - } - - private static bool IsNameValid(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return false; - } - - var index = name.IndexOfAny(_invalidChars); - return index == -1; - } - - /// - /// An object representing a remote MongoDB database. - /// - public class Database - { - private static readonly ConcurrentDictionary _schemas = new(); - - /// - /// Gets the that manages this database. - /// - /// The database's . - public MongoClient Client { get; } - - /// - /// Gets the name of the database. - /// - /// The database name. - public string Name { get; } - - internal Database(MongoClient client, string name) - { - Client = client; - Name = name; - } - - /// - /// Gets a collection from the database. - /// - /// The name of the collection. - /// A instance that exposes an API for CRUD operations on its contents. - public Collection GetCollection(string name) => GetCollection(name); - - /// - /// Gets a collection from the database. - /// - /// - /// The MongoDB Bson library is used - /// to decode the response. It will automatically handle most cases, but if you want to control the behavior - /// of the deserializer, you can use the attributes in the - /// MongoDB.Bson.Serialization.Attributes - /// namespace. - ///
- /// If you want to modify the global conventions used when deserializing the response, such as convert - /// camelCase properties to PascalCase, you can register a - /// ConventionPack. - ///
- /// The managed type that matches the shape of the documents in the collection. - /// The name of the collection. - /// A instance that exposes an API for CRUD operations on its contents. - public Collection GetCollection(string name) - where TDocument : class - { - Argument.Ensure(IsNameValid(name), "Collection names must be non-empty and not contain '.' or the null character.", nameof(name)); - - return new Collection(this, name); - } - } - - /// - /// An object representing a remote MongoDB collection. - /// - /// The managed type that matches the shape of the documents in the collection. - public class Collection - where TDocument : class - { - private MongoClient _client; - - /// - /// Gets the this collection belongs to. - /// - /// The collection's or null if the database was automatically - /// inferred with . - public Database? Database { get; } - - /// - /// Gets the name of the collection. - /// - /// The collection name. - public string Name { get; } - - internal Collection(Database database, string name) - { - _client = database.Client; - Database = database; - Name = name; - } - - internal Collection(MongoClient client, string name) - { - _client = client; - Name = name; - } - - /// - /// Inserts the provided document in the collection. - /// - /// The document to insert. - /// - /// An awaitable representing the remote insert operation. The result of the task - /// contains the _id of the inserted document. - /// - /// - public async Task InsertOneAsync(TDocument doc) - { - Argument.NotNull(doc, nameof(doc)); - - return await InvokeOperationAsync("insertOne", "document", doc); - } - - /// - /// Inserts one or more documents in the collection. - /// - /// The documents to insert. - /// - /// An awaitable representing the remote insert many operation. The result of the task - /// contains the _ids of the inserted documents. - /// - /// - public async Task InsertManyAsync(IEnumerable docs) - { - Argument.NotNull(docs, nameof(docs)); - Argument.Ensure(docs.All(d => d != null), "Collection must not contain null elements.", nameof(docs)); - - return await InvokeOperationAsync("insertMany", "documents", docs); - } - - /// - /// Updates a single document in the collection according to the specified arguments. - /// - /// - /// A document describing the selection criteria of the update. If not specified, the first document in the - /// collection will be updated. Can only contain - /// query selector expressions. - /// - /// - /// A document describing the update. Can only contain - /// update operator expressions. - /// - /// - /// A boolean controlling whether the update should insert a document if no documents match the . - /// Defaults to false. - /// - /// - /// An awaitable representing the remote update one operation. The result of the task - /// contains information about the number of matched and updated documents, as well as the _id of the - /// upserted document if was set to true and the operation resulted in an - /// upsert. - /// - /// - public async Task UpdateOneAsync(object? filter, object updateDocument, bool upsert = false) - { - Argument.NotNull(updateDocument, nameof(updateDocument)); - - return await InvokeOperationAsync("updateOne", "query", filter, "update", updateDocument, "upsert", upsert); - } - - /// - /// Updates one or more documents in the collection according to the specified arguments. - /// - /// - /// A document describing the selection criteria of the update. If not specified, all documents in the - /// collection will be updated. Can only contain - /// query selector expressions. - /// - /// - /// A document describing the update. Can only contain - /// update operator expressions. - /// - /// - /// A boolean controlling whether the update should insert a document if no documents match the . - /// Defaults to false. - /// - /// - /// An awaitable representing the remote update many operation. The result of the task - /// contains information about the number of matched and updated documents, as well as the _id of the - /// upserted document if was set to true and the operation resulted in an - /// upsert. - /// - /// - public async Task UpdateManyAsync(object? filter, object updateDocument, bool upsert = false) - { - Argument.NotNull(updateDocument, nameof(updateDocument)); - - return await InvokeOperationAsync("updateMany", "query", filter, "update", updateDocument, "upsert", upsert); - } - - /// - /// Removes a single document from a collection. If no documents match the , the collection is not modified. - /// - /// - /// A document describing the deletion criteria using query operators. - /// If not specified, the first document in the collection will be deleted. - /// - /// - /// An awaitable representing the remote delete one operation. The result of the task contains the number - /// of deleted documents. - /// - /// - public Task DeleteOneAsync(object? filter = null) => InvokeOperationAsync("deleteOne", "query", filter); - - /// - /// Removes one or more documents from a collection. If no documents match the , the collection is not modified. - /// - /// - /// A document describing the deletion criteria using query operators. - /// If not specified, all documents in the collection will be deleted. - /// - /// - /// An awaitable representing the remote delete many operation. The result of the task contains the number - /// of deleted documents. - /// - /// - public Task DeleteManyAsync(object? filter = null) => InvokeOperationAsync("deleteMany", "query", filter); - - /// - /// Finds the all documents in the collection up to . - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will be returned. - /// - /// A document describing the sort criteria. If not specified, the order of the returned documents is not guaranteed. - /// - /// A document describing the fields to return for all matching documents. If not specified, all fields are returned. - /// - /// The maximum number of documents to return. If not specified, all documents in the collection are returned. - /// - /// An awaitable representing the remote find operation. The result of the task is an array containing the documents that match the find criteria. - /// - /// - public Task FindAsync(object? filter = null, object? sort = null, object? projection = null, long? limit = null) - => InvokeOperationAsync("find", "query", filter, "project", projection, "sort", sort, "limit", limit); - - /// - /// Finds the first document in the collection that satisfies the query criteria. - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will match the request. - /// - /// A document describing the sort criteria. If not specified, the order of the returned documents is not guaranteed. - /// - /// A document describing the fields to return for all matching documents. If not specified, all fields are returned. - /// - /// - /// An awaitable representing the remote find one operation. The result of the task is the first document that matches the find criteria. - /// - /// - public Task FindOneAsync(object? filter = null, object? sort = null, object? projection = null) - => InvokeOperationAsync("findOne", "query", filter, "project", projection, "sort", sort); - - /// - /// Finds the first document in the collection that satisfies the query criteria. - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will match the request. - /// - /// - /// A document describing the update. Can only contain - /// update operator expressions. - /// - /// A document describing the sort criteria. If not specified, the order of the returned documents is not guaranteed. - /// - /// A document describing the fields to return for all matching documents. If not specified, all fields are returned. - /// - /// - /// A boolean controlling whether the update should insert a document if no documents match the . - /// Defaults to false. - /// - /// - /// A boolean controlling whether to return the new updated document. If set to false the original document - /// before the update is returned. Defaults to false. - /// - /// - /// An awaitable representing the remote find one operation. The result of the task is the first document that matches the find criteria. - /// - /// - public async Task FindOneAndUpdateAsync(object? filter, object updateDocument, object? sort = null, object? projection = null, bool upsert = false, bool returnNewDocument = false) - { - Argument.NotNull(updateDocument, nameof(updateDocument)); - - return await InvokeOperationAsync("findOneAndUpdate", "filter", filter, "update", updateDocument, "projection", projection, "sort", sort, "upsert", upsert, "returnNewDocument", returnNewDocument); - } - - /// - /// Finds the first document in the collection that satisfies the query criteria. - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will match the request. - /// - /// - /// The replacement document. Cannot contain update operator expressions. - /// - /// - /// A document describing the sort criteria. If not specified, the order of the returned documents is not guaranteed. - /// - /// - /// A document describing the fields to return for all matching documents. If not specified, all fields are returned. - /// - /// - /// A boolean controlling whether the replace should insert a document if no documents match the . - /// Defaults to false. - ///
- /// MongoDB will add the _id field to the replacement document if it is not specified in either the filter or - /// replacement documents. If _id is present in both, the values must be equal. - /// - /// - /// A boolean controlling whether to return the replacement document. If set to false the original document - /// before the update is returned. Defaults to false. - /// - /// - /// An awaitable representing the remote find one operation. The result of the task is the first document that matches the find criteria. - /// - /// - public async Task FindOneAndReplaceAsync(object? filter, TDocument replacementDoc, object? sort = null, object? projection = null, bool upsert = false, bool returnNewDocument = false) - { - Argument.NotNull(replacementDoc, nameof(replacementDoc)); - - return await InvokeOperationAsync("findOneAndReplace", "filter", filter, "update", replacementDoc, "projection", projection, "sort", sort, "upsert", upsert, "returnNewDocument", returnNewDocument); - } - - /// - /// Finds the first document in the collection that satisfies the query criteria. - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will match the request. - /// - /// A document describing the sort criteria. If not specified, the order of the returned documents is not guaranteed. - /// - /// A document describing the fields to return for all matching documents. If not specified, all fields are returned. - /// - /// - /// An awaitable representing the remote find one operation. The result of the task is the first document that matches the find criteria. - /// - /// - public Task FindOneAndDeleteAsync(object? filter = null, object? sort = null, object? projection = null) - => InvokeOperationAsync("findOneAndDelete", "filter", filter, "projection", projection, "sort", sort); - - /// - /// Executes an aggregation pipeline on the collection and returns the results as a array. - /// - /// The managed type that matches the shape of the result of the pipeline. - /// - /// Documents describing the different pipeline stages using pipeline expressions. - /// - /// - /// An awaitable representing the remote aggregate operation. The result of the task is an array containing the documents returned - /// by executing the aggregation . - /// - /// - public Task AggregateAsync(params object[] pipeline) => InvokeOperationAsync("aggregate", "pipeline", pipeline); - - /// - /// Executes an aggregation pipeline on the collection and returns the results as a array. - /// - /// - /// Documents describing the different pipeline stages using pipeline expressions. - /// - /// - /// An awaitable representing the remote aggregate operation. The result of the task is an array containing the documents returned - /// by executing the aggregation . - /// - /// - public Task AggregateAsync(params object[] pipeline) => AggregateAsync(pipeline); - - /// - /// Counts the number of documents in the collection that match the provided . - /// - /// - /// A document describing the find criteria using query operators. - /// If not specified, all documents in the collection will be counted. - /// - /// The maximum number of documents to count. If not specified, all documents in the collection are counted. - /// - /// An awaitable representing the remote count operation. The result of the task is the number of documents that match the - /// and criteria. - /// - public Task CountAsync(object? filter = null, long? limit = null) => InvokeOperationAsync("count", "query", filter, "limit", limit); - - private async Task InvokeOperationAsync(string functionName, params object?[] args) - { - var jsonBuilder = new StringBuilder(); - - jsonBuilder.Append("[{"); - - if (Database is not null) - { - jsonBuilder.Append($"\"database\":\"{Database.Name}\",\"collection\":\"{Name}\""); - } - else - { - // Database and Collection names are automatically inferred by server based on schema name - jsonBuilder.Append($"\"schema_name\":\"{Name}\""); - } - - Debug.Assert(args.Length % 2 == 0, "args should be provided as key-value pairs"); - - for (var i = 0; i < args.Length; i += 2) - { - if (args[i + 1] != null) - { - jsonBuilder.Append($",\"{args[i]}\":{args[i + 1].ToNativeJson()}"); - } - } - - jsonBuilder.Append("}]"); - - return await _client.User.Functions.CallSerializedAsync(functionName, jsonBuilder.ToString(), _client.ServiceName); - } - } - - /// - /// The result of or operation. - /// - public class UpdateResult - { - /// - /// Gets the number of documents matched by the filter. - /// - /// The number of matched documents. - [BsonElement("matchedCount")] - [Preserve] - public int MatchedCount { get; private set; } - - /// - /// Gets the number of documents modified by the operation. - /// - /// The number of modified documents. - [BsonElement("modifiedCount")] - [Preserve] - public int ModifiedCount { get; private set; } - - /// - /// Gets the _id of the inserted document if the operation resulted in an insertion. - /// - /// The _id of the inserted document or null if the operation didn't result in an insertion. - [BsonElement("upsertedId")] - [Preserve] - public object? UpsertedId { get; private set; } - } - - /// - /// The result of operation. - /// - public class InsertResult - { - /// - /// Gets the _id of the inserted document. - /// - /// The _id of the inserted document. - [BsonElement("insertedId")] - [Preserve] - public object InsertedId { get; private set; } = null!; - } - - /// - /// The result of operation. - /// - public class InsertManyResult - { - /// - /// Gets an array containing the _ids of the inserted documents. - /// - /// The _ids of the inserted documents. - [BsonElement("insertedIds")] - [Preserve] - public object[] InsertedIds { get; private set; } = Array.Empty(); - } - - /// - /// The result of or operation. - /// - public class DeleteResult - { - /// - /// Gets the number of deleted documents. - /// - /// The number of deleted documents. - [BsonElement("deletedCount")] - [Preserve] - public int DeletedCount { get; private set; } - } - } -} diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressDirection.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressDirection.cs deleted file mode 100644 index b82c438c7c..0000000000 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressDirection.cs +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2017 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The transfer direction (upload or download) tracked by a given progress notification subscription. - /// - public enum ProgressDirection : byte - { - /// - /// Monitors upload progress. - /// - Upload = 0, - - /// - /// Monitors download progress. - /// - Download = 1 - } -} diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs deleted file mode 100644 index 9fd170fa54..0000000000 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressMode.cs +++ /dev/null @@ -1,40 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2017 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The desired behavior of a progress notification subscription. - /// - public enum ProgressMode - { - /// - /// The callback will be called forever, or until it is unregistered by disposing the subscription token. - /// - ReportIndefinitely, - - /// - /// The callback will be active: - /// - /// For uploads, until all the unsynced data available at the moment of the registration of the callback is sent to the server. - /// For downloads, until the client catches up to the current data (available at the moment of callback registration) or the next batch of data sent from the server. - /// - /// - ForCurrentlyOutstandingWork - } -} diff --git a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs b/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs deleted file mode 100644 index 03a74eb692..0000000000 --- a/Realm/Realm/Sync/ProgressNotifications/ProgressNotificationToken.cs +++ /dev/null @@ -1,78 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2019 Realm Inc. -// -// 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; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Realms.Logging; - -namespace Realms.Sync -{ - internal class ProgressNotificationToken : IDisposable - { - private readonly ulong _nativeToken; - private readonly GCHandle _gcHandle; - private readonly Action _observer; - private readonly Action _unregister; - - private bool _isDisposed; - - public ProgressNotificationToken(Action observer, Func register, Action unregister) - { - _observer = observer; - _gcHandle = GCHandle.Alloc(this); - _unregister = unregister; - try - { - _nativeToken = register(_gcHandle); - } - catch - { - _gcHandle.Free(); - throw; - } - } - - public void Notify(double progressEstimate) - { - Task.Run(() => - { - try - { - _observer(new SyncProgress(progressEstimate)); - } - catch (Exception ex) - { - RealmLogger.Default.Log(LogLevel.Warn, $"An error occurred while reporting progress: {ex}"); - } - }); - } - - public void Dispose() - { - if (!_isDisposed) - { - GC.SuppressFinalize(this); - - _isDisposed = true; - _unregister(_nativeToken); - _gcHandle.Free(); - } - } - } -} diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs deleted file mode 100644 index 20ddfa8379..0000000000 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgress.cs +++ /dev/null @@ -1,39 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2017 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// A struct containing information about the progress state at a given instant. - /// - public readonly struct SyncProgress - { - /// - /// Gets the percentage estimate of the current progress, expressed as a double between 0.0 and 1.0. - /// - /// A percentage estimate of the progress. - public double ProgressEstimate { get; } - - internal SyncProgress(double progressEstimate) - { - ProgressEstimate = progressEstimate; - } - - internal bool IsComplete => ProgressEstimate >= 1.0; - } -} diff --git a/Realm/Realm/Sync/ProgressNotifications/SyncProgressObservable.cs b/Realm/Realm/Sync/ProgressNotifications/SyncProgressObservable.cs deleted file mode 100644 index c8d97a4a5d..0000000000 --- a/Realm/Realm/Sync/ProgressNotifications/SyncProgressObservable.cs +++ /dev/null @@ -1,48 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2017 Realm Inc. -// -// 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; - -namespace Realms.Sync -{ - internal class SyncProgressObservable : IObservable - { - private readonly SessionHandle _sessionHandle; - private readonly ProgressDirection _direction; - private readonly ProgressMode _mode; - - public SyncProgressObservable(SessionHandle sessionHandle, ProgressDirection direction, ProgressMode mode) - { - _sessionHandle = sessionHandle; - _direction = direction; - _mode = mode; - } - - public IDisposable Subscribe(IObserver observer) - { - return new ProgressNotificationToken(progress => - { - observer.OnNext(progress); - if (_mode == ProgressMode.ForCurrentlyOutstandingWork && progress.IsComplete) - { - observer.OnCompleted(); - } - }, handle => _sessionHandle.RegisterProgressNotifier(handle, _direction, _mode), _sessionHandle.UnregisterProgressNotifier); - } - } -} diff --git a/Realm/Realm/Sync/ReconnectBackoffOptions.cs b/Realm/Realm/Sync/ReconnectBackoffOptions.cs deleted file mode 100644 index 493ed660f3..0000000000 --- a/Realm/Realm/Sync/ReconnectBackoffOptions.cs +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2024 Realm Inc. -// -// 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; - -namespace Realms.Sync -{ - /// - /// Options for configuring the reconnection delay used by the sync client. - /// - /// - /// The sync client employs an exponential backoff delay strategy when reconnecting to the server. - /// In order to not spam the network interface the sync client performs an increasing wait before reconnecting. - /// The wait starts from and multiplies by - /// until it reaches . - /// - public class ReconnectBackoffOptions - { - /// - /// Gets or sets the maximum amount of time to wait before a reconnection attempt. - /// - /// - /// Defaults to 5 minutes. - /// - /// - /// The maximum amount of time to wait before a reconnection attempt. - /// - public TimeSpan MaxReconnectDelayInterval { get; set; } = TimeSpan.FromMinutes(5); - - /// - /// Gets or sets the initial amount of time to wait before a reconnection attempt. - /// - /// - /// Defaults to 1 second. - /// - /// - /// The initial amount of time to wait before a reconnection attempt. - /// - public TimeSpan ReconnectDelayInterval { get; set; } = TimeSpan.FromSeconds(1); - - /// - /// Gets or sets the multiplier to apply to the accumulated reconnection delay before a new reconection attempt. - /// - /// - /// Defaults to 2. - /// - /// - /// The delay multiplier. - /// - public int ReconnectDelayBackoffMultiplier { get; set; } = 2; - - /// - /// Gets or sets the jitter randomization factor to apply to the delay. - /// - /// - /// The reconnection delay is subtracted by a value derived from this divisor so that if a lot of clients lose connection and reconnect at the same time the server won't be overwhelmed. - ///
- /// Defaults to 4. - ///
- /// - /// The jitter randomization factor to apply to the delay. - /// - public int DelayJitterDivisor { get; set; } = 4; - } -} \ No newline at end of file diff --git a/Realm/Realm/Sync/SchemaMode.cs b/Realm/Realm/Sync/SchemaMode.cs deleted file mode 100644 index bfa9cf9cb3..0000000000 --- a/Realm/Realm/Sync/SchemaMode.cs +++ /dev/null @@ -1,29 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// To see details of each mode check its mirroring definition in core. - /// - internal enum SchemaMode : byte - { - AdditiveDiscovered = 5, - AdditiveExplicit = 6 - } -} diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs deleted file mode 100644 index 51d9e2c96e..0000000000 --- a/Realm/Realm/Sync/Session.cs +++ /dev/null @@ -1,232 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Realms.Exceptions.Sync; - -namespace Realms.Sync -{ - /// - /// An object encapsulating a synchronization session. Sessions represent the communication between the client (and a local Realm file on disk), - /// and MongoDB Atlas. Sessions are always created by the SDK and vended - /// out through various APIs. The lifespans of sessions associated with Realms are managed automatically. - /// - public class Session : INotifyPropertyChanged - { - private readonly SessionHandle _handle; - - internal readonly Action? _onSubscribed; - - internal readonly Action? _onUnsubscribed; - - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "This is the private event - the public is uppercased.")] - private event PropertyChangedEventHandler? _propertyChanged; - - private SessionHandle Handle - { - get - { - if (_handle.IsClosed) - { - throw new ObjectDisposedException( - nameof(Session), - "This Session instance is invalid. This typically means that Sync has closed or otherwise invalidated the native session. You can get a new valid instance by calling realm.GetSession()."); - } - - return _handle; - } - } - - internal bool IsClosed => _handle.IsClosed; - - /// - /// Occurs when a property value changes. - /// - public event PropertyChangedEventHandler? PropertyChanged - { - add - { - if (_propertyChanged == null) - { - Handle.SubscribeNotifications(this); - _onSubscribed?.Invoke(this); - } - - _propertyChanged += value; - } - - remove - { - _propertyChanged -= value; - - if (_propertyChanged == null) - { - _onUnsubscribed?.Invoke(); - Handle.UnsubscribeNotifications(); - } - } - } - - /// - /// Gets the session’s current state. - /// - /// An enum value indicating the state of the session. - public SessionState State => Handle.GetState(); - - /// - /// Gets the session’s current connection state. - /// - /// An enum value indicating the connection state of the session. - public ConnectionState ConnectionState => Handle.GetConnectionState(); - - /// - /// Gets the defined by the that is used to connect to MongoDB Atlas. - /// - /// The that was used to create the 's . - public User User => new(Handle.GetUser()); - - /// - /// Gets the on-disk path of the Realm file backing the this Session represents. - /// - /// The file path. - public string Path => Handle.GetPath(); - - /// - /// Gets an that can be used to track upload or download progress. - /// - /// - /// To start receiving notifications, you should call on the returned object. - /// The token returned from should be retained as long as progress - /// notifications are desired. To stop receiving notifications, call - /// on the token. - /// You don't need to keep a reference to the observable itself. - /// The progress callback will always be called once immediately upon subscribing in order to provide - /// the latest available status information. - /// - /// An observable that you can subscribe to and receive progress updates. - /// The transfer direction (upload or download) to track in the subscription callback. - /// The desired behavior of this progress notification block. - /// - /// - /// class ProgressNotifyingViewModel - /// { - /// private IDisposable notificationToken; - /// - /// public void ShowProgress() - /// { - /// var observable = session.GetProgressObservable(ProgressDirection.Upload, ProgressMode.ReportIndefinitely); - /// notificationToken = observable.Subscribe(progress => - /// { - /// // Update relevant properties by accessing progress.ProgressEstimate - /// }); - /// } - /// - /// public void HideProgress() - /// { - /// notificationToken?.Dispose(); - /// notificationToken = null; - /// } - /// } - /// - /// In this example we're using ObservableExtensions.Subscribe - /// found in the Reactive Extensions class library. - /// If you prefer not to take a dependency on it, you can create a class that implements - /// and use it to subscribe instead. - /// - public IObservable GetProgressObservable(ProgressDirection direction, ProgressMode mode) => new SyncProgressObservable(Handle, direction, mode); - - /// - /// Waits for the to finish all pending uploads. - /// - /// An optional cancellation token that can be used to cancel the wait operation. - /// An awaitable that will be completed when all pending uploads for this are completed. - /// Thrown when a faulted session is waited on. - public Task WaitForUploadAsync(CancellationToken? cancellationToken = null) => Handle.WaitAsync(ProgressDirection.Upload, cancellationToken); - - /// - /// Waits for the to finish all pending downloads. - /// - /// An optional cancellation token that can be used to cancel the wait operation. - /// An awaitable that will be completed when all pending downloads for this are completed. - /// Thrown when a faulted session is waited on. - public Task WaitForDownloadAsync(CancellationToken? cancellationToken = null) => Handle.WaitAsync(ProgressDirection.Download, cancellationToken); - - /// - /// Stops any synchronization with the server until the Realm is re-opened again - /// after fully closing it. - ///
- /// Synchronization can be re-enabled by calling again. - ///
- /// - /// If the session is already stopped, calling this method will do nothing. - /// - public void Stop() => Handle.Stop(); - - /// - /// Attempts to resume the session and enable synchronization with the server. - /// - /// - /// All sessions will be active by default and calling this method only makes sense if - /// was called before that. - /// - public void Start() => Handle.Start(); - - /// - public override bool Equals(object? obj) - => obj is Session other && - Handle.GetRawPointer() == other.Handle.GetRawPointer(); - - /// - public override int GetHashCode() => Handle.GetRawPointer().GetHashCode(); - - internal Session(SessionHandle handle, Action? onSubscribed = null, Action? onUnsubscribed = null) - { - _handle = handle; - _onSubscribed = onSubscribed; - _onUnsubscribed = onUnsubscribed; - } - - internal void CloseHandle(bool waitForShutdown = false) - { - GC.SuppressFinalize(this); - if (!IsClosed) - { - if (waitForShutdown) - { - _handle.ShutdownAndWait(); - } - - _propertyChanged = null; - _onUnsubscribed?.Invoke(); - _handle.Close(); - } - } - - internal void ReportErrorForTesting(int errorCode, string errorMessage, bool isFatal, ServerRequestsAction action) - => Handle.ReportErrorForTesting(errorCode, errorMessage, isFatal, action); - - internal void RaisePropertyChanged(string propertyName) - { - _propertyChanged?.Invoke(this, new(propertyName)); - } - } -} diff --git a/Realm/Realm/Sync/SessionState.cs b/Realm/Realm/Sync/SessionState.cs deleted file mode 100644 index 49b3b14524..0000000000 --- a/Realm/Realm/Sync/SessionState.cs +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The current state of a sync session object. - /// - public enum SessionState : byte - { - /// - /// The session is connected to Atlas Devices and is actively transferring data. - /// - Active = 0, - - /// - /// The session is not currently communicating with the server. - /// - Inactive - } -} \ No newline at end of file diff --git a/Realm/Realm/Sync/SessionStopPolicy.cs b/Realm/Realm/Sync/SessionStopPolicy.cs deleted file mode 100644 index c943fccea4..0000000000 --- a/Realm/Realm/Sync/SessionStopPolicy.cs +++ /dev/null @@ -1,27 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - internal enum SessionStopPolicy - { - Immediately, // Immediately stop the session as soon as all Realms/Sessions go out of scope. - LiveIndefinitely, // Never stop the session. - AfterChangesUploaded, // Once all Realms/Sessions go out of scope, wait for uploads to complete and stop. - } -} \ No newline at end of file diff --git a/Realm/Realm/Sync/SyncTimeoutOptions.cs b/Realm/Realm/Sync/SyncTimeoutOptions.cs deleted file mode 100644 index f8af541bfe..0000000000 --- a/Realm/Realm/Sync/SyncTimeoutOptions.cs +++ /dev/null @@ -1,116 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2023 Realm Inc. -// -// 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; - -namespace Realms.Sync -{ - /// - /// Options for configuring timeouts and intervals used by the sync client. - /// - public class SyncTimeoutOptions - { - /// - /// Gets or sets the maximum amount of time to allow for a connection to - /// become fully established. - /// - /// - /// This includes the time to resolve the - /// network address, the TCP connect operation, the SSL handshake, and - /// the WebSocket handshake. - ///
- /// Defaults to 2 minutes. - ///
- /// The connection timeout. - public TimeSpan ConnectTimeout { get; set; } = TimeSpan.FromMinutes(2); - - /// - /// Gets or sets the amount of time to keep a connection open after all - /// sessions have been abandoned. - /// - /// - /// After all synchronized Realms have been closed for a given server, the - /// connection is kept open until the linger time has expired to avoid the - /// overhead of reestablishing the connection when Realms are being closed and - /// reopened. - ///
- /// Defaults to 30 seconds. - ///
- /// The time to keep the connection open. - public TimeSpan ConnectionLingerTime { get; set; } = TimeSpan.FromSeconds(30); - - /// - /// Gets or sets how long to wait between each heartbeat ping message. - /// - /// - /// The client periodically sends ping messages to the server to check if the - /// connection is still alive. Shorter periods make connection state change - /// notifications more responsive at the cost of battery life (as the antenna - /// will have to wake up more often). - ///
- /// Defaults to 1 minute. - ///
- /// The ping interval. - public TimeSpan PingKeepAlivePeriod { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Gets or sets how long to wait for a response to a heartbeat ping before - /// concluding that the connection has dropped. - /// - /// - /// Shorter values will make connection state change notifications more - /// responsive as it will only change to `disconnected` after this much time has - /// elapsed, but overly short values may result in spurious disconnection - /// notifications when the server is simply taking a long time to respond. - ///
- /// Defaults to 2 minutes. - ///
- /// The pong timeout. - public TimeSpan PongKeepAliveTimeout { get; set; } = TimeSpan.FromMinutes(2); - - /// - /// Gets or sets the maximum amount of time since the loss of a - /// prior connection, for a new connection to be considered a "fast - /// reconnect". - /// - /// - /// When a client first connects to the server, it defers uploading any local - /// changes until it has downloaded all changesets from the server. This - /// typically reduces the total amount of merging that has to be done, and is - /// particularly beneficial the first time that a specific client ever connects - /// to the server. - ///
- /// When an existing client disconnects and then reconnects within the "fact - /// reconnect" time this is skipped and any local changes are uploaded - /// immediately without waiting for downloads, just as if the client was online - /// the whole time. - ///
- /// Defaults to 1 minute. - ///
- /// The window in which a drop in connectivity is considered transient. - public TimeSpan FastReconnectLimit { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Gets or sets the options for the reconnection behavior of the sync client. - /// - /// - /// The options controlling how long the sync client waits before attempting to reconnect. - /// - public ReconnectBackoffOptions ReconnectBackoffOptions { get; set; } = new (); - } -} diff --git a/Realm/Realm/Sync/User.cs b/Realm/Realm/Sync/User.cs deleted file mode 100644 index 7f88a87ff2..0000000000 --- a/Realm/Realm/Sync/User.cs +++ /dev/null @@ -1,501 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using MongoDB.Bson; -using MongoDB.Bson.Serialization; -using Realms.Helpers; -using Realms.Sync.Exceptions; - -namespace Realms.Sync -{ - /// - /// This class represents a user in a Atlas App Services application. The credentials are provided by various 3rd party providers (Facebook, Google, etc.). - /// A user can log in to the server and, if access is granted, it is possible to synchronize the local and the remote Realm. Moreover, synchronization is halted when the user is logged out. - /// It is possible to persist a user. By retrieving a user, there is no need to log in to the 3rd party provider again. Persisting a user between sessions, the user's credentials are stored locally on the device, and should be treated as sensitive data. - /// - public class User : IEquatable - { - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "This is the private event - the public is uppercased.")] - private event EventHandler? _changed; - - /// - /// Occurs when a property value changes. - /// - public event EventHandler? Changed - { - add - { - if (_changed == null) - { - Handle.SubscribeNotifications(this); - } - - _changed += value; - } - - remove - { - _changed -= value; - - if (_changed == null) - { - Handle.UnsubscribeNotifications(); - } - } - } - - /// - /// Gets this user's refresh token. This is the user's credential for accessing MongoDB Atlas data and should be treated as sensitive information. - /// - /// A unique string that can be used for refreshing the user's credentials. - public string RefreshToken - { - get => Handle.GetRefreshToken(); - } - - /// - /// Gets this user's access token. This is the user's credential for accessing MongoDB Atlas data and should be treated as sensitive information. - /// - /// A unique string that can be used to represent this user before the server. - public string AccessToken - { - get => Handle.GetAccessToken(); - } - - /// - /// Gets a unique identifier for the device the user logged in to. - /// - /// A unique string that identifies the current device. - public string DeviceId - { - get => Handle.GetDeviceId(); - } - - /// - /// Gets the Id of this user in Atlas App Services. - /// - /// A string that uniquely identifies that user. - public string Id => Handle.GetUserId(); - - /// - /// Gets the current state of the user. - /// - /// A value indicating whether the user is active, logged out, or an error has occurred. - public UserState State => Handle.GetState(); - - /// - /// Gets a value indicating which this user logged in with. - /// - /// The used to login the user. - [Obsolete("User.Provider wasn't working consistently and will be removed in a future version. You can get the provider of the user identity instead.")] - public Credentials.AuthProvider Provider => Identities.FirstOrDefault()?.Provider ?? Credentials.AuthProvider.Unknown; - - /// - /// Gets the app with which this user is associated. - /// - /// An instance that owns this user. - public App App { get; } - - /// - /// Gets the profile information for that user. - /// - /// A object, containing information about the user's name, email, and so on. - public UserProfile Profile { get; } - - /// - /// Gets the custom user data associated with this user in the Realm app. - /// - /// - /// The data is only refreshed when the user's access token is refreshed or when explicitly calling . - /// - /// A document containing the user data. - /// Custom User Data Docs - public BsonDocument? GetCustomData() - { - var serialized = Handle.GetCustomData(); - if (string.IsNullOrEmpty(serialized) || !BsonDocument.TryParse(serialized, out var doc)) - { - return null; - } - - return doc; - } - - /// - /// Gets the custom user data associated with this user in the Realm app and parses it to the specified type. - /// - /// The managed type that matches the shape of the custom data documents. - /// - /// The data is only refreshed when the user's access token is refreshed or when explicitly calling . - /// - /// A document containing the user data. - /// Custom User Data Docs - public T? GetCustomData() - where T : class - { - var customData = GetCustomData(); - if (customData is null) - { - return null; - } - - return BsonSerializer.Deserialize(customData); - } - - /// - /// Gets a collection of all identities associated with this user. - /// - /// The user's identities across different s. - public UserIdentity[] Identities - { - get - { - var serialized = Handle.GetIdentities(); - return BsonSerializer.Deserialize(serialized); - } - } - - /// - /// Gets a instance that exposes functionality for managing user API keys. - /// - /// A instance scoped to this . - /// API Keys Authentication Docs - public ApiKeyClient ApiKeys { get; } - - /// - /// Gets a instance that exposes functionality for calling remote Atlas Functions. - /// - /// A instance scoped to this . - /// Functions Docs - public FunctionsClient Functions { get; } - - internal readonly SyncUserHandle Handle; - - internal User(SyncUserHandle handle, App? app = null) - { - if (app is null && handle.TryGetApp(out var appHandle)) - { - app = new App(appHandle); - } - - App = app!; - Handle = handle; - Profile = new UserProfile(this); - ApiKeys = new ApiKeyClient(this); - Functions = new FunctionsClient(this); - } - - /// - /// Removes the user's local credentials and attempts to invalidate their refresh token from the server. - /// - /// An awaitable that represents the remote logout operation. - public Task LogOutAsync() => App.RemoveUserAsync(this); - - /// - /// Re-fetch the user's custom data from the server. - /// - /// - /// An awaitable that represents the remote refresh operation. The result is a - /// containing the updated custom user data. The value returned by will also be updated with the new information. - /// - public async Task RefreshCustomDataAsync() - { - await Handle.RefreshCustomDataAsync(); - - return GetCustomData(); - } - - /// - /// Re-fetch the user's custom data from the server. - /// - /// The managed type that matches the shape of the custom data documents. - /// - /// An awaitable that represents the remote refresh operation. The result is an object - /// containing the updated custom user data. The value returned by will also be updated with the new information. - /// - public async Task RefreshCustomDataAsync() - where T : class - { - var result = await RefreshCustomDataAsync(); - if (result is null) - { - return null; - } - - return BsonSerializer.Deserialize(result); - } - - /// - /// Gets a instance for accessing documents in a MongoDB database. - /// - /// The name of the service as configured on the server. - /// A instance that can interact with the databases exposed in the remote service. - public MongoClient GetMongoClient(string serviceName) => new(this, serviceName); - - /// - /// Links the current user with a new user identity represented by the given credentials. - /// - /// - /// Linking a user with more credentials, mean the user can login either of these credentials. It also - /// makes it possible to "upgrade" an anonymous user by linking it with e.g. Email/Password credentials. - ///
- /// Note: It is not possible to link two existing users of Atlas App Services. The provided credentials must not have been used by another user. - ///
- /// Note for email/password auth: To link a user with a new set of credentials, you will need to first - /// register these credentials by calling . - ///
- /// - /// The following snippet shows how to associate an email and password with an anonymous user - /// allowing them to login on a different device. - /// - /// var app = App.Create("app-id") - /// var user = await app.LogInAsync(Credentials.Anonymous()); - /// - /// // This step is only needed for email password auth - a password record must exist - /// // before you can link a user to it. - /// await app.EmailPasswordAuth.RegisterUserAsync("email", "password"); - /// await user.LinkCredentialsAsync(Credentials.EmailPassword("email", "password")); - /// - /// - /// The credentials to link with the current user. - /// - /// An awaitable representing the remote link credentials operation. Upon successful completion, the task result - /// will contain the user to which the credentials were linked. - /// - public async Task LinkCredentialsAsync(Credentials credentials) - { - Argument.NotNull(credentials, nameof(credentials)); - - var handle = await Handle.LinkCredentialsAsync(App.Handle, credentials.ToNative()); - return new User(handle, App); - } - - /// - public override bool Equals(object? obj) => Equals(obj as User); - - /// - /// Determines whether this instance and another instance are equal by comparing their identities. - /// - /// The instance to compare with. - /// true if the two instances are equal; false otherwise. - public bool Equals(User? other) => Id.Equals(other?.Id); - - /// - public override int GetHashCode() => Id.GetHashCode(); - - /// - /// Determines whether two instances are equal. - /// - /// The first user to compare. - /// The second user to compare. - /// true if the two instances are equal; false otherwise. - public static bool operator ==(User? user1, User? user2) => user1?.Id == user2?.Id; - - /// - /// Determines whether two instances are different. - /// - /// The first user to compare. - /// The second user to compare. - /// true if the two instances are different; false otherwise. - public static bool operator !=(User? user1, User? user2) => !(user1 == user2); - - /// - /// Returns a string that represents the current object. - /// - /// A string that represents the current object. - public override string ToString() - { - return $"User {Id}, State: {State}"; - } - - internal void RaiseChanged() - { - _changed?.Invoke(this, EventArgs.Empty); - } - - /// - /// A class exposing functionality for users to manage API keys from the client. It is always scoped - /// to a particular and can only be accessed via . - /// - public class ApiKeyClient - { - private readonly User _user; - - internal ApiKeyClient(User user) - { - _user = user; - } - - /// - /// Creates an API key that can be used to authenticate as the user. - /// - /// - /// The value of the returned API key must be persisted at this time as this is the only - /// time it is visible. The key is enabled when created. It can be disabled by calling - /// . - /// - /// The friendly name of the key. - /// - /// An awaitable representing the asynchronous operation. Successful completion indicates - /// that the has been created on the server and its can - /// be used to create . - /// - public Task CreateAsync(string name) - { - Argument.NotNullOrEmpty(name, nameof(name)); - - return _user.Handle.CreateApiKeyAsync(_user.App.Handle, name); - } - - /// - /// Fetches a specific user API key by id. - /// - /// The id of the key to fetch. - /// - /// An awaitable representing the asynchronous lookup operation. - /// - public Task FetchAsync(ObjectId id) => Handle404(_user.Handle.FetchApiKeyAsync(_user.App.Handle, id)); - - /// - /// Fetches all API keys associated with the user. - /// - /// - /// An awaitable task representing the asynchronous lookup operation. Upon completion, the result contains - /// a collection of all API keys for that user. - /// - public async Task> FetchAllAsync() - { - return await _user.Handle.FetchAllApiKeysAsync(_user.App.Handle); - } - - /// - /// Deletes an API key by id. - /// - /// The id of the key to delete. - /// An awaitable representing the asynchronous delete operation. - public Task DeleteAsync(ObjectId id) => Handle404(_user.Handle.DeleteApiKeyAsync(_user.App.Handle, id)); - - /// - /// Disables an API key by id. - /// - /// The id of the key to disable. - /// An awaitable representing the asynchronous disable operation. - /// - public Task DisableAsync(ObjectId id) => Handle404(_user.Handle.DisableApiKeyAsync(_user.App.Handle, id), id); - - /// - /// Enables an API key by id. - /// - /// The id of the key to enable. - /// An awaitable representing the asynchronous enable operation. - /// - public Task EnableAsync(ObjectId id) => Handle404(_user.Handle.EnableApiKeyAsync(_user.App.Handle, id), id); - - private static async Task Handle404(Task task) - { - try - { - return await task; - } - catch (AppException ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - return default; - } - } - - private static async Task Handle404(Task task, ObjectId? id = null) - { - try - { - await task; - } - catch (AppException ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - if (id.HasValue) - { - throw new AppException($"Failed to execute operation because ApiKey with Id: {id} doesn't exist.", ex.HelpLink, 404); - } - } - } - } - - /// - /// A class exposing functionality for calling remote Atlas Functions. - /// - /// Functions Docs - public class FunctionsClient - { - private readonly User _user; - - internal FunctionsClient(User user) - { - _user = user; - } - - /// - /// Calls a remote function with the supplied arguments. - /// - /// Name of the Realm function to call. - /// Arguments that will be sent to the Realm function. They have to be json serializable values. - /// - /// An awaitable wrapping the asynchronous call function operation. The result of the task is - /// the value returned by the function. - /// - public Task CallAsync(string name, params object?[] args) => CallAsync(name, args); - - /// - /// Calls a remote function with the supplied arguments. - /// - /// - /// The MongoDB Bson library is used - /// to decode the response. It will automatically handle most cases, but if you want to control the behavior - /// of the deserializer, you can use the attributes in the - /// MongoDB.Bson.Serialization.Attributes - /// namespace. - ///
- /// If you want to modify the global conventions used when deserializing the response, such as convert - /// camelCase properties to PascalCase, you can register a - /// ConventionPack. - ///
- /// The type that the response will be decoded to. - /// Name of the Realm function to call. - /// Arguments that will be sent to the Realm function. They have to be json serializable values. - /// - /// An awaitable wrapping the asynchronous call function operation. The result of the task is - /// the value returned by the function decoded as . - /// - public Task CallAsync(string name, params object?[] args) => CallSerializedAsync(name, args.ToNativeJson()); - - internal async Task CallSerializedAsync(string name, string args, string? serviceName = null) - { - Argument.NotNullOrEmpty(name, nameof(name)); - - var response = await _user.Handle.CallFunctionAsync(_user.App.Handle, name, args, serviceName); - - return BsonSerializer.Deserialize(response); - } - } - } -} diff --git a/Realm/Realm/Sync/UserIdentity.cs b/Realm/Realm/Sync/UserIdentity.cs deleted file mode 100644 index 83951275cf..0000000000 --- a/Realm/Realm/Sync/UserIdentity.cs +++ /dev/null @@ -1,70 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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 MongoDB.Bson.Serialization.Attributes; - -namespace Realms.Sync -{ - /// - /// A class containing information about an identity associated with a user. - /// - [BsonNoId] - public class UserIdentity - { - /// - /// Gets the unique identifier for this identity. - /// - /// The identity's Id. - [Preserve] - public string Id { get; private set; } = null!; - - /// - /// Gets the auth provider defining this identity. - /// - /// The identity's auth provider. - [Preserve] - public Credentials.AuthProvider Provider { get; private set; } - - /// - [Preserve] - public override bool Equals(object? obj) => (obj is UserIdentity id) && id.Id == Id && id.Provider == Provider; - - /// - /// Gets the hash code. - /// - /// The hash code. - [Preserve] - public override int GetHashCode() - { - unchecked - { - var hashCode = -1285871140; - hashCode = (hashCode * -1521134295) + (Id?.GetHashCode() ?? 0); - hashCode = (hashCode * -1521134295) + Provider.GetHashCode(); - return hashCode; - } - } - - /// - /// Returns a string representation of the value. - /// - /// A string representation of the value. - [Preserve] - public override string ToString() => $"UserIdentity: {Id} ({Provider})"; - } -} diff --git a/Realm/Realm/Sync/UserProfile.cs b/Realm/Realm/Sync/UserProfile.cs deleted file mode 100644 index ffb6b60757..0000000000 --- a/Realm/Realm/Sync/UserProfile.cs +++ /dev/null @@ -1,97 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2020 Realm Inc. -// -// 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; -using Realms.Native; - -namespace Realms.Sync -{ - /// - /// A class containing profile information about . - /// - public class UserProfile - { - private readonly User _user; - - /// - /// Gets the name of the user. - /// - /// A string representing the user's name or null if not available. - public string? Name => _user.Handle.GetProfileData(UserProfileField.Name); - - /// - /// Gets the email of the user. - /// - /// A string representing the user's email or null if not available. - public string? Email => _user.Handle.GetProfileData(UserProfileField.Email); - - /// - /// Gets the url for the user's profile picture. - /// - /// A string representing the user's profile picture url or null if not available. - public Uri? PictureUrl - { - get - { - var url = _user.Handle.GetProfileData(UserProfileField.PictureUrl); - return url != null ? new Uri(url) : null; - } - } - - /// - /// Gets the first name of the user. - /// - /// A string representing the user's first name or null if not available. - public string? FirstName => _user.Handle.GetProfileData(UserProfileField.FirstName); - - /// - /// Gets the last name of the user. - /// - /// A string representing the user's last name or null if not available. - public string? LastName => _user.Handle.GetProfileData(UserProfileField.LastName); - - /// - /// Gets the gender of the user. - /// - /// A string representing the user's gender or null if not available. - public string? Gender => _user.Handle.GetProfileData(UserProfileField.Gender); - - /// - /// Gets the birthday of the user. - /// - /// A string representing the user's birthday or null if not available. - public string? Birthday => _user.Handle.GetProfileData(UserProfileField.Birthday); - - /// - /// Gets the minimum age of the user. - /// - /// A string representing the user's minimum age or null if not available. - public string? MinAge => _user.Handle.GetProfileData(UserProfileField.MinAge); - - /// - /// Gets the maximum age of the user. - /// - /// A string representing the user's maximum age or null if not available. - public string? MaxAge => _user.Handle.GetProfileData(UserProfileField.MaxAge); - - internal UserProfile(User user) - { - _user = user; - } - } -} diff --git a/Realm/Realm/Sync/UserState.cs b/Realm/Realm/Sync/UserState.cs deleted file mode 100644 index 4ad62db2ae..0000000000 --- a/Realm/Realm/Sync/UserState.cs +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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. -// -//////////////////////////////////////////////////////////////////////////// - -namespace Realms.Sync -{ - /// - /// The state of the user object. - /// - public enum UserState - { - /// - /// The user is logged out. Call with valid credentials to log the user back in. - /// - LoggedOut, - - /// - /// The user is logged in, and any Realms associated with it are synchronizing with Atlas Device Sync. - /// - LoggedIn, - - /// - /// The user has been logged out and their local data has been removed. - /// - Removed - } -} \ No newline at end of file diff --git a/Tests/Benchmarks/Benchmarks/FodyWeavers.xml b/Tests/Benchmarks/Benchmarks/FodyWeavers.xml index 3e1a999a33..8a24e482bf 100644 --- a/Tests/Benchmarks/Benchmarks/FodyWeavers.xml +++ b/Tests/Benchmarks/Benchmarks/FodyWeavers.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/Tests/Benchmarks/PerformanceTests/FodyWeavers.xml b/Tests/Benchmarks/PerformanceTests/FodyWeavers.xml index 3e1a999a33..8a24e482bf 100644 --- a/Tests/Benchmarks/PerformanceTests/FodyWeavers.xml +++ b/Tests/Benchmarks/PerformanceTests/FodyWeavers.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/Tests/Realm.Tests/Database/DateTimeTests.cs b/Tests/Realm.Tests/Database/DateTimeTests.cs index c27bb8698d..1e5540f2e3 100644 --- a/Tests/Realm.Tests/Database/DateTimeTests.cs +++ b/Tests/Realm.Tests/Database/DateTimeTests.cs @@ -20,12 +20,8 @@ using System.Linq; using NUnit.Framework; #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; -using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else -using TestAsymmetricObject = Realms.IAsymmetricObject; -using TestEmbeddedObject = Realms.IEmbeddedObject; using TestRealmObject = Realms.IRealmObject; #endif diff --git a/Tests/Realm.Tests/Database/DynamicEmbeddedTests.cs b/Tests/Realm.Tests/Database/DynamicEmbeddedTests.cs index fc4f843254..b822e7f1d0 100644 --- a/Tests/Realm.Tests/Database/DynamicEmbeddedTests.cs +++ b/Tests/Realm.Tests/Database/DynamicEmbeddedTests.cs @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////////////////////// #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else diff --git a/Tests/Realm.Tests/Database/DynamicRelationshipTests.cs b/Tests/Realm.Tests/Database/DynamicRelationshipTests.cs index 129074e716..b7f9c93565 100644 --- a/Tests/Realm.Tests/Database/DynamicRelationshipTests.cs +++ b/Tests/Realm.Tests/Database/DynamicRelationshipTests.cs @@ -21,8 +21,6 @@ using System.Linq; using NUnit.Framework; #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; -using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else using TestRealmObject = Realms.IRealmObject; diff --git a/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs b/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs index 187af873d7..39ae4852f7 100644 --- a/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs +++ b/Tests/Realm.Tests/Database/GuidRepresentationMigrationTests.cs @@ -22,9 +22,7 @@ using MongoDB.Bson; using NUnit.Framework; using Realms.Logging; -using Realms.Tests.Sync; #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else @@ -35,7 +33,7 @@ namespace Realms.Tests.Database { [TestFixture, Preserve(AllMembers = true)] - public class GuidRepresentationMigrationTests : SyncTestBase + public class GuidRepresentationMigrationTests : RealmTest { private RealmConfiguration _configuration = null!; @@ -143,31 +141,6 @@ public void GuidRepresentationMatchesQuery([Values(true, false)] bool useLegacyR AssertQueryDescription(description, guidString, useLegacyRepresentation); } - [Test] - public void FlexibleSync_Subscriptions_MatchesGuid([Values(true, false)] bool useLegacyRepresentation) - { -#pragma warning disable CS0618 // Type or member is obsolete - Realm.UseLegacyGuidRepresentation = useLegacyRepresentation; -#pragma warning restore CS0618 // Type or member is obsolete - - var config = GetFakeFLXConfig(setFakeSyncRoute: true); - config.Schema = new[] { typeof(GuidType), typeof(EmbeddedGuidType) }; - using var realm = GetRealm(config); - - var guidString = "981b8fa2-c496-43b0-b401-48ce08b38e00"; - var guid = Guid.Parse(guidString); - - realm.Subscriptions.Update(() => - { - var query = (RealmResults)realm.All().Where(t => t.RegularProperty == guid); - realm.Subscriptions.Add(query); - }); - - var description = realm.Subscriptions.Single().Query; - - AssertQueryDescription(description, guidString, useLegacyRepresentation); - } - [Test] public void UnmigratedRealm_WhenOpenedAsReadonly_LogsAMessageAndDoesntChangeFile() { @@ -298,53 +271,6 @@ public void Migration_FromLittleEndian_WhenContainingBothGoodAndBadGuids_LogsWar Assert.That(logger.GetLog(), Does.Contain("found to contain Guid values in little-endian format and was automatically migrated")); } - [Test] - public void SynchronizedRealm_DoesntMigrate([Values(true, false)] bool useLegacyRepresentation) - { - var logger = new RealmLogger.InMemoryLogger(); - RealmLogger.Default = logger; - -#pragma warning disable CS0618 // Type or member is obsolete - Realm.UseLegacyGuidRepresentation = useLegacyRepresentation; -#pragma warning restore CS0618 // Type or member is obsolete - - var expected = GetGuidObjects().ToArray(); - - var config = GetFakeConfig(userId: "sync-guids-test-user", setFakeSyncRoute: true); - config.Schema = new[] { typeof(GuidType), typeof(EmbeddedGuidType) }; - - TestHelpers.CopyBundledFileToDocuments("sync-guids.realm", config.DatabasePath); - using var realm = GetRealm(config); - - var actual = realm.All().ToArray(); - - Assert.That(actual.Length, Is.EqualTo(expected.Length)); - - if (useLegacyRepresentation) - { - foreach (var expectedObj in expected) - { - var actualObj = actual.Single(o => o.Id == expectedObj.Id); - - AssertEqual(expectedObj, actualObj); - - var actualFound = realm.Find(expectedObj.Id); - Assert.That(actualObj, Is.EqualTo(actualFound)); - } - } - else - { - foreach (var expectedObj in expected) - { - var flipped = FlipGuid(expectedObj.Id); - var actualObj = actual.Single(o => o.Id == flipped); - Assert.That(actualObj.RegularProperty, Is.EqualTo(FlipGuid(expectedObj.RegularProperty))); - } - } - - Assert.That(logger.GetLog(), Does.Not.Contain("migrated")); - } - protected override void CustomSetUp() { _configuration = new RealmConfiguration(Guid.NewGuid().ToString()) diff --git a/Tests/Realm.Tests/Database/InstanceTests.cs b/Tests/Realm.Tests/Database/InstanceTests.cs index 0383366379..ac4ba96d29 100644 --- a/Tests/Realm.Tests/Database/InstanceTests.cs +++ b/Tests/Realm.Tests/Database/InstanceTests.cs @@ -318,7 +318,7 @@ public void RealmObjectClassesOnlyAllowRealmObjects() })!; Assert.That(ex.Message, Does.Contain("System.Object")); - Assert.That(ex.Message, Does.Contain("must descend directly from either RealmObject, EmbeddedObject, or AsymmetricObject")); + Assert.That(ex.Message, Does.Contain("must descend directly from either RealmObject or EmbeddedObject")); } [TestCase(false, true)] @@ -733,7 +733,6 @@ public void GetInstance_WhenDynamic_ReadsSchemaFromDisk() Assert.That(dynamicRealm.Schema.TryFindObjectSchema(nameof(AllTypesObject), out var allTypesSchema), Is.True); Assert.That(allTypesSchema, Is.Not.Null); Assert.That(allTypesSchema!.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(allTypesSchema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); var hasExpectedProp = allTypesSchema.TryFindProperty(nameof(AllTypesObject.RequiredStringProperty), out var requiredStringProp); Assert.That(hasExpectedProp); diff --git a/Tests/Realm.Tests/Database/LoggerTests.cs b/Tests/Realm.Tests/Database/LoggerTests.cs index 522b0f3b72..2dcfb378a1 100644 --- a/Tests/Realm.Tests/Database/LoggerTests.cs +++ b/Tests/Realm.Tests/Database/LoggerTests.cs @@ -43,19 +43,6 @@ public UserDefinedLogger(Action logFunction) protected override void LogImpl(LogLevel level, LogCategory category, string message) => _logFunction(level, category, message); } - [Obsolete("Using obsolete logger.")] - private class ObsoleteUserDefinedLogger : Logger - { - private readonly Action _logFunction; - - public ObsoleteUserDefinedLogger(Action logFunction) - { - _logFunction = logFunction; - } - - protected override void LogImpl(LogLevel level, string message) => _logFunction(level, message); - } - [SetUp] public void Setup() { @@ -101,19 +88,6 @@ public void Logger_CanSetDefaultLoggerToUserDefinedLogger() AssertLogMessageContains(messages[0], LogLevel.Warn, LogCategory.Realm.SDK, "A log message"); } - [Test] - [Obsolete("Using obsolete logger class.")] - public void ObsoleteLogger_CanSetDefaultLoggerToUserDefinedLogger() - { - var messages = new List(); - Logger.Default = new ObsoleteUserDefinedLogger((level, message) => messages.Add(Logger.FormatLog(level, LogCategory.Realm.SDK, message))); - - Logger.Default.Log(LogLevel.Warn, "A log message"); - - Assert.That(messages.Count, Is.EqualTo(1)); - AssertLogMessageContains(messages[0], LogLevel.Warn, LogCategory.Realm.SDK, "A log message"); - } - [Test] public void Logger_SkipsDebugMessagesByDefault() { @@ -158,18 +132,6 @@ public void Logger_SetsLogLevelAtSubcategories() } } - [Test] - [Obsolete("Using LogLevel set accessor.")] - public void Logger_WhenUsingLogLevelSetter_OverwritesCategory() - { - var category = LogCategory.Realm.Storage; - RealmLogger.SetLogLevel(LogLevel.Error, category); - Assert.That(RealmLogger.GetLogLevel(category), Is.EqualTo(LogLevel.Error)); - - RealmLogger.LogLevel = LogLevel.All; - Assert.That(RealmLogger.GetLogLevel(category), Is.EqualTo(LogLevel.All)); - } - [TestCase(LogLevel.Error)] [TestCase(LogLevel.Info)] [TestCase(LogLevel.Debug)] @@ -232,20 +194,7 @@ public void Logger_CallsCustomFunction() AssertLogMessageContains(messages[0], LogLevel.Warn, LogCategory.Realm.SDK, "A log message"); } - [Test] - [Obsolete("Using function not accepting category.")] - public void Logger_CallsObsoleteCustomFunction() - { - var messages = new List(); - RealmLogger.Default = RealmLogger.Function((level, message) => messages.Add(RealmLogger.FormatLog(level, LogCategory.Realm.SDK, message))); - - RealmLogger.Default.Log(LogLevel.Warn, "A log message"); - - Assert.That(messages.Count, Is.EqualTo(1)); - AssertLogMessageContains(messages[0], LogLevel.Warn, LogCategory.Realm.SDK, "A log message"); - } - - [Test] + [Test, Ignore("We have fewer log categories since we're manually removing the Sync-related ones")] public void Logger_MatchesCoreCategoryNames() { var coreCategoryNames = SharedRealmHandle.GetLogCategoryNames(); @@ -254,9 +203,9 @@ public void Logger_MatchesCoreCategoryNames() Assert.That(sdkCategoriesMap.Count, Is.EqualTo(coreCategoryNames.Length)); foreach (var name in coreCategoryNames) { - Assert.That(sdkCategoriesMap.TryGetValue(name!, out var category), Is.True); + Assert.That(sdkCategoriesMap.TryGetValue(name, out var category), Is.True); Assert.That(category!.Name, Is.EqualTo(name)); - Assert.That(LogCategory.FromName(name!), Is.SameAs(category)); + Assert.That(LogCategory.FromName(name), Is.SameAs(category)); } } diff --git a/Tests/Realm.Tests/Database/NotificationTests.cs b/Tests/Realm.Tests/Database/NotificationTests.cs index a1f7eee2ca..d6c2539dcd 100644 --- a/Tests/Realm.Tests/Database/NotificationTests.cs +++ b/Tests/Realm.Tests/Database/NotificationTests.cs @@ -2331,7 +2331,7 @@ void OnNotification(IRealmCollection s, ChangeSet? changes) var dog = new Dog(); _realm.Write(() => _realm.Add(dog)); - using (query.SubscribeForNotifications(OnNotification, KeyPathsCollection.Of("Owners"))) + using (query.SubscribeForNotifications(OnNotification, KeyPathsCollection.Of("Owners.*"))) { var owner = new Owner { Name = "Mario", ListOfDogs = { dog } }; _realm.Write(() => _realm.Add(owner)); diff --git a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs index 79b989bec2..fab066890e 100644 --- a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs +++ b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs @@ -530,7 +530,6 @@ public void ObjectSchemaBuilder_FromType_AddsCorrectProperties() var builder = new ObjectSchema.Builder(typeof(ClassWithUnqueryableMembers)); Assert.That(builder.Name, Is.EqualTo(nameof(ClassWithUnqueryableMembers))); Assert.That(builder.RealmSchemaType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(builder.RealmSchemaType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); Assert.That(builder.Contains(nameof(ClassWithUnqueryableMembers.PublicField)), Is.False); Assert.That(builder.Contains(nameof(ClassWithUnqueryableMembers.PublicMethod)), Is.False); @@ -554,7 +553,6 @@ public void ObjectSchemaBuilder_FromType_CanAddProperties() var builder = new ObjectSchema.Builder(typeof(PrimaryKeyGuidObject)); Assert.That(builder.Name, Is.EqualTo(nameof(PrimaryKeyGuidObject))); Assert.That(builder.RealmSchemaType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(builder.RealmSchemaType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); Assert.That(builder.Count, Is.EqualTo(1)); @@ -885,7 +883,6 @@ public void ObjectSchema_FromType_RemappedType() Assert.That(schema.Name, Is.EqualTo("__RemappedTypeObject")); Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); Assert.That(schema.TryFindProperty("__mappedLink", out var remappedProp), Is.True); Assert.That(remappedProp.Type, Is.EqualTo(PropertyType.Object | PropertyType.Nullable)); Assert.That(remappedProp.ObjectType, Is.EqualTo("__RemappedTypeObject")); @@ -898,7 +895,6 @@ public void ObjectSchema_FromType_ResolvesPrimaryKey() Assert.That(schema.Name, Is.EqualTo(nameof(PrimaryKeyStringObject))); Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); Assert.That(schema.PrimaryKeyProperty, Is.Not.Null); Assert.That(schema.PrimaryKeyProperty!.Value.IsPrimaryKey, Is.True); Assert.That(schema.PrimaryKeyProperty.Value.Type, Is.EqualTo(PropertyType.NullableString)); @@ -1564,7 +1560,6 @@ private static void ValidateBuiltSchema(ObjectSchema.Builder builder, params Pro var schema = builder.Build(); Assert.That(schema.Name, Is.EqualTo("MyClass")); Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.EmbeddedObject)); - Assert.That(schema.BaseType, Is.Not.EqualTo(ObjectSchema.ObjectType.AsymmetricObject)); Assert.That(schema.Count, Is.EqualTo(expectedProperties.Length)); foreach (var prop in expectedProperties) diff --git a/Tests/Realm.Tests/Database/PropertyChangedTests.cs b/Tests/Realm.Tests/Database/PropertyChangedTests.cs index 764471a8a1..1addb472ce 100644 --- a/Tests/Realm.Tests/Database/PropertyChangedTests.cs +++ b/Tests/Realm.Tests/Database/PropertyChangedTests.cs @@ -23,8 +23,6 @@ using System.Threading.Tasks; using NUnit.Framework; #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; -using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else using TestRealmObject = Realms.IRealmObject; diff --git a/Tests/Realm.Tests/Database/RealmResults/SimpleLINQtests.cs b/Tests/Realm.Tests/Database/RealmResults/SimpleLINQtests.cs index 856d73682d..b045f0894d 100644 --- a/Tests/Realm.Tests/Database/RealmResults/SimpleLINQtests.cs +++ b/Tests/Realm.Tests/Database/RealmResults/SimpleLINQtests.cs @@ -21,6 +21,7 @@ using NUnit.Framework; using Realms.Exceptions; +// ReSharper disable NegativeEqualityExpression ReplaceWithSingleCallToCount ReturnValueOfPureMethodIsNotUsed ReplaceWithSingleCallToAny ReplaceWithSingleCallToSingle ReplaceWithSingleCallToSingleOrDefault ReplaceWithSingleCallToFirst ReplaceWithSingleCallToFirstOrDefault ReplaceWithSingleCallToLast ReplaceWithSingleCallToLastOrDefault namespace Realms.Tests.Database { [TestFixture, Preserve(AllMembers = true)] @@ -696,18 +697,6 @@ public void StringSearch_Contains_CaseSensitivityTests() // ignore case Assert.That(_realm.All().Where(p => QueryMethods.Contains(p.FirstName, "atri", StringComparison.OrdinalIgnoreCase)).Count(), Is.EqualTo(3)); Assert.That(_realm.All().Where(p => QueryMethods.Contains(p.FirstName, "atri", StringComparison.OrdinalIgnoreCase)).ToArray().Length, Is.EqualTo(3)); - -#pragma warning disable CS0618 // Type or member is obsolete - - // case sensitive obsolete extensions - Assert.That(_realm.All().Where(p => p.FirstName!.Contains("atri", StringComparison.Ordinal)).Count(), Is.EqualTo(2)); - Assert.That(_realm.All().Where(p => p.FirstName!.Contains("atri", StringComparison.Ordinal)).Count(), Is.EqualTo(2)); - - // ignore case obsolete extensions - Assert.That(_realm.All().Where(p => p.FirstName!.Contains("atri", StringComparison.OrdinalIgnoreCase)).Count(), Is.EqualTo(3)); - Assert.That(_realm.All().Where(p => p.FirstName!.Contains("atri", StringComparison.OrdinalIgnoreCase)).ToArray().Length, Is.EqualTo(3)); - -#pragma warning restore CS0618 // Type or member is obsolete } [Test] @@ -728,18 +717,6 @@ public void StringSearch_Contains_NonLiteral_CaseSensitivityTests() // ignore case Assert.That(_realm.All().Where(p => QueryMethods.Contains(p.FirstName, searchString, StringComparison.OrdinalIgnoreCase)).Count(), Is.EqualTo(3)); Assert.That(_realm.All().Where(p => QueryMethods.Contains(p.FirstName, searchString, StringComparison.OrdinalIgnoreCase)).ToArray().Length, Is.EqualTo(3)); - -#pragma warning disable CS0618 // Type or member is obsolete - - // case sensitive obsolete extensions - Assert.That(_realm.All().Where(p => p.FirstName!.Contains(searchString, StringComparison.Ordinal)).Count(), Is.EqualTo(2)); - Assert.That(_realm.All().Where(p => p.FirstName!.Contains(searchString, StringComparison.Ordinal)).ToArray().Length, Is.EqualTo(2)); - - // ignore case obsolete extensions - Assert.That(_realm.All().Where(p => p.FirstName!.Contains(searchString, StringComparison.OrdinalIgnoreCase)).Count(), Is.EqualTo(3)); - Assert.That(_realm.All().Where(p => p.FirstName!.Contains(searchString, StringComparison.OrdinalIgnoreCase)).ToArray().Length, Is.EqualTo(3)); - -#pragma warning restore CS0618 // Type or member is obsolete } [Test] @@ -781,13 +758,6 @@ public void StringSearch_InvalidStringComparisonTests() { _realm.All().Where(p => QueryMethods.Contains(p.FirstName, "atri", StringComparison.CurrentCulture)).Count(); }, Throws.TypeOf()); - - Assert.That(() => - { -#pragma warning disable CS0618 // Type or member is obsolete - _realm.All().Where(p => p.FirstName!.Contains("atri", StringComparison.CurrentCulture)).Count(); -#pragma warning restore CS0618 // Type or member is obsolete - }, Throws.TypeOf()); } [TestCaseSource(nameof(LikeTestValues))] @@ -798,24 +768,12 @@ public void StringSearch_LikeTests(string str, string pattern, bool caseSensitiv var regularQuery = _realm.All().Where(o => QueryMethods.Like(o.StringValue, pattern, caseSensitive)); var negatedQuery = _realm.All().Where(o => !QueryMethods.Like(o.StringValue, pattern, caseSensitive)); -#pragma warning disable CS0618 // Type or member is obsolete - - var obsoleteRegularQuery = _realm.All().Where(o => o.StringValue!.Like(pattern, caseSensitive)); - var obsoleteNegatedQuery = _realm.All().Where(o => !o.StringValue!.Like(pattern, caseSensitive)); - -#pragma warning restore CS0618 // Type or member is obsolete - if (expected) { Assert.That(regularQuery.Count(), Is.EqualTo(1)); Assert.That(regularQuery.Single().Id, Is.EqualTo(1)); Assert.That(negatedQuery.Count(), Is.EqualTo(0)); - - Assert.That(obsoleteRegularQuery.Count(), Is.EqualTo(1)); - Assert.That(obsoleteRegularQuery.Single().Id, Is.EqualTo(1)); - - Assert.That(obsoleteNegatedQuery.Count(), Is.EqualTo(0)); } else { @@ -823,11 +781,6 @@ public void StringSearch_LikeTests(string str, string pattern, bool caseSensitiv Assert.That(negatedQuery.Count(), Is.EqualTo(1)); Assert.That(negatedQuery.Single().Id, Is.EqualTo(1)); - - Assert.That(obsoleteRegularQuery.Count(), Is.EqualTo(0)); - - Assert.That(obsoleteNegatedQuery.Count(), Is.EqualTo(1)); - Assert.That(obsoleteNegatedQuery.Single().Id, Is.EqualTo(1)); } } diff --git a/Tests/Realm.Tests/Database/RelationshipTests.cs b/Tests/Realm.Tests/Database/RelationshipTests.cs index f1f70d304c..d12e4bb554 100644 --- a/Tests/Realm.Tests/Database/RelationshipTests.cs +++ b/Tests/Realm.Tests/Database/RelationshipTests.cs @@ -21,8 +21,6 @@ using System.Linq; using NUnit.Framework; #if TEST_WEAVER -using TestAsymmetricObject = Realms.AsymmetricObject; -using TestEmbeddedObject = Realms.EmbeddedObject; using TestRealmObject = Realms.RealmObject; #else using TestRealmObject = Realms.IRealmObject; diff --git a/Tests/Realm.Tests/Database/StringExtensionsTests.cs b/Tests/Realm.Tests/Database/StringExtensionsTests.cs deleted file mode 100644 index 49e0e61096..0000000000 --- a/Tests/Realm.Tests/Database/StringExtensionsTests.cs +++ /dev/null @@ -1,118 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// 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; -using NUnit.Framework; - -namespace Realms.Tests.Database -{ - /// - /// Tests for the standalone (non-query) versions of our string extensions. - /// - [TestFixture, Preserve(AllMembers = true)] - public class StringExtensionsTests - { -#if !NETCOREAPP2_1_OR_GREATER - public static object[] ContainsTestValues = - { - new object[] { "text", "x", StringComparison.Ordinal, true }, - new object[] { "teXt", "x", StringComparison.Ordinal, false }, - new object[] { "teXt", "X", StringComparison.Ordinal, true }, - new object[] { "teXt", "x", StringComparison.OrdinalIgnoreCase, true }, - new object[] { "teXt", "X", StringComparison.OrdinalIgnoreCase, true }, - new object[] { "Text", "t", StringComparison.Ordinal, true }, - new object[] { "Text", "T", StringComparison.Ordinal, true }, - new object[] { "texT", "ext", StringComparison.Ordinal, false }, - new object[] { "texT", "ext", StringComparison.OrdinalIgnoreCase, true }, - new object[] { "teXt", "test", StringComparison.Ordinal, false }, - new object[] { "teXt", "test", StringComparison.OrdinalIgnoreCase, false }, - new object[] { "teXt", string.Empty, StringComparison.OrdinalIgnoreCase, true }, - new object[] { "teXt", string.Empty, StringComparison.Ordinal, true }, - new object[] { string.Empty, "a", StringComparison.Ordinal, false }, - new object[] { string.Empty, "a", StringComparison.OrdinalIgnoreCase, false }, - new object[] { string.Empty, string.Empty, StringComparison.Ordinal, true }, - - // "".IndexOf("", StringComparison.OrdinalIgnoreCase) == -1 for .NET Native ?! -#if DEBUG - new object[] { string.Empty, string.Empty, StringComparison.OrdinalIgnoreCase, true } -#endif - }; - - [TestCaseSource(nameof(ContainsTestValues)), Obsolete("Tests obsolete extension methods")] - public void ContainsTests(string str, string value, StringComparison comparisonType, bool expected) - { - var result = StringExtensions.Contains(str, value, comparisonType); - Assert.That(result, Is.EqualTo(expected)); - } - - public static object?[] InvalidContainsTestCases = - { - new object?[] { "text", null, StringComparison.Ordinal, typeof(ArgumentNullException) }, - new object?[] { "teXt", "X", (StringComparison)123, typeof(ArgumentException) } - }; - - [TestCaseSource(nameof(InvalidContainsTestCases)), Obsolete("Tests obsolete extension methods")] - public void Contains_TestInvalidArgumentCases(string original, string value, StringComparison comparisonType, Type exceptionType) - { - Assert.That(() => StringExtensions.Contains(original, value, comparisonType), Throws.TypeOf(exceptionType)); - } -#endif - - [TestCaseSource(nameof(LikeTestValues)), Obsolete("Tests obsolete extension methods")] - public void LikeTests(string str, string value, bool caseSensitive, bool expected) - { - var result = StringExtensions.Like(str, value, caseSensitive); - Assert.That(result, Is.EqualTo(expected)); - } - - public static object?[] LikeTestValues = - { - new object?[] { string.Empty, string.Empty, true, true }, - new object?[] { string.Empty, string.Empty, false, true }, - new object?[] { null, null, true, true }, - new object?[] { null, null, false, true }, - new object?[] { "abc", string.Empty, true, false }, - new object?[] { string.Empty, "abc", true, false }, - new object?[] { "abcd", "abc", true, false }, - - new object?[] { "abc", "*a*", true, true }, - new object?[] { "abc", "*b*", true, true }, - new object?[] { "abc", "*c", true, true }, - new object?[] { "abc", "ab*", true, true }, - new object?[] { "abc", "*bc", true, true }, - new object?[] { "abc", "a*bc", true, true }, - new object?[] { "abc", "*abc*", true, true }, - new object?[] { "abc", "*d*", true, false }, - new object?[] { "abc", "aabc", true, false }, - new object?[] { "abc", "b*bc", true, false }, - - new object?[] { "abc", "a??", true, true }, - new object?[] { "abc", "?b?", true, true }, - new object?[] { "abc", "*?c", true, true }, - new object?[] { "abc", "ab?", true, true }, - new object?[] { "abc", "?bc", true, true }, - new object?[] { "abc", "?d?", true, false }, - new object?[] { "abc", "?abc", true, false }, - new object?[] { "abc", "b?bc", true, false }, - - new object?[] { "abc", "*C*", true, false }, - new object?[] { "abc", "*c*", false, true }, - new object?[] { "abc", "*C*", false, true }, - }; - } -} diff --git a/Tests/Realm.Tests/Database/TestHelpersTests.cs b/Tests/Realm.Tests/Database/TestHelpersTests.cs index c33ada9f3d..4305cf7952 100644 --- a/Tests/Realm.Tests/Database/TestHelpersTests.cs +++ b/Tests/Realm.Tests/Database/TestHelpersTests.cs @@ -40,7 +40,7 @@ public void RunAsyncTest_ContinuesOnSameThread() [Test] public void RunAsyncTest_Timeouts() { - Assert.Throws(() => TestHelpers.RunAsyncTest(async () => await Task.Delay(100), timeout: 50)); + Assert.Throws(() => TestHelpers.RunAsyncTest(async () => await Task.Delay(1000), timeout: 50)); } } } diff --git a/Tests/Realm.Tests/FodyWeavers.xml b/Tests/Realm.Tests/FodyWeavers.xml index 6769f96097..ef20bfeea8 100644 --- a/Tests/Realm.Tests/FodyWeavers.xml +++ b/Tests/Realm.Tests/FodyWeavers.xml @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricObjectWithAllTypes_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricObjectWithAllTypes_generated.cs index 33b7c863d1..95f27265b8 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricObjectWithAllTypes_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricObjectWithAllTypes_generated.cs @@ -21,7 +21,6 @@ using System.Runtime.Serialization; using System.Threading.Tasks; using System.Xml.Serialization; -using TestAsymmetricObject = Realms.IAsymmetricObject; namespace Realms.Tests.Sync { diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedGuidType_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedGuidType_generated.cs index c96482794d..0c37456308 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedGuidType_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedGuidType_generated.cs @@ -8,7 +8,6 @@ using Realms.Logging; using Realms.Schema; using Realms.Tests.Database; -using Realms.Tests.Sync; using Realms.Weaving; using System; using System.Collections.Generic; diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/GuidType_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/GuidType_generated.cs index 85a68ed3d6..8b9ea3b9d5 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/GuidType_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/GuidType_generated.cs @@ -8,7 +8,6 @@ using Realms.Logging; using Realms.Schema; using Realms.Tests.Database; -using Realms.Tests.Sync; using Realms.Weaving; using System; using System.Collections.Generic; diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedDateTimeOffsetObject_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedDateTimeOffsetObject_generated.cs index f8e2730620..966d0276dd 100644 --- a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedDateTimeOffsetObject_generated.cs +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedDateTimeOffsetObject_generated.cs @@ -15,8 +15,6 @@ using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Xml.Serialization; -using TestAsymmetricObject = Realms.IAsymmetricObject; -using TestEmbeddedObject = Realms.IEmbeddedObject; using TestRealmObject = Realms.IRealmObject; namespace Realms.Tests.Database diff --git a/Tests/Realm.Tests/Program.cs b/Tests/Realm.Tests/Program.cs index ee7dfb8ed2..bfb7cd7bf5 100644 --- a/Tests/Realm.Tests/Program.cs +++ b/Tests/Realm.Tests/Program.cs @@ -32,8 +32,6 @@ public static int Main(string[] args) var autorun = new AutoRun(typeof(Program).GetTypeInfo().Assembly); IDisposable? logger = null; - (args, logger) = Sync.SyncTestHelpers.SetLoggerFromArgs(args); - args = Sync.SyncTestHelpers.ExtractBaasSettings(args); autorun.Execute(args); diff --git a/Tests/Realm.Tests/Realm.Tests.csproj b/Tests/Realm.Tests/Realm.Tests.csproj index dc7fa2dacc..32f5ca3239 100644 --- a/Tests/Realm.Tests/Realm.Tests.csproj +++ b/Tests/Realm.Tests/Realm.Tests.csproj @@ -56,10 +56,6 @@ $(RealmDllsPath)\Realm.dll - - $(RealmDllsPath)\Realm.PlatformHelpers.dll - -