From 3ad185ec51e8fd3ff1cb426f8213a520fba24ff0 Mon Sep 17 00:00:00 2001 From: Flor Elisa Chacon Ochoa Date: Mon, 18 May 2026 15:57:37 -0700 Subject: [PATCH 1/6] Make sfs a vcpkg port --- azure-pipelines.yml | 25 + cgmanifest.json | 2 +- src/AppInstallerCLI.sln | 33 +- .../AppInstallerCLITests.vcxproj | 15 +- .../AppInstallerCommonCore.vcxproj | 18 +- src/SfsClient/SfsClient.vcxproj | 291 -------- src/SfsClient/SfsClient.vcxproj.filters | 159 ---- src/SfsClient/readme.md | 19 - src/SfsClient/sfs-client/.clang-format | 29 - src/SfsClient/sfs-client/.cmake-format.json | 7 - src/SfsClient/sfs-client/.github/CODEOWNERS | 2 - .../.github/ISSUE_TEMPLATE/bug_report.md | 24 - .../.github/ISSUE_TEMPLATE/feature_request.md | 20 - .../sfs-client/.github/dependabot.yml | 23 - .../.github/pull_request_template.md | 15 - .../workflows/initialize-codeql/action.yml | 12 - .../workflows/install-winget/action.yml | 56 -- .../.github/workflows/main-build-ubuntu.yml | 46 -- .../.github/workflows/main-build-windows.yml | 53 -- .../sfs-client/.github/workflows/pr.yml | 87 --- src/SfsClient/sfs-client/.gitignore | 45 -- src/SfsClient/sfs-client/API.md | 59 -- src/SfsClient/sfs-client/CMakeLists.txt | 79 -- src/SfsClient/sfs-client/CODE_OF_CONDUCT.md | 9 - src/SfsClient/sfs-client/DEVELOPMENT.md | 6 - src/SfsClient/sfs-client/LICENSE | 21 - src/SfsClient/sfs-client/NOTICE.md | 441 ----------- src/SfsClient/sfs-client/README.md | 195 ----- src/SfsClient/sfs-client/SECURITY.md | 41 - src/SfsClient/sfs-client/SUPPORT.md | 11 - src/SfsClient/sfs-client/TEST.md | 12 - src/SfsClient/sfs-client/cgmanifest.json | 85 --- .../sfs-client/client/CMakeLists.txt | 147 ---- .../client/include/sfsclient/AppContent.h | 87 --- .../client/include/sfsclient/AppFile.h | 59 -- .../include/sfsclient/ApplicabilityDetails.h | 42 -- .../client/include/sfsclient/ClientConfig.h | 34 - .../client/include/sfsclient/Content.h | 62 -- .../client/include/sfsclient/ContentId.h | 48 -- .../client/include/sfsclient/File.h | 70 -- .../client/include/sfsclient/Logging.h | 33 - .../client/include/sfsclient/RequestParams.h | 46 -- .../client/include/sfsclient/Result.h | 89 --- .../client/include/sfsclient/SFSClient.h | 80 -- .../sfs-client/client/src/AppContent.cpp | 90 --- .../sfs-client/client/src/AppFile.cpp | 60 -- .../client/src/ApplicabilityDetails.cpp | 35 - .../sfs-client/client/src/Content.cpp | 91 --- .../sfs-client/client/src/ContentId.cpp | 49 -- src/SfsClient/sfs-client/client/src/File.cpp | 68 -- .../sfs-client/client/src/Logging.cpp | 22 - .../sfs-client/client/src/Result.cpp | 127 ---- .../sfs-client/client/src/SFSClient.cpp | 57 -- .../client/src/details/ContentUtil.cpp | 117 --- .../client/src/details/ContentUtil.h | 61 -- .../client/src/details/CorrelationVector.cpp | 55 -- .../client/src/details/CorrelationVector.h | 40 - .../sfs-client/client/src/details/Env.cpp | 84 --- .../sfs-client/client/src/details/Env.h | 40 - .../client/src/details/ErrorHandling.cpp | 75 -- .../client/src/details/ErrorHandling.h | 87 --- .../sfs-client/client/src/details/OSInfo.cpp | 84 --- .../sfs-client/client/src/details/OSInfo.h | 12 - .../client/src/details/ReportingHandler.cpp | 28 - .../client/src/details/ReportingHandler.h | 80 -- .../client/src/details/SFSClientImpl.cpp | 415 ----------- .../client/src/details/SFSClientImpl.h | 114 --- .../client/src/details/SFSClientInterface.h | 104 --- .../client/src/details/SFSException.cpp | 28 - .../client/src/details/SFSException.h | 28 - .../client/src/details/SFSUrlBuilder.cpp | 81 -- .../client/src/details/SFSUrlBuilder.h | 47 -- .../client/src/details/TestOverride.cpp | 59 -- .../client/src/details/TestOverride.h | 52 -- .../client/src/details/UrlBuilder.cpp | 192 ----- .../client/src/details/UrlBuilder.h | 144 ---- .../sfs-client/client/src/details/Util.cpp | 21 - .../sfs-client/client/src/details/Util.h | 12 - .../src/details/connection/Connection.cpp | 21 - .../src/details/connection/Connection.h | 57 -- .../details/connection/ConnectionConfig.cpp | 16 - .../src/details/connection/ConnectionConfig.h | 30 - .../details/connection/ConnectionManager.cpp | 16 - .../details/connection/ConnectionManager.h | 28 - .../src/details/connection/CurlConnection.cpp | 432 ----------- .../src/details/connection/CurlConnection.h | 64 -- .../connection/CurlConnectionManager.cpp | 53 -- .../connection/CurlConnectionManager.h | 24 - .../src/details/connection/HttpHeader.cpp | 38 - .../src/details/connection/HttpHeader.h | 21 - .../connection/mock/MockConnection.cpp | 26 - .../details/connection/mock/MockConnection.h | 23 - .../connection/mock/MockConnectionManager.cpp | 21 - .../connection/mock/MockConnectionManager.h | 24 - .../client/src/details/entity/ContentType.cpp | 19 - .../client/src/details/entity/ContentType.h | 17 - .../client/src/details/entity/FileEntity.cpp | 289 -------- .../client/src/details/entity/FileEntity.h | 71 -- .../src/details/entity/VersionEntity.cpp | 153 ---- .../client/src/details/entity/VersionEntity.h | 61 -- .../sfs-client/client/tests/CMakeLists.txt | 64 -- .../tests/functional/SFSClientTests.cpp | 461 ------------ .../details/CurlConnectionTests.cpp | 611 --------------- .../functional/details/SFSClientImplTests.cpp | 358 --------- .../client/tests/mock/MockWebServer.cpp | 698 ------------------ .../client/tests/mock/MockWebServer.h | 68 -- .../client/tests/mock/ProxyServer.cpp | 97 --- .../client/tests/mock/ProxyServer.h | 34 - .../client/tests/mock/ServerCommon.cpp | 121 --- .../client/tests/mock/ServerCommon.h | 84 --- .../client/tests/unit/AppContentTests.cpp | 215 ------ .../client/tests/unit/AppFileTests.cpp | 117 --- .../tests/unit/ApplicabilityDetailsTests.cpp | 61 -- .../client/tests/unit/ContentIdTests.cpp | 69 -- .../client/tests/unit/ContentTests.cpp | 160 ---- .../client/tests/unit/FileTests.cpp | 72 -- .../client/tests/unit/ResultTests.cpp | 58 -- .../client/tests/unit/SFSClientTests.cpp | 338 --------- .../details/CurlConnectionManagerTests.cpp | 48 -- .../unit/details/CurlConnectionTests.cpp | 106 --- .../client/tests/unit/details/EnvTests.cpp | 119 --- .../tests/unit/details/ErrorHandlingTests.cpp | 255 ------- .../unit/details/ReportingHandlerTests.cpp | 190 ----- .../tests/unit/details/SFSClientImplTests.cpp | 410 ---------- .../tests/unit/details/SFSUrlBuilderTests.cpp | 100 --- .../tests/unit/details/TestOverrideTests.cpp | 131 ---- .../tests/unit/details/UrlBuilderTests.cpp | 143 ---- .../client/tests/unit/details/UtilTests.cpp | 32 - .../unit/details/entity/FileEntityTests.cpp | 639 ---------------- .../details/entity/VersionEntityTests.cpp | 305 -------- .../client/tests/util/SFSExceptionMatcher.cpp | 59 -- .../client/tests/util/SFSExceptionMatcher.h | 51 -- .../client/tests/util/TestHelper.cpp | 49 -- .../sfs-client/client/tests/util/TestHelper.h | 26 - .../sfs-client/cmake/SFSOptions.cmake | 15 - .../cmake/sfsclient-config.cmake.in | 16 - .../sfs-client/pre-commit-wrapper.sh | 18 - src/SfsClient/sfs-client/pre-commit.sh | 152 ---- .../sfs-client/samples/CMakeLists.txt | 5 - src/SfsClient/sfs-client/samples/README.md | 9 - .../integration-do-client/CMakeLists.txt | 62 -- .../IntegrationDOClient.cpp | 333 --------- .../sfs-client/samples/tool/CMakeLists.txt | 21 - .../sfs-client/samples/tool/SFSClientTool.cpp | 548 -------------- src/SfsClient/sfs-client/scripts/Build.ps1 | 90 --- src/SfsClient/sfs-client/scripts/Setup.ps1 | 169 ----- src/SfsClient/sfs-client/scripts/Test.ps1 | 28 - src/SfsClient/sfs-client/scripts/build.sh | 163 ---- .../sfs-client/scripts/check-format.py | 66 -- .../sfs-client/scripts/pip.requirements.txt | 2 - src/SfsClient/sfs-client/scripts/setup.sh | 190 ----- src/SfsClient/sfs-client/scripts/test.sh | 60 -- .../sfs-client/portfile.cmake | 37 - .../x64-windows-static-custom.cmake | 6 - src/SfsClient/sfs-client/vcpkg.json | 64 -- src/VcpkgPortOverlay/CreatePortOverlay.ps1 | 220 +++++- src/VcpkgPortOverlay/README.md | 73 +- .../add-server-certificate-validation.patch | 169 +++++ ...ove-unconditional-toolchain-override.patch | 12 + .../sfs-client/portfile.cmake | 30 + ...ove-unconditional-toolchain-override.patch | 12 + .../sfs-client/usage | 8 +- .../sfs-client/vcpkg.json | 4 +- .../WindowsPackageManager.vcxproj | 3 - src/vcpkg.json | 1 + 165 files changed, 534 insertions(+), 15301 deletions(-) delete mode 100644 src/SfsClient/SfsClient.vcxproj delete mode 100644 src/SfsClient/SfsClient.vcxproj.filters delete mode 100644 src/SfsClient/readme.md delete mode 100644 src/SfsClient/sfs-client/.clang-format delete mode 100644 src/SfsClient/sfs-client/.cmake-format.json delete mode 100644 src/SfsClient/sfs-client/.github/CODEOWNERS delete mode 100644 src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 src/SfsClient/sfs-client/.github/dependabot.yml delete mode 100644 src/SfsClient/sfs-client/.github/pull_request_template.md delete mode 100644 src/SfsClient/sfs-client/.github/workflows/initialize-codeql/action.yml delete mode 100644 src/SfsClient/sfs-client/.github/workflows/install-winget/action.yml delete mode 100644 src/SfsClient/sfs-client/.github/workflows/main-build-ubuntu.yml delete mode 100644 src/SfsClient/sfs-client/.github/workflows/main-build-windows.yml delete mode 100644 src/SfsClient/sfs-client/.github/workflows/pr.yml delete mode 100644 src/SfsClient/sfs-client/.gitignore delete mode 100644 src/SfsClient/sfs-client/API.md delete mode 100644 src/SfsClient/sfs-client/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/CODE_OF_CONDUCT.md delete mode 100644 src/SfsClient/sfs-client/DEVELOPMENT.md delete mode 100644 src/SfsClient/sfs-client/LICENSE delete mode 100644 src/SfsClient/sfs-client/NOTICE.md delete mode 100644 src/SfsClient/sfs-client/README.md delete mode 100644 src/SfsClient/sfs-client/SECURITY.md delete mode 100644 src/SfsClient/sfs-client/SUPPORT.md delete mode 100644 src/SfsClient/sfs-client/TEST.md delete mode 100644 src/SfsClient/sfs-client/cgmanifest.json delete mode 100644 src/SfsClient/sfs-client/client/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/AppContent.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/AppFile.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/ApplicabilityDetails.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/ClientConfig.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/Content.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/ContentId.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/File.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/Logging.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/RequestParams.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/Result.h delete mode 100644 src/SfsClient/sfs-client/client/include/sfsclient/SFSClient.h delete mode 100644 src/SfsClient/sfs-client/client/src/AppContent.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/AppFile.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/ApplicabilityDetails.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/Content.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/ContentId.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/File.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/Logging.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/Result.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/SFSClient.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/ContentUtil.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/ContentUtil.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/CorrelationVector.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/CorrelationVector.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/Env.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/Env.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/ErrorHandling.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/ErrorHandling.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/OSInfo.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/OSInfo.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/ReportingHandler.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/ReportingHandler.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSClientImpl.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSClientImpl.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSClientInterface.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSException.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSException.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/TestOverride.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/TestOverride.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/UrlBuilder.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/UrlBuilder.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/Util.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/Util.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/Connection.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/Connection.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/ContentType.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/ContentType.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/FileEntity.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/FileEntity.h delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.cpp delete mode 100644 src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.h delete mode 100644 src/SfsClient/sfs-client/client/tests/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/client/tests/functional/SFSClientTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/functional/details/SFSClientImplTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/MockWebServer.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/MockWebServer.h delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/ProxyServer.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/ProxyServer.h delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/ServerCommon.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/mock/ServerCommon.h delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/AppContentTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/AppFileTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/ApplicabilityDetailsTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/ContentIdTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/ContentTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/FileTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/ResultTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/SFSClientTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionManagerTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/EnvTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/ErrorHandlingTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/ReportingHandlerTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/SFSClientImplTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/SFSUrlBuilderTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/TestOverrideTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/UrlBuilderTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/UtilTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/entity/FileEntityTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/unit/details/entity/VersionEntityTests.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.h delete mode 100644 src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp delete mode 100644 src/SfsClient/sfs-client/client/tests/util/TestHelper.h delete mode 100644 src/SfsClient/sfs-client/cmake/SFSOptions.cmake delete mode 100644 src/SfsClient/sfs-client/cmake/sfsclient-config.cmake.in delete mode 100644 src/SfsClient/sfs-client/pre-commit-wrapper.sh delete mode 100644 src/SfsClient/sfs-client/pre-commit.sh delete mode 100644 src/SfsClient/sfs-client/samples/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/samples/README.md delete mode 100644 src/SfsClient/sfs-client/samples/integration-do-client/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp delete mode 100644 src/SfsClient/sfs-client/samples/tool/CMakeLists.txt delete mode 100644 src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp delete mode 100644 src/SfsClient/sfs-client/scripts/Build.ps1 delete mode 100644 src/SfsClient/sfs-client/scripts/Setup.ps1 delete mode 100644 src/SfsClient/sfs-client/scripts/Test.ps1 delete mode 100755 src/SfsClient/sfs-client/scripts/build.sh delete mode 100644 src/SfsClient/sfs-client/scripts/check-format.py delete mode 100644 src/SfsClient/sfs-client/scripts/pip.requirements.txt delete mode 100755 src/SfsClient/sfs-client/scripts/setup.sh delete mode 100755 src/SfsClient/sfs-client/scripts/test.sh delete mode 100644 src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/portfile.cmake delete mode 100644 src/SfsClient/sfs-client/vcpkg-custom-triplets/x64-windows-static-custom.cmake delete mode 100644 src/SfsClient/sfs-client/vcpkg.json create mode 100644 src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch create mode 100644 src/VcpkgPortOverlay/patches/sfs-client/remove-unconditional-toolchain-override.patch create mode 100644 src/VcpkgPortOverlay/sfs-client/portfile.cmake create mode 100644 src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch rename src/{SfsClient/sfs-client/sfs-client-vcpkg-port => VcpkgPortOverlay}/sfs-client/usage (97%) rename src/{SfsClient/sfs-client/sfs-client-vcpkg-port => VcpkgPortOverlay}/sfs-client/vcpkg.json (96%) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f343fb4156..44cd9f2149 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -720,3 +720,28 @@ jobs: env: onefuzzDropDirectory: '$(buildOutDir)\WinGetYamlFuzzing' SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +- job: 'VerifyPortOverlay' + displayName: 'Verify Port Overlay' + variables: + runCodesignValidationInjection: ${{ false }} + skipComponentGovernanceDetection: ${{ true }} + + steps: + - task: PowerShell@2 + displayName: Regenerate Port Overlay + inputs: + filePath: 'src\VcpkgPortOverlay\CreatePortOverlay.ps1' + workingDirectory: 'src\VcpkgPortOverlay' + + - task: PowerShell@2 + displayName: Verify No Changes + inputs: + targetType: inline + script: | + $diff = git diff --name-only src/VcpkgPortOverlay + if ($diff) { + Write-Host "##[error]Port overlay is out of sync. Run CreatePortOverlay.ps1 and commit the result." + Write-Host $diff + exit 1 + } diff --git a/cgmanifest.json b/cgmanifest.json index 0ab008bb1a..fc5c3c3447 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -33,7 +33,7 @@ "type": "git", "git": { "repositoryUrl": "https://github.com/microsoft/sfs-client.git", - "commitHash": "ff315ecfa2ef2953d8a808e51e8a61a4e0759180" + "commitHash": "0e27525d597c730e71646fd0b15bdc8c8503f24d" } } }, diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln index f2a67fd9e9..31a5df57e6 100644 --- a/src/AppInstallerCLI.sln +++ b/src/AppInstallerCLI.sln @@ -181,8 +181,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5A52D9FC PowerShell\tests\RunTests.ps1 = PowerShell\tests\RunTests.ps1 EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SfsClient", "SfsClient\SfsClient.vcxproj", "{1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VcpkgCustomTriplets", "VcpkgCustomTriplets", "{76B26B2C-602A-4AD0-9736-4162D3FCA92A}" ProjectSection(SolutionItems) = preProject VcpkgCustomTriplets\arm64-release-static.cmake = VcpkgCustomTriplets\arm64-release-static.cmake @@ -200,6 +198,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VcpkgCustomTriplets", "Vcpk VcpkgCustomTriplets\x86.cmake = VcpkgCustomTriplets\x86.cmake EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VcpkgPortOverlay", "VcpkgPortOverlay", "{EF18BED6-EBC0-45E9-8D61-4202528E8AAB}" + ProjectSection(SolutionItems) = preProject + VcpkgPortOverlay\CreatePortOverlay.ps1 = VcpkgPortOverlay\CreatePortOverlay.ps1 + VcpkgPortOverlay\README.md = VcpkgPortOverlay\README.md + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment.OutOfProc", "Microsoft.Management.Deployment.OutOfProc\Microsoft.Management.Deployment.OutOfProc.vcxproj", "{0BA531C8-CF0C-405B-8221-0FE51BA529D1}" ProjectSection(ProjectDependencies) = postProject {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} = {2B00D362-AC92-41F3-A8D2-5B1599BDCA01} @@ -936,27 +940,6 @@ Global {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x64.Build.0 = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Any CPU {272B2B0E-40D4-4F0F-B187-519A6EF89B10}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Any CPU - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|ARM64.Build.0 = Debug|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x64.ActiveCfg = Debug|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x64.Build.0 = Debug|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x86.ActiveCfg = Debug|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Debug|x86.Build.0 = Debug|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|x64.ActiveCfg = Release|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Fuzzing|x86.ActiveCfg = Release|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|ARM64.ActiveCfg = Release|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|ARM64.Build.0 = Release|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x64.ActiveCfg = Release|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x64.Build.0 = Release|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x86.ActiveCfg = Release|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.Release|x86.Build.0 = Release|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|ARM64.ActiveCfg = ReleaseStatic|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|ARM64.Build.0 = ReleaseStatic|ARM64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|ARM64.Build.0 = Debug|ARM64 {0BA531C8-CF0C-405B-8221-0FE51BA529D1}.Debug|x64.ActiveCfg = Debug|x64 @@ -1104,8 +1087,8 @@ Global {C54F80ED-B736-49B0-9BD3-662F57024D01} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {272B2B0E-40D4-4F0F-B187-519A6EF89B10} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {5A52D9FC-0059-4A4A-8196-427A7AA0D1C5} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4} = {60618CAC-2995-4DF9-9914-45C6FC02C995} - {76B26B2C-602A-4AD0-9736-4162D3FCA92A} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} + {76B26B2C-602A-4AD0-9736-4162D3FCA92A} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} + {EF18BED6-EBC0-45E9-8D61-4202528E8AAB} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} {A0B4F808-B190-41C4-97CB-C8EA1932F84F} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {A33223D2-550B-4D99-A53D-488B1F68683E} = {60618CAC-2995-4DF9-9914-45C6FC02C995} {7139ED6E-8FBC-0B61-3E3A-AA2A23CC4D6A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 1899bb36c7..e90444925c 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -121,8 +121,8 @@ Disabled _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) true true false @@ -148,7 +148,7 @@ _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) true false @@ -169,9 +169,9 @@ true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) + $(MSBuildThisFileDirectory)..\AppInstallerCommonCore;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerRepositoryCore;$(MSBuildThisFileDirectory)..\AppInstallerCommonCore\Public;$(MSBuildThisFileDirectory)..\AppInstallerSharedLib\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore\Public;$(MSBuildThisFileDirectory)..\AppInstallerCLICore;%(AdditionalIncludeDirectories) true true true @@ -1112,9 +1112,6 @@ {ca460806-5e41-4e97-9a3d-1d74b433b663} - - {1b9077b3-8923-4ecd-8fc9-b3190fcbe4d4} - diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 5fa3628a34..ff8ac9f05e 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -204,8 +204,8 @@ Disabled _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true @@ -224,7 +224,7 @@ _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true @@ -240,9 +240,9 @@ true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true @@ -271,9 +271,9 @@ true true _NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) - $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\SfsClient\sfs-client\client\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;%(AdditionalIncludeDirectories) true true true diff --git a/src/SfsClient/SfsClient.vcxproj b/src/SfsClient/SfsClient.vcxproj deleted file mode 100644 index 9b50a5516e..0000000000 --- a/src/SfsClient/SfsClient.vcxproj +++ /dev/null @@ -1,291 +0,0 @@ - - - - 16.0 - {1B9077B3-8923-4ECD-8FC9-B3190FCBE4D4} - Win32Proj - true - 10.0.26100.0 - 10.0.17763.0 - - - - - Debug - ARM64 - - - Debug - Win32 - - - ReleaseStatic - ARM64 - - - ReleaseStatic - Win32 - - - ReleaseStatic - x64 - - - Release - ARM64 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - StaticLibrary - true - - - StaticLibrary - false - Spectre - - - true - true - true - $(ProjectDir)..\vcpkg_installed - - - - - - - - - true - $(SolutionDir)$(PlatformShortname)\$(Configuration)\$(ProjectName)\ - - - true - $(SolutionDir)$(PlatformShortname)\$(Configuration)\$(ProjectName)\ - - - true - $(SolutionDir)$(PlatformShortname)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ - - - x64 - - - x64-release - - - x64-release-static - - - arm64-release-static - - - x86-release-static - - - arm64 - - - x86 - - - x86-release - - - arm64-release - - - - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";WIN32;_DEBUG;%(PreprocessorDefinitions) - TurnOffAllWarnings - ProgramDatabase - stdcpp17 - Disabled - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - MachineX86 - true - Windows - - - - - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";WIN32;NDEBUG;%(PreprocessorDefinitions) - Level3 - ProgramDatabase - stdcpp17 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - MachineX86 - true - Windows - true - true - - - - - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";WIN32;NDEBUG;%(PreprocessorDefinitions) - Level3 - ProgramDatabase - stdcpp17 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - MultiThreaded - true - - - MachineX86 - true - Windows - true - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";NDEBUG;%(PreprocessorDefinitions) - Level3 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";NDEBUG;%(PreprocessorDefinitions) - Level3 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - MultiThreaded - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";NDEBUG;%(PreprocessorDefinitions)%(PreprocessorDefinitions) - Level3 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";NDEBUG;%(PreprocessorDefinitions)%(PreprocessorDefinitions) - Level3 - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - MultiThreaded - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";_DEBUG;%(PreprocessorDefinitions)%(PreprocessorDefinitions) - TurnOffAllWarnings - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - - - stdcpp17 - NotUsing - CURL_STATICLIB;GUID_WINDOWS;SFS_VERSION="1.1.0";_DEBUG;%(PreprocessorDefinitions) - TurnOffAllWarnings - $(ProjectDir)sfs-client\client\include\sfsclient;%(AdditionalIncludeDirectories) - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/SfsClient/SfsClient.vcxproj.filters b/src/SfsClient/SfsClient.vcxproj.filters deleted file mode 100644 index 6db39e4511..0000000000 --- a/src/SfsClient/SfsClient.vcxproj.filters +++ /dev/null @@ -1,159 +0,0 @@ - - - - - {7825c1db-0159-4653-b26c-bc674bb9e29f} - - - {357ffe7c-3ce6-4b0d-9e93-bbda28fb9fc3} - - - {d7879d14-b049-4837-a336-895bd20ab5d8} - - - {15462934-ddd8-46ad-a0fd-110df7fa2acb} - - - {133cf863-8baa-4ce2-9050-40155b027f04} - - - {93204abc-3d4c-4cf0-9433-db8c4ad667b5} - - - {102d246a-6b50-4171-a909-cb68923ac321} - - - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - include\sfsclient - - - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details - - - src\details\connection - - - src\details\connection - - - src\details\connection - - - src\details\connection - - - src\details\connection - - - src\details\connection - - - src\details\connection\mock - - - src\details\connection\mock - - - src\details\entity - - - src\details\entity - - - src\details\entity - - - diff --git a/src/SfsClient/readme.md b/src/SfsClient/readme.md deleted file mode 100644 index 84fcea431b..0000000000 --- a/src/SfsClient/readme.md +++ /dev/null @@ -1,19 +0,0 @@ -## SfsClient - -Do not change code under the sfs-client directory; it contains sfs-client source code from release 1.1.0 (https://github.com/microsoft/sfs-client/releases/tag/1.1.0). -It was initially created using git subtree command: -``` - git subtree add --prefix=src/SfsClient/sfs-client https://github.com/microsoft/sfs-client.git be733af9e5c8e9227f2018ff618800bf08a31180 --squash -``` -Then updated to release 1.1.0 using: -``` - git subtree pull -P src/SfsClient/sfs-client https://github.com/microsoft/sfs-client 1.1.0 --squash -``` - - -### Update -To update, run the following command, then update the above commit for reference. 'master' can be replaced with the appropriate commit spec as desired. -``` - git subtree pull -P src/SfsClient/sfs-client https://github.com/microsoft/sfs-client master --squash -``` -**When committing the PR, DO NOT squash it. The two commits are needed as is to allow for future subtree pulls.** diff --git a/src/SfsClient/sfs-client/.clang-format b/src/SfsClient/sfs-client/.clang-format deleted file mode 100644 index 4ee2a2ec17..0000000000 --- a/src/SfsClient/sfs-client/.clang-format +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# -# -- Usage -- -# Read README.md for initial context, and instructions for VSCode and command line. -# -# See here for the meaning of each item in this file: https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# To get all the options that are part of a standard style such as Microsoft, do this: -# > clang-format --style=Microsoft -dump-config > clang-format-microsoft.txt -# -# -- Philosophy -- -# We will try to adhere to the standard Microsoft style as much as possible. -# The overrides below are deviations from the standard. Keep it short. ---- -Language: Cpp -BasedOnStyle: Microsoft - -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterCaseLabel: true -BreakConstructorInitializers: BeforeComma -BreakStringLiterals: false -InsertNewlineAtEOF: true -PackConstructorInitializers: CurrentLine -PointerAlignment: Left diff --git a/src/SfsClient/sfs-client/.cmake-format.json b/src/SfsClient/sfs-client/.cmake-format.json deleted file mode 100644 index a06983a3f8..0000000000 --- a/src/SfsClient/sfs-client/.cmake-format.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "first_comment_is_literal": "true", - "keyword_case": "upper", - "line_ending": "windows", - "max_pargs_hwrap": 3, - "tab_size": 4 -} \ No newline at end of file diff --git a/src/SfsClient/sfs-client/.github/CODEOWNERS b/src/SfsClient/sfs-client/.github/CODEOWNERS deleted file mode 100644 index 69e67dd6cb..0000000000 --- a/src/SfsClient/sfs-client/.github/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# The SFS Client Reviewers team will be requested for review when someone opens a pull request. -* @microsoft/sfs-client-reviewers \ No newline at end of file diff --git a/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/bug_report.md b/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 74514987e3..0000000000 --- a/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Additional context** -Add any other context about the problem here. diff --git a/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/feature_request.md b/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7d61..0000000000 --- a/src/SfsClient/sfs-client/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/src/SfsClient/sfs-client/.github/dependabot.yml b/src/SfsClient/sfs-client/.github/dependabot.yml deleted file mode 100644 index c64b5ab54b..0000000000 --- a/src/SfsClient/sfs-client/.github/dependabot.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Dependabot helps maintain dependencies updated for different package ecosystems -# -# Here is the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - - # Maintain dependencies for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - - # Maintain dependencies for pip - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "weekly" - ignore: - # Ignore patch updates for all pip dependencies - - dependency-name: "*" - update-types: ["version-update:semver-patch"] diff --git a/src/SfsClient/sfs-client/.github/pull_request_template.md b/src/SfsClient/sfs-client/.github/pull_request_template.md deleted file mode 100644 index ddaa8e6abe..0000000000 --- a/src/SfsClient/sfs-client/.github/pull_request_template.md +++ /dev/null @@ -1,15 +0,0 @@ -#### Related Issues - -- Closes # -- Helps # - -#### Why is this change being made? - -#### What is being changed? - -#### How was the change tested? - - - - - diff --git a/src/SfsClient/sfs-client/.github/workflows/initialize-codeql/action.yml b/src/SfsClient/sfs-client/.github/workflows/initialize-codeql/action.yml deleted file mode 100644 index 990b0603bf..0000000000 --- a/src/SfsClient/sfs-client/.github/workflows/initialize-codeql/action.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Initialize CodeQL - -description: Initializes CodeQL action to be used in build workflows - -runs: - using: "composite" - - steps: - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: cpp \ No newline at end of file diff --git a/src/SfsClient/sfs-client/.github/workflows/install-winget/action.yml b/src/SfsClient/sfs-client/.github/workflows/install-winget/action.yml deleted file mode 100644 index 55847ae2ce..0000000000 --- a/src/SfsClient/sfs-client/.github/workflows/install-winget/action.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Install WinGet - -description: Installs WinGet from sources, caching the installation files if possible - -inputs: - winget-installation-cache: - description: 'Where the installation files will be cached' - required: false - default: 'winget-installation-cache' - -runs: - using: "composite" - - steps: - - name: Cache WinGet installation - id: cache-winget-installation - uses: actions/cache@v4 - with: - path: winget-installation-cache - key: winget-installation - - # Winget is not available in the windows-latest image as it is Windows Server 2022 and winget is not yet available officially for it. - # We can still install it in "experimental" mode manually though. - - name: Install WinGet - shell: pwsh - run: | - # Check it doesn't exist to avoid installing it without need - if (!(Get-Command 'winget.exe' -CommandType Application -ErrorAction SilentlyContinue)) - { - New-Item -ItemType Directory ${{ inputs.winget-installation-cache }} -Force | Out-Null - Set-Location ${{ inputs.winget-installation-cache }} - $ProgressPreference = "silentlyContinue" - $WingetInstaller = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" - if (!(Test-Path $WingetInstaller)) - { - Write-Host "Downloading Winget" - Invoke-WebRequest -Uri https://github.com/microsoft/winget-cli/releases/download/v1.7.10582/$WingetInstaller -OutFile $WingetInstaller - } - $VCLibsInstaller = "Microsoft.VCLibs.x64.14.00.Desktop.appx" - if (!(Test-Path $VCLibsInstaller)) - { - Write-Host "Downloading Winget dependency VCLibs" - Invoke-WebRequest -Uri https://aka.ms/$VCLibsInstaller -OutFile $VCLibsInstaller - } - $XamlInstaller = "Microsoft.UI.Xaml.2.8.x64.appx" - if (!(Test-Path $XamlInstaller)) - { - Write-Host "Downloading Winget dependency Xaml" - Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/$XamlInstaller -OutFile $XamlInstaller - } - - Import-Module -Name Appx -UseWindowsPowershell | Out-Null - Add-AppxPackage $VCLibsInstaller - Add-AppxPackage $XamlInstaller - Add-AppxPackage $WingetInstaller - } diff --git a/src/SfsClient/sfs-client/.github/workflows/main-build-ubuntu.yml b/src/SfsClient/sfs-client/.github/workflows/main-build-ubuntu.yml deleted file mode 100644 index 59ee8521c9..0000000000 --- a/src/SfsClient/sfs-client/.github/workflows/main-build-ubuntu.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Main Build (Ubuntu) - -on: - push: - branches: [ "main" ] - -# Permissions and environment values to be able to update the dependency graph with vcpkg information -# and to enable the writing/uploading of CodeQL scan results -permissions: - contents: write - security-events: write - -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VCPKG_FEATURE_FLAGS: dependencygraph - -jobs: - build-ubuntu: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: ./.github/workflows/initialize-codeql - - - name: Setup - run: source ./scripts/setup.sh - - - name: Build and Test (no test overrides) - run: | - ./scripts/build.sh - ./scripts/test.sh --output-on-failure - - - name: Build and Test (with test overrides) - run: | - ./scripts/build.sh --enable-test-overrides - ./scripts/test.sh --output-on-failure - - - name: Build and Test (Release) - run: | - ./scripts/build.sh --build-type Release - ./scripts/test.sh --output-on-failure - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/src/SfsClient/sfs-client/.github/workflows/main-build-windows.yml b/src/SfsClient/sfs-client/.github/workflows/main-build-windows.yml deleted file mode 100644 index 0c2a6cf653..0000000000 --- a/src/SfsClient/sfs-client/.github/workflows/main-build-windows.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Main Build (Windows) - -on: - push: - branches: [ "main" ] - -# Permissions and environment values to be able to update the dependency graph with vcpkg information -# and to enable the writing/uploading of CodeQL scan results -permissions: - contents: write - security-events: write - -env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VCPKG_FEATURE_FLAGS: dependencygraph - -jobs: - build-windows: - runs-on: 'windows-latest' - - steps: - - uses: actions/checkout@v4 - - - name: Initialize CodeQL - uses: ./.github/workflows/initialize-codeql - - - name: Install Winget - uses: ./.github/workflows/install-winget - - - name: Setup - shell: pwsh - run: .\scripts\Setup.ps1 - - - name: Build and Test (no test overrides) - shell: pwsh - run: | - .\scripts\Build.ps1 - .\scripts\Test.ps1 -OutputOnFailure - - - name: Build and Test (with test overrides) - shell: pwsh - run: | - .\scripts\Build.ps1 -EnableTestOverrides - .\scripts\Test.ps1 -OutputOnFailure - - - name: Build and Test (Release) - shell: pwsh - run: | - .\scripts\Build.ps1 -BuildType Release - .\scripts\Test.ps1 -OutputOnFailure - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/src/SfsClient/sfs-client/.github/workflows/pr.yml b/src/SfsClient/sfs-client/.github/workflows/pr.yml deleted file mode 100644 index f3740d36c4..0000000000 --- a/src/SfsClient/sfs-client/.github/workflows/pr.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: PR Build - -env: - # Set up vcpkg to read from the GitHub cache (https://learn.microsoft.com/en-us/vcpkg/consume/binary-caching-github-actions-cache) - VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite' - -on: - pull_request: - branches: [ "main" ] - -jobs: - build-windows: - runs-on: 'windows-latest' - - steps: - - uses: actions/checkout@v4 - - - name: Set up required environment variables for vcpkg cache - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - - - name: Initialize CodeQL - uses: ./.github/workflows/initialize-codeql - - - name: Install Winget - uses: ./.github/workflows/install-winget - - - name: Setup - shell: pwsh - run: .\scripts\Setup.ps1 - - - name: Check formatting - shell: pwsh - run: python .\scripts\check-format.py - - - name: Build and Test (no test overrides) - shell: pwsh - run: | - .\scripts\Build.ps1 - .\scripts\Test.ps1 -OutputOnFailure - - - name: Build and Test (with test overrides) - shell: pwsh - run: | - .\scripts\Build.ps1 -EnableTestOverrides - .\scripts\Test.ps1 -OutputOnFailure - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - - build-ubuntu: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up required environment variables for vcpkg cache - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - - - name: Initialize CodeQL - uses: ./.github/workflows/initialize-codeql - - - name: Setup - run: source ./scripts/setup.sh - - - name: Check formatting - run: python ./scripts/check-format.py - - - name: Build and Test (no test overrides) - run: | - ./scripts/build.sh - ./scripts/test.sh --output-on-failure - - - name: Build and Test (with test overrides) - run: | - ./scripts/build.sh --enable-test-overrides - ./scripts/test.sh --output-on-failure - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/src/SfsClient/sfs-client/.gitignore b/src/SfsClient/sfs-client/.gitignore deleted file mode 100644 index 934e6cbb7d..0000000000 --- a/src/SfsClient/sfs-client/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Build -/build - -# VSCode specific folder -/.vscode - -# Vcpkg -/vcpkg -/vcpkg_installed - -# Formatting temporary file -/.tmp_formatted diff --git a/src/SfsClient/sfs-client/API.md b/src/SfsClient/sfs-client/API.md deleted file mode 100644 index acc930ad3c..0000000000 --- a/src/SfsClient/sfs-client/API.md +++ /dev/null @@ -1,59 +0,0 @@ -## SFSClient - -To start using the SFSClient library, use `SFSClient::Make()` to create an `SFSClient` instance, which allows you to use the SFS APIs. -The first argument to the factory is a `ClientConfig` struct. Configuring this struct allows you to customize the behavior of the client. -Refer to the documentation of the `ClientConfig` struct in [ClientConfig.h](client/include/sfsclient/ClientConfig.h) to see the available options. - -## Logging Callback - -To retrieve logging information from the API, set a logging callback in `ClientConfig::logCallbackFn` when constructing an SFSClient instance with `SFSClient::Make()`. - -The logging callback function has the signature: - -```cpp -void callback(const SFS::LogData&); -``` - -An example to log the data directly to the standard output using `std::cout`: - -```cpp -void LoggingCallback(const SFS::LogData& logData) -{ - std::cout << "Log: [" << ToString(logData.severity) << "]" << " " << logData.file << ":" - << logData.line << " " << logData.message << std::endl; -} -``` - -Notes: -- The callback itself is processed in the main thread. Do not use a blocking callback. If heavy processing has to be done, consider capturing the data and processing another thread. -- The LogData contents only exist within the callback call. If the processing will be done later, you should copy the data elsewhere. -- The callback should not do any re-entrant calls (e.g. call `SFSClient` methods). - -## Class instances - -It is recommended to only create a single `SFSClient` instance, even if multiple threads will be used. -Each `GetLatestDownloadInfo()` call will create its own connection and should not interfere with other calls. - -### Thread safety - -All API calls are thread-safe. - -If a logging callback is set in a multi-threaded environment, and the same `SFSClient()` is reused across different threads, the same callback will be called by all usages of the class. So, make sure the callback itself is also thread-safe. - -## Content types - -A few data types are provided which abstract contents that can be sent by the SFS Service, such as `Content`, `ContentId`, `File`. -These data types provide `noexcept` methods to interact with member data. - -## Retry Behavior - -The API follows a certain set of rules to retry upon reaching specific HTTP Status Codes. The behavior is configurable through the `retryOnError` member of `RequestParams`. - -By default, the client will retry up to 3 times when reaching the following HTTP Status Codes: -- 429: Too Many Requests -- 500: Internal Server Error -- 502: Bad Gateway -- 503: Server Busy -- 504: Gateway Timeout - -Between each retry the Client will wait an interval that follows either the `Retry-After` response header, or an exponential backoff calculation with a factor of 2 starting from 15s. diff --git a/src/SfsClient/sfs-client/CMakeLists.txt b/src/SfsClient/sfs-client/CMakeLists.txt deleted file mode 100644 index 31bb1010cd..0000000000 --- a/src/SfsClient/sfs-client/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -cmake_minimum_required(VERSION 3.19) - -# Tell CMake to look in cmake for our custom CMake module files. -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -include(SFSOptions) - -if(SFS_BUILD_TESTS) - list(APPEND VCPKG_MANIFEST_FEATURES "tests") -endif() - -set(CMAKE_TOOLCHAIN_FILE "vcpkg/scripts/buildsystems/vcpkg.cmake") - -# By default using x64 static custom triplet for Windows. Can be overridden by -# setting VCPKG_TARGET_TRIPLET -if(SFS_WINDOWS_STATIC_ONLY - AND WIN32 - AND NOT DEFINED VCPKG_TARGET_TRIPLET) - set(VCPKG_TARGET_TRIPLET "x64-windows-static-custom") -endif() - -# Semver versioning. Given MAJOR.MINOR.PATCH, increment the: -# -# 1. MAJOR version when you make incompatible API changes -# 2. MINOR version when you add functionality in a backward compatible manner -# 3. PATCH version when you make backward compatible bug fixes -set(SFS_LIBRARY_VERSION "1.1.0") - -project( - sfsclient - VERSION ${SFS_LIBRARY_VERSION} - LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -# Use this function to set warning level and warnings as errors for a given -# target -function(set_compile_options_for_target target) - if(MSVC) - # Enable some MSVC warnings that are off by default: - # - # * 4062: all enums are handled in switch statements without a default - # label - # * 4191: unsafe conversion from 'type of expression' to 'type required' - # * 4242: possible loss of data (identifier) - # * 4254: possible loss of data (operator) - # * 4287: 'operator': unsigned/negative constant mismatch - # * 4296: 'operator': expression is always true (or false) - # * 4388: signed/unsigned mismatch - # * 4800: implicit conversion to bool; possible information loss - # * 4946: reinterpret_cast used between related classes - - # cmake-format: off - target_compile_options(${target} PRIVATE /W4 /WX /we4062 /we4191 /we4242 /we4254 /we4287 /we4296 /we4388 /we4800 /we4946) - # cmake-format: on - else() - target_compile_options( - ${target} - PRIVATE -Wall - -Wextra - -Wpedantic - -Werror) - endif() -endfunction() - -if(SFS_WINDOWS_STATIC_ONLY AND MSVC) - # For MSVC, use a multi-threaded statically-linked runtime library - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif() - -add_subdirectory(client) - -if(SFS_BUILD_SAMPLES) - add_subdirectory(samples) -endif() diff --git a/src/SfsClient/sfs-client/CODE_OF_CONDUCT.md b/src/SfsClient/sfs-client/CODE_OF_CONDUCT.md deleted file mode 100644 index f9ba8cf65f..0000000000 --- a/src/SfsClient/sfs-client/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/src/SfsClient/sfs-client/DEVELOPMENT.md b/src/SfsClient/sfs-client/DEVELOPMENT.md deleted file mode 100644 index 015159ac88..0000000000 --- a/src/SfsClient/sfs-client/DEVELOPMENT.md +++ /dev/null @@ -1,6 +0,0 @@ -# cgmanifest.json - -Keep this file updated as you update `vcpkg.json`. It must indicate the dependencies we use for proper automatic scanning of dependencies done in the Azure DevOps backend. -The folder `build\vcpkg_installed\vcpkg\info` is a good source of truth of which vcpkg packages are installed. It does however accumulate temporary packages you may have added in the past. Before using it, try cleaning the build folder and re-building. - -Once you find the GitHub repo of the dependency, use the version on the tags page to find the commit hash that corresponds to the installed version. diff --git a/src/SfsClient/sfs-client/LICENSE b/src/SfsClient/sfs-client/LICENSE deleted file mode 100644 index 9e841e7a26..0000000000 --- a/src/SfsClient/sfs-client/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/src/SfsClient/sfs-client/NOTICE.md b/src/SfsClient/sfs-client/NOTICE.md deleted file mode 100644 index da464a7d05..0000000000 --- a/src/SfsClient/sfs-client/NOTICE.md +++ /dev/null @@ -1,441 +0,0 @@ -# NOTICES - -This repository incorporates material as listed below or described in the code. - ---------------------------------------------------------- - -**Component:** - -CorrelationVector-Cpp - -**Open Source License/Copyright Notice:** - -MIT License - -Copyright (c) Microsoft Corporation. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE - ---------------------------------------------------------- - -**Component:** - -curl - -**Open Source License/Copyright Notice:** - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1996 - 2024, Daniel Stenberg, , and many -contributors, see the THANKS file. - -All rights reserved. - -Permission to use, copy, modify, and distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright -notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN -NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE -OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of a copyright holder shall not -be used in advertising or otherwise to promote the sale, use or other dealings -in this Software without prior written authorization of the copyright holder. - ---------------------------------------------------------- - -**Component:** - -c-ares - -**Open Source License/Copyright Notice:** - -MIT License - -Copyright (c) 1998 Massachusetts Institute of Technology Copyright (c) 2007 - 2023 Daniel Stenberg with many contributors, see AUTHORS file. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -**Additional Attribution:** - -c-ares is based on ares, and these are the people that have worked on it since -the fork was made: - -Albert Chin -Alex Loukissas -Alexander Klauer -Alexander Lazic -Alexey Simak -Andreas Rieke -Andrew Andkjar -Andrew Ayer -Andrew C. Morrow -Ashish Sharma -Ben Greear -Ben Noordhuis -BogDan Vatra -Brad House -Brad Spencer -Bram Matthys -Chris Araman -Dan Fandrich -Daniel Johnson -Daniel Stenberg -David Drysdale -David Stuart -Denis Bilenko -Dima Tisnek -Dirk Manske -Dominick Meglio -Doug Goldstein -Doug Kwan -Duncan Wilcox -Eino Tuominen -Erik Kline -Fedor Indutny -Frederic Germain -Geert Uytterhoeven -George Neill -Gisle Vanem -Google LLC -Gregor Jasny -Guenter Knauf -Guilherme Balena Versiani -Gunter Knauf -Henrik Stoerner -Jakub Hrozek -James Bursa -Jérémy Lal -John Schember -Keith Shaw -Lei Shi -Marko Kreen -Michael Wallner -Mike Crowe -Nick Alcock -Nick Mathewson -Nicolas "Pixel" Noble -Ning Dong -Oleg Pudeyev -Patrick Valsecchi -Patrik Thunstrom -Paul Saab -Peter Pentchev -Phil Blundell -Poul Thomas Lomholt -Ravi Pratap -Robin Cornelius -Saúl Ibarra Corretgé -Sebastian at basti79.de -Shmulik Regev -Stefan Bühler -Steinar H. Gunderson -Svante Karlsson -Tofu Linden -Tom Hughes -Tor Arntsen -Viktor Szakats -Vlad Dinulescu -William Ahern -Yang Tse -hpopescu at ixiacom.com -liren at vivisimo.com -nordsturm -saghul - ---------------------------------------------------------- - -**Component:** - -openssl - -**Open Source License/Copyright Notice:** - -
-                                 Apache License
-                           Version 2.0, January 2004
-                        https://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
- -**Additional Attribution:** - -
-
-Authors
-=======
-
-This is the list of OpenSSL authors for copyright purposes.
-It does not necessarily list everyone who has contributed code,
-since in some cases, their employer may be the copyright holder.
-To see the full list of contributors, see the revision history in
-source control.
-
-Groups
-------
-
- * OpenSSL Software Services, Inc.
- * OpenSSL Software Foundation, Inc.
-
-Individuals
------------
-
- * Andy Polyakov
- * Ben Laurie
- * Ben Kaduk
- * Bernd Edlinger
- * Bodo Möller
- * David Benjamin
- * David von Oheimb
- * Dmitry Belyavskiy (Дмитрий Белявский)
- * Emilia Käsper
- * Eric Young
- * Geoff Thorpe
- * Holger Reif
- * Kurt Roeckx
- * Lutz Jänicke
- * Mark J. Cox
- * Matt Caswell
- * Matthias St. Pierre
- * Nicola Tuveri
- * Nils Larsch
- * Patrick Steuer
- * Paul Dale
- * Paul C. Sutton
- * Paul Yang
- * Ralf S. Engelschall
- * Rich Salz
- * Richard Levitte
- * Shane Lontis
- * Stephen Henson
- * Steve Marquess
- * Tim Hudson
- * Tomáš Mráz
- * Ulf Möller
- * Viktor Dukhovni
-
- ---------------------------------------------------------- - -**Component:** nlohmann/json - -**Open Source License/Copyright Notice:** - -MIT License - -Copyright (c) 2013-2022 Niels Lohmann - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/SfsClient/sfs-client/README.md b/src/SfsClient/sfs-client/README.md deleted file mode 100644 index c16717c799..0000000000 --- a/src/SfsClient/sfs-client/README.md +++ /dev/null @@ -1,195 +0,0 @@ -# SFS Client - -[![Windows Latest](https://github.com/microsoft/sfs-client/actions/workflows/main-build-windows.yml/badge.svg?branch=main&event=push)](https://github.com/microsoft/sfs-client/actions/workflows/main-build-windows.yml) [![Ubuntu Latest](https://github.com/microsoft/sfs-client/actions/workflows/main-build-ubuntu.yml/badge.svg?branch=main&event=push)](https://github.com/microsoft/sfs-client/actions/workflows/main-build-ubuntu.yml) - -## Introduction - -This repository holds the Simple File Solution (SFS) Client, a C++ library that simplifies the interface with the SFS service. -Read below to get started on developing and using the library. - -## Usage - -Follow the [API](API.md) document for tips on how to use the API. - -## Getting Started - -### Prerequisites - -#### Setup script - -There are a few dependencies required to work on this project. -To set them up, use the Setup script. - -Windows: -```powershell -.\scripts\Setup.ps1 -``` - -Linux: -```bash -source ./scripts/setup.sh -``` - -The script can be run multiple times as it does not replace what has been installed, and updates dependencies. -It also sets up useful command-line aliases that can be used while developing. - -## Consuming the library - -This library is distributed as Source Code and meant for consumption in this format. Below we outline how to easily consume us through the vcpkg tool, but feel free to use other methods to incorporate the source. - -### vcpkg - -The [vcpkg](https://vcpkg.io/) tool is a Microsoft dependency manager for C/C++. It works "for all platforms, buildsystems, and workflows". -In general the dependencies are registered in the central vcpkg registry, where the "portfile" recipes are hosted. These files indicate the way the dependencies should be acquired and built. - -One of the features it provides is also a way to find dependencies listed in the local filesystem. See the [overlay-ports](https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports) feature to see that. -We are not hosted in the central vcpkg registry, but we provide a template overlay-port for easy consumption of the library. See the [sfs-client-vcpkg-port](./sfs-client-vcpkg-port) folder for the files you need to have in your local repository in order to consume us. A few placeholders have to be filled in on those files. - -## Formatting - -This project is currently using the clang-format tool to format its source code according to predefined rules. -It is also using cmake-format to format the CMakeLists.txt files. -Both are installed automatically with the Setup script. - -### Automatic usage - -The project is configured to automatically run formatting upon committing so nobody introduces -unformatted changes to the codebase. This is done through a pre-commit hook. -If you must avoid the hook, you can use `git commit -n` to bypass it. - -### Running on command line - -Use -h to see the binary help: -``` -clang-format -h -cmake-format -h -``` - -Use -i to edit a file inplace: -``` -clang-format -i ./interface.cpp -cmake-format -i ./CMakeLists.txt -``` - -Wildcards are accepted in clang-format: -``` -clang-format -i ./*.h -``` - -## Building - -To build, use the `build` command. It simplifies the CMake build commands and re-generates CMake configurations if needed. - -Available build options: - -| Switch (PowerShell) | Switch (Bash) | Description | -|----------------------|---------------------------|------------------------------------------------------------------------------------------| -| -Clean | --clean | Use this to clean the build folder before building. | -| -BuildType | --build-type | Use this to define the build type between "Debug" and "Release". The default is "Debug". | -| -EnableTestOverrides | --enable-test-overrides | Use this to enable test overrides. See [TEST](TEST.md) for more. | -| -BuildTests | --build-tests {ON, OFF} | Use this to build tests alongside the library. On by default. | -| -BuildSamples | --build-samples {ON, OFF} | Use this to build samples alongside the library. On by default. | - -See [below](#building-with-cmake-vscode-extension) for building within VSCode. - -## VSCode - -[Visual Studio Code](https://code.visualstudio.com) is the recommended editor to work with this project. -But you're free to use other editors and command-line tools. - -### Configuring VSCode includes with CMake - -VSCode has a great integration with CMake through the CMake Tools extension (ms-vscode.cmake-tools). -It allows you to configure and build the project through the UI. - -Open the command pane on VSCode and search for "C/C++: Edit Configurations (JSON)" to create a c_cpp_properties.json file under a .vscode folder in repo root. -The folder is ignored in git by default. -Add the following line to the "configurations" element to make VSCode start using the includes defined in the CMakeLists.txt files. - -```json -"configurations": [ - { - "configurationProvider": "ms-vscode.cmake-tools" - } -] -``` - -### Formatting C++ Sources with VSCode - -Install the extension https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools and Shift+Alt+F inside a file. -You can also access "Format" options by right clicking on an open file or over a line selection. - -If you want it, you can also make VSCode format on each Save operation by adding this to your User JSON settings: -`"editor.formatOnSave": true` - -### vcpkg integration - -From [the vcpkg docs](https://github.com/Microsoft/vcpkg/#visual-studio-code-with-cmake-tools): - -Adding the following to your workspace settings.json will make CMake Tools automatically use vcpkg for libraries: - -```json -{ - "cmake.configureSettings": { - "CMAKE_TOOLCHAIN_FILE": "[vcpkg root]/scripts/buildsystems/vcpkg.cmake" - } -} -``` - -### Building with CMake VSCode extension - -If you're using the CMake Tools extension on VSCode, you can set the build options through the VSCode settings. Add something like below to either your user or workspace JSON settings to get a default value for an option. You can also later use the command "Edit CMake Cache (UI)" for visual editing. - -```json -"cmake.configureArgs": [ - "-DSFS_ENABLE_TEST_OVERRIDES=ON" -] -``` - -See [SFSOptions.cmake](cmake/SFSOptions.cmake) for the CMake options available for the library. - -## Testing - -Tests are compiled alongside the library by default, and live in the client/tests subdirectory. -To run the tests, you can use the `test` command. It will run all tests directly and output the result to the console. - -If you want to customize the test run, you can make use of the `ctest` tool. - -``` -ctest --test-dir ./build/client -``` - -To run specific tests, you can filter the chosen tests through the switch `-R` or `--tests-regex`. -For more test selection switches, use `ctest --help`. - -The tests are built using the Catch2 framework. For a more verbose run you can run the executable directly, with -s. - -Windows: -``` -.\build\tests\bin\\SFSClientTests.exe -s -``` - -Linux: -``` -./build/tests/bin/SFSClientTests -s -``` - -Follow the [TEST](TEST.md) document for more information regarding testing. - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft’s Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party’s policies. diff --git a/src/SfsClient/sfs-client/SECURITY.md b/src/SfsClient/sfs-client/SECURITY.md deleted file mode 100644 index b3c89efc85..0000000000 --- a/src/SfsClient/sfs-client/SECURITY.md +++ /dev/null @@ -1,41 +0,0 @@ - - -## Security - -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. - -## Reporting Security Issues - -**Please do not report security vulnerabilities through public GitHub issues.** - -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. - -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. - -## Preferred Languages - -We prefer all communications to be in English. - -## Policy - -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). - - diff --git a/src/SfsClient/sfs-client/SUPPORT.md b/src/SfsClient/sfs-client/SUPPORT.md deleted file mode 100644 index 85c3de9c9f..0000000000 --- a/src/SfsClient/sfs-client/SUPPORT.md +++ /dev/null @@ -1,11 +0,0 @@ -# Support - -## How to file issues and get help - -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. - -## Microsoft Support Policy - -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/src/SfsClient/sfs-client/TEST.md b/src/SfsClient/sfs-client/TEST.md deleted file mode 100644 index 99406e95c5..0000000000 --- a/src/SfsClient/sfs-client/TEST.md +++ /dev/null @@ -1,12 +0,0 @@ -## Testing overrides - -Some test behaviors are controlled by test overrides set via environment variables. -They only work when the CMake Option `SFS_ENABLE_TEST_OVERRIDES` is set to `ON`. -To enable that during build, you must use the switch `-EnableTestOverrides` (`--enable-test-overrides` on Linux) with the build scripts. Click [here](README.md#building) for more. See also [here](README.md#building-with-cmake-vscode-extension) for how to set when using VSCode. - -When the test overrides are enabled, a few environment variables can be used to adjust the behavior of the tool: - -| Environment Variable | Description | -|-----------------------------------------------|---------------------------------------------------------------------------------------------| -| SFS_TEST_BASE_RETRY_DELAY_MS | Set this to override the base retry delay of 15s. | -| SFS_TEST_OVERRIDE_BASE_URL | Set this to any string value which will be used as the SFS URL rather than the default one. | diff --git a/src/SfsClient/sfs-client/cgmanifest.json b/src/SfsClient/sfs-client/cgmanifest.json deleted file mode 100644 index 355bf846a5..0000000000 --- a/src/SfsClient/sfs-client/cgmanifest.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/component-detection-manifest.json", - "version": 1, - "registrations": [ - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/catchorg/Catch2", - "commitHash": "6e79e682b726f524310d55dec8ddac4e9c52fb5f" - } - }, - "developmentDependency": true - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/microsoft/CorrelationVector-Cpp", - "commitHash": "cf38d2b44baaf352509ad9980786bc49554c32e4" - } - }, - "developmentDependency": false - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/yhirose/cpp-httplib", - "commitHash": "5c00bbf36ba8ff47b4fb97712fc38cb2884e5b98" - } - }, - "developmentDependency": true - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/curl/curl", - "commitHash": "83bedbd730d62b83744cc26fa0433d3f6e2e4cd6" - } - }, - "developmentDependency": false - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/c-ares/c-ares", - "commitHash": "fddf01938d3789e06cc1c3774e4cd0c7d2a89976" - } - }, - "developmentDependency": false, - "dependencyRoots": [ - { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/curl/curl", - "commitHash": "83bedbd730d62b83744cc26fa0433d3f6e2e4cd6" - } - } - ] - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/microsoft/do-client", - "commitHash": "d71ade6f692dd8bc319ec3228c956517e9b29292" - } - }, - "developmentDependency": true - }, - { - "component": { - "type": "git", - "git": { - "repositoryUrl": "https://github.com/nlohmann/json", - "commitHash": "9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03" - } - }, - "developmentDependency": false - } - ] -} \ No newline at end of file diff --git a/src/SfsClient/sfs-client/client/CMakeLists.txt b/src/SfsClient/sfs-client/client/CMakeLists.txt deleted file mode 100644 index e2fd215ff9..0000000000 --- a/src/SfsClient/sfs-client/client/CMakeLists.txt +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# HTTP stack library -find_package(CURL REQUIRED) - -# JSON library -find_package(nlohmann_json CONFIG REQUIRED) - -# CorrelationVector Library from Microsoft -find_package(correlation_vector CONFIG REQUIRED) - -add_library(${PROJECT_NAME} STATIC) -add_library(Microsoft::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) - -target_sources( - ${PROJECT_NAME} - PRIVATE src/AppContent.cpp - src/AppFile.cpp - src/ApplicabilityDetails.cpp - src/Content.cpp - src/ContentId.cpp - src/details/connection/Connection.cpp - src/details/connection/ConnectionConfig.cpp - src/details/connection/ConnectionManager.cpp - src/details/connection/CurlConnection.cpp - src/details/connection/CurlConnectionManager.cpp - src/details/connection/HttpHeader.cpp - src/details/connection/mock/MockConnection.cpp - src/details/connection/mock/MockConnectionManager.cpp - src/details/ContentUtil.cpp - src/details/CorrelationVector.cpp - src/details/entity/ContentType.cpp - src/details/entity/FileEntity.cpp - src/details/entity/VersionEntity.cpp - src/details/Env.cpp - src/details/ErrorHandling.cpp - src/details/OSInfo.cpp - src/details/ReportingHandler.cpp - src/details/SFSClientImpl.cpp - src/details/SFSException.cpp - src/details/SFSUrlBuilder.cpp - src/details/TestOverride.cpp - src/details/UrlBuilder.cpp - src/details/Util.cpp - src/File.cpp - src/Logging.cpp - src/Result.cpp - src/SFSClient.cpp) - -# Include dir for the library depends on whether the library is being built or -# if it was installed -target_include_directories( - ${PROJECT_NAME} - PUBLIC $ - $ # /include -) - -target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) -target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) -target_link_libraries(${PROJECT_NAME} PRIVATE microsoft::correlation_vector) - -# Pick up git revision during configuration to add to logging -include(FindGit) -if(GIT_FOUND) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SFS_GIT_HEAD_NAME - OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SFS_GIT_HEAD_REVISION - OUTPUT_STRIP_TRAILING_WHITESPACE) - - target_compile_definitions( - ${PROJECT_NAME} - PRIVATE SFS_GIT_INFO="${SFS_GIT_HEAD_NAME}:${SFS_GIT_HEAD_REVISION}") -endif() - -set_compile_options_for_target(${PROJECT_NAME}) - -target_compile_definitions(${PROJECT_NAME} - PRIVATE SFS_VERSION="${SFS_LIBRARY_VERSION}") - -if(SFS_ENABLE_TEST_OVERRIDES) - target_compile_definitions(${PROJECT_NAME} - PRIVATE SFS_ENABLE_TEST_OVERRIDES=1) -endif() - -if(SFS_BUILD_TESTS) - # Enables one to run tests through "ctest --test-dir .\build\client" - enable_testing() - - add_subdirectory(tests) -endif() - -# -# Install section -# - -set(_INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/sfsclient") -set(_TARGET_CONFIG_CMAKE_FILEPATH - "${CMAKE_CURRENT_BINARY_DIR}/sfsclient-config.cmake") - -# Install headers -install( - FILES include/sfsclient/AppContent.h - include/sfsclient/AppFile.h - include/sfsclient/ApplicabilityDetails.h - include/sfsclient/ClientConfig.h - include/sfsclient/Content.h - include/sfsclient/ContentId.h - include/sfsclient/File.h - include/sfsclient/Logging.h - include/sfsclient/RequestParams.h - include/sfsclient/Result.h - include/sfsclient/SFSClient.h - DESTINATION include/sfsclient) - -# Export targets for this library to a local file -install( - TARGETS ${PROJECT_NAME} - EXPORT sfsclient-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -# And then install the exported targets file to the destination -install( - EXPORT sfsclient-targets - FILE sfsclient-targets.cmake - NAMESPACE microsoft:: - DESTINATION ${_INSTALL_DESTINATION}) - -# Add configure_package_config_file for below -include(CMakePackageConfigHelpers) - -# Create the sfsclient-config.cmake file, which will be used by find_package() -configure_package_config_file( - ../cmake/sfsclient-config.cmake.in ${_TARGET_CONFIG_CMAKE_FILEPATH} - INSTALL_DESTINATION ${_INSTALL_DESTINATION}) - -# Then install the generated file to the destination -install(FILES ${_TARGET_CONFIG_CMAKE_FILEPATH} - DESTINATION ${_INSTALL_DESTINATION}) diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/AppContent.h b/src/SfsClient/sfs-client/client/include/sfsclient/AppContent.h deleted file mode 100644 index e70eb538ba..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/AppContent.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "AppFile.h" -#include "Content.h" - -#include -#include -#include - -namespace SFS -{ -class AppPrerequisiteContent -{ - public: - [[nodiscard]] static Result Make(std::unique_ptr&& contentId, - std::vector&& files, - std::unique_ptr& out) noexcept; - - AppPrerequisiteContent(AppPrerequisiteContent&&) noexcept; - - AppPrerequisiteContent(const AppPrerequisiteContent&) = delete; - AppPrerequisiteContent& operator=(const AppPrerequisiteContent&) = delete; - - /** - * @return Unique content identifier - */ - const ContentId& GetContentId() const noexcept; - - /** - * @return Files belonging to this Prequisite - */ - const std::vector& GetFiles() const noexcept; - - private: - AppPrerequisiteContent() = default; - - std::unique_ptr m_contentId; - std::vector m_files; -}; - -class AppContent -{ - public: - [[nodiscard]] static Result Make(std::unique_ptr&& contentId, - std::string updateId, - std::vector&& prerequisites, - std::vector&& files, - std::unique_ptr& out) noexcept; - - AppContent(AppContent&&) noexcept; - - AppContent(const AppContent&) = delete; - AppContent& operator=(const AppContent&) = delete; - - /** - * @return Unique content identifier - */ - const ContentId& GetContentId() const noexcept; - - /** - * @return Unique Update Id - */ - const std::string& GetUpdateId() const noexcept; - - /** - * @return Files belonging to this App - */ - const std::vector& GetFiles() const noexcept; - - /** - * @return List of Prerequisite content needed for this App. Prerequisites don't have further dependencies. - */ - const std::vector& GetPrerequisites() const noexcept; - - private: - AppContent() = default; - - std::unique_ptr m_contentId; - std::vector m_files; - - std::string m_updateId; - std::vector m_prerequisites; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/AppFile.h b/src/SfsClient/sfs-client/client/include/sfsclient/AppFile.h deleted file mode 100644 index 35e4f94ee2..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/AppFile.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "ApplicabilityDetails.h" -#include "File.h" - -#include -#include -#include -#include - -namespace SFS -{ -class AppFile : private File -{ - public: - [[nodiscard]] static Result Make(std::string fileId, - std::string url, - uint64_t sizeInBytes, - std::unordered_map hashes, - std::vector architectures, - std::vector platformApplicabilityForPackage, - std::string fileMoniker, - std::unique_ptr& out) noexcept; - - AppFile(AppFile&&) noexcept; - - AppFile(const AppFile&) = delete; - AppFile& operator=(const AppFile&) = delete; - - /// Getter methods from File class - using File::GetFileId; - using File::GetHashes; - using File::GetSizeInBytes; - using File::GetUrl; - - /** - * @return Set of details related to applicability of the file - */ - const ApplicabilityDetails& GetApplicabilityDetails() const noexcept; - - /** - * @return Package Moniker of the file - */ - const std::string& GetFileMoniker() const noexcept; - - private: - AppFile(std::string&& fileId, - std::string&& url, - uint64_t sizeInBytes, - std::unordered_map&& hashes, - std::string&& fileMoniker); - - std::unique_ptr m_applicabilityDetails; - std::string m_fileMoniker; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/ApplicabilityDetails.h b/src/SfsClient/sfs-client/client/include/sfsclient/ApplicabilityDetails.h deleted file mode 100644 index 0cbf451c8c..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/ApplicabilityDetails.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include -#include -#include - -namespace SFS -{ -enum class Architecture -{ - None, - Amd64, - Arm, - Arm64, - x86, -}; - -class ApplicabilityDetails -{ - public: - [[nodiscard]] static Result Make(std::vector architectures, - std::vector platformApplicabilityForPackage, - std::unique_ptr& out) noexcept; - - ApplicabilityDetails(const ApplicabilityDetails&) = delete; - ApplicabilityDetails& operator=(const ApplicabilityDetails&) = delete; - - const std::vector& GetArchitectures() const noexcept; - const std::vector& GetPlatformApplicabilityForPackage() const noexcept; - - private: - ApplicabilityDetails() = default; - - std::vector m_architectures; - std::vector m_platformApplicabilityForPackage; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/ClientConfig.h b/src/SfsClient/sfs-client/client/include/sfsclient/ClientConfig.h deleted file mode 100644 index 38d2defa4e..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/ClientConfig.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Logging.h" - -#include -#include - -namespace SFS -{ -/// @brief Configurations to create an SFSClient instance -struct ClientConfig -{ - /// @brief The account ID of the SFS service is used to identify the caller (required) - std::string accountId; - - /// @brief The instance ID of the SFS service - std::optional instanceId; - - /// @brief The namespace of the SFS service - std::optional nameSpace; - - /** - * @brief A logging callback function that is called when the SFSClient logs a message - * @details This function returns logging information from the SFSClient. The caller is responsible for incoporating - * the received data into their logging system. The callback will be called in the same thread as the - * main flow, so make sure the callback does not block for too long so it doesn't delay operations. The - * LogData does not exist after the callback returns, so caller has to copy it if the data will be stored. - */ - std::optional logCallbackFn; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/Content.h b/src/SfsClient/sfs-client/client/include/sfsclient/Content.h deleted file mode 100644 index 512e1058c2..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/Content.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "ContentId.h" -#include "File.h" -#include "Result.h" - -#include -#include -#include - -namespace SFS -{ -class Content -{ - public: - /** - * @brief This Make() method should be used when the caller wants the @param files to be cloned - */ - [[nodiscard]] static Result Make(std::string contentNameSpace, - std::string contentName, - std::string contentVersion, - const std::vector& files, - std::unique_ptr& out) noexcept; - - /** - * @brief This Make() method should be used when the caller wants the @param files to be moved - */ - [[nodiscard]] static Result Make(std::string contentNameSpace, - std::string contentName, - std::string contentVersion, - std::vector&& files, - std::unique_ptr& out) noexcept; - - /** - * @brief This Make() method should be used when the caller wants the @param contentId and @param files to be moved - */ - [[nodiscard]] static Result Make(std::unique_ptr&& contentId, - std::vector&& files, - std::unique_ptr& out) noexcept; - - Content(Content&&) noexcept; - - Content(const Content&) = delete; - Content& operator=(const Content&) = delete; - - /** - * @return Unique content identifier - */ - const ContentId& GetContentId() const noexcept; - - const std::vector& GetFiles() const noexcept; - - private: - Content() = default; - - std::unique_ptr m_contentId; - std::vector m_files; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/ContentId.h b/src/SfsClient/sfs-client/client/include/sfsclient/ContentId.h deleted file mode 100644 index 903cf8dedd..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/ContentId.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include -#include - -namespace SFS -{ -class ContentId -{ - public: - [[nodiscard]] static Result Make(std::string nameSpace, - std::string name, - std::string version, - std::unique_ptr& out) noexcept; - - ContentId(ContentId&&) noexcept; - - ContentId(const ContentId&) = delete; - ContentId& operator=(const ContentId&) = delete; - - /** - * @return Content namespace - */ - const std::string& GetNameSpace() const noexcept; - - /** - * @return Content name - */ - const std::string& GetName() const noexcept; - - /** - * @return 4-part integer version. Each part can range from 0-65535 - */ - const std::string& GetVersion() const noexcept; - - private: - ContentId() = default; - - std::string m_nameSpace; - std::string m_name; - std::string m_version; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/File.h b/src/SfsClient/sfs-client/client/include/sfsclient/File.h deleted file mode 100644 index 160801a41f..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/File.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include -#include -#include -#include - -namespace SFS -{ -enum class HashType -{ - Sha1, - Sha256 -}; - -class File -{ - public: - [[nodiscard]] static Result Make(std::string fileId, - std::string url, - uint64_t sizeInBytes, - std::unordered_map hashes, - std::unique_ptr& out) noexcept; - - File(File&&) noexcept; - - File(const File&) = delete; - File& operator=(const File&) = delete; - - /** - * @return Unique file identifier within a content version - */ - const std::string& GetFileId() const noexcept; - - /** - * @return Download URL - */ - const std::string& GetUrl() const noexcept; - - /** - * @return File size in number of bytes - */ - uint64_t GetSizeInBytes() const noexcept; - - /** - * @return Dictionary of algorithm type to base64 encoded file hash string - */ - const std::unordered_map& GetHashes() const noexcept; - - protected: - File(std::string&& fileId, - std::string&& url, - uint64_t sizeInBytes, - std::unordered_map&& hashes); - - [[nodiscard]] Result Clone(std::unique_ptr& out) const noexcept; - - friend class Content; - - std::string m_fileId; - std::string m_url; - uint64_t m_sizeInBytes; - std::unordered_map m_hashes; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/Logging.h b/src/SfsClient/sfs-client/client/include/sfsclient/Logging.h deleted file mode 100644 index d8608e19e0..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/Logging.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include - -namespace SFS -{ -enum class LogSeverity -{ - Info, - Warning, - Error, - Verbose, -}; - -struct LogData -{ - LogSeverity severity; - const char* message; - const char* file; - unsigned line; - const char* function; - std::chrono::time_point time; -}; - -using LoggingCallbackFn = std::function; - -std::string_view ToString(LogSeverity severity) noexcept; -}; // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/RequestParams.h b/src/SfsClient/sfs-client/client/include/sfsclient/RequestParams.h deleted file mode 100644 index b2961f5c99..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/RequestParams.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include -#include - -namespace SFS -{ -using TargetingAttributes = std::unordered_map; - -constexpr unsigned c_maxRetries = 3; - -struct ProductRequest -{ - /// @brief The name or GUID that uniquely represents the product in the service (required) - std::string product; - - /// @brief Key-value pair to filter the data retrieved from the service. Known from publishing (optional) - TargetingAttributes attributes; -}; - -/// @brief Configurations to perform a request to the SFS service -struct RequestParams -{ - /// @brief List of products to be retrieved from the server (required) - /// @note At the moment only a single product request is supported. Using a vector for future implementation of - /// batch requests - std::vector productRequests; - - /// @brief Base CorrelationVector to be used in the request for service telemetry stitching (optional) - /// @note If not provided, a new CorrelationVector will be generated - std::optional baseCV; - - /// @brief Proxy setting which can be used to establish connections with the server (optional) - /// @note The string can be a hostname or dotted numerical IP address. It can be suffixed with the port number - /// like :[port], and can be prefixed with [scheme]://. If not provided, no proxy will be used. - std::optional proxy; - - /// @brief Retry for a web request after a failed attempt. If true, client will retry up to c_maxRetries times - bool retryOnError{true}; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/Result.h b/src/SfsClient/sfs-client/client/include/sfsclient/Result.h deleted file mode 100644 index 3a6924952a..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/Result.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include - -namespace SFS -{ -class Result -{ - public: - enum Code : uint32_t - { - Success = 0x00000000, - - // - // Failure codes - // - - // Generic errors start at 0x8000'0000 - InvalidArg = 0x8000'0001, - NotImpl = 0x8000'0002, - NotSet = 0x8000'0003, - OutOfMemory = 0x8000'0004, - Unexpected = 0x8000'0005, - - // Connection errors start at 0x8000'1000 - ConnectionSetupFailed = 0x8000'1000, - ConnectionUnexpectedError = 0x8000'1001, - ConnectionUrlSetupFailed = 0x8000'1002, - - // Http Errors start at 0x8000'2000 - // Generic Http errors - HttpTimeout = 0x8000'2000, - HttpUnexpected = 0x8000'2001, - - // Last 3 digits mapped to Http status codes - HttpBadRequest = 0x8000'2400, - HttpNotFound = 0x8000'2404, - HttpMethodNotAllowed = 0x8000'2405, - HttpTooManyRequests = 0x8000'2429, - HttpServiceNotAvailable = 0x8000'2503, - - // Service errors start at 0x8000'3000 - ServiceInvalidResponse = 0x8000'3000, - ServiceUnexpectedContentType = 0x8000'3001, - }; - - Result(Code code) noexcept; - Result(Code code, std::string message) noexcept; - - Code GetCode() const noexcept; - - /// @brief Returns the message associated with the result code. - /// @note "GetMsg" is used instead of "GetMessage" to avoid conflicts with Windows API. - const std::string& GetMsg() const noexcept; - - bool IsSuccess() const noexcept; - bool IsFailure() const noexcept; - - /** - * @brief Returns true if the result code represents a successful operation. - */ - operator bool() const noexcept; - - bool operator==(Code resultCode) const noexcept; - bool operator!=(Code resultCode) const noexcept; - - // Results should not be compared directly - bool operator==(Result) const = delete; - bool operator!=(Result) const = delete; - bool operator>(Result) const = delete; - bool operator>=(Result) const = delete; - bool operator<(Result) const = delete; - bool operator<=(Result) const = delete; - - private: - Code m_code; - std::string m_message; -}; - -std::string_view ToString(Result::Code code) noexcept; -} // namespace SFS - -std::ostream& operator<<(std::ostream& os, const SFS::Result& value); -std::ostream& operator<<(std::ostream& os, const SFS::Result::Code& value); diff --git a/src/SfsClient/sfs-client/client/include/sfsclient/SFSClient.h b/src/SfsClient/sfs-client/client/include/sfsclient/SFSClient.h deleted file mode 100644 index b8b1a5b27f..0000000000 --- a/src/SfsClient/sfs-client/client/include/sfsclient/SFSClient.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "AppContent.h" -#include "ClientConfig.h" -#include "Content.h" -#include "Logging.h" -#include "RequestParams.h" -#include "Result.h" - -#include -#include -#include -#include - -namespace SFS -{ -namespace details -{ -class SFSClientInterface; -} - -class SFSClient -{ - public: - ~SFSClient() noexcept; - - SFSClient(const SFSClient&) = delete; - SFSClient& operator=(const SFSClient&) = delete; - - /** - * @brief Make a new SFSClient object - * @details An SFSClient object is used to make calls to the SFS service. The SFSClient object is initialized with - * a few parameters that are used to build the URL for the SFS service. The URL is built as follows: - * https://{accountId}.api.cdp.microsoft.com/api/v2/contents/{instanceId}/namespaces/{nameSpace} - * The instanceId and nameSpace are optionally set in @param config and have a default value if not provided. - * The accountId is required and must be set to a non-empty value. - * - * @param config Describes a set of startup configurations for the SFSClient - */ - [[nodiscard]] static Result Make(ClientConfig config, std::unique_ptr& out) noexcept; - - // - // API to retrieve download information from the SFS Service - // - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified products - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - * @param contents A vector of Content that is populated with the result - */ - [[nodiscard]] Result GetLatestDownloadInfo(const RequestParams& requestParams, - std::vector& contents) const noexcept; - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified apps - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - * @param contents A vector of AppContent that is populated with the result - */ - [[nodiscard]] Result GetLatestAppDownloadInfo(const RequestParams& requestParams, - std::vector& contents) const noexcept; - - /** - * @return The version of the SFSClient library - */ - static const char* GetVersion() noexcept; - - private: - /** - * @brief Construct a new SFSClient object - */ - SFSClient() noexcept; - - std::unique_ptr m_impl; -}; -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/src/AppContent.cpp b/src/SfsClient/sfs-client/client/src/AppContent.cpp deleted file mode 100644 index 6a7a80aae2..0000000000 --- a/src/SfsClient/sfs-client/client/src/AppContent.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "AppContent.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -Result AppPrerequisiteContent::Make(std::unique_ptr&& contentId, - std::vector&& files, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new AppPrerequisiteContent()); - tmp->m_contentId = std::move(contentId); - tmp->m_files = std::move(files); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -AppPrerequisiteContent::AppPrerequisiteContent(AppPrerequisiteContent&& other) noexcept -{ - m_contentId = std::move(other.m_contentId); - m_files = std::move(other.m_files); -} - -const ContentId& AppPrerequisiteContent::GetContentId() const noexcept -{ - return *m_contentId; -} - -const std::vector& AppPrerequisiteContent::GetFiles() const noexcept -{ - return m_files; -} - -Result AppContent::Make(std::unique_ptr&& contentId, - std::string updateId, - std::vector&& prerequisites, - std::vector&& files, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new AppContent()); - tmp->m_contentId = std::move(contentId); - tmp->m_updateId = std::move(updateId); - tmp->m_prerequisites = std::move(prerequisites); - tmp->m_files = std::move(files); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -AppContent::AppContent(AppContent&& other) noexcept -{ - m_contentId = std::move(other.m_contentId); - m_updateId = std::move(other.m_updateId); - m_prerequisites = std::move(other.m_prerequisites); - m_files = std::move(other.m_files); -} - -const ContentId& AppContent::GetContentId() const noexcept -{ - return *m_contentId; -} - -const std::string& AppContent::GetUpdateId() const noexcept -{ - return m_updateId; -} - -const std::vector& AppContent::GetFiles() const noexcept -{ - return m_files; -} - -const std::vector& AppContent::GetPrerequisites() const noexcept -{ - return m_prerequisites; -} diff --git a/src/SfsClient/sfs-client/client/src/AppFile.cpp b/src/SfsClient/sfs-client/client/src/AppFile.cpp deleted file mode 100644 index 88ea2e1726..0000000000 --- a/src/SfsClient/sfs-client/client/src/AppFile.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "AppFile.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -AppFile::AppFile(std::string&& fileId, - std::string&& url, - uint64_t sizeInBytes, - std::unordered_map&& hashes, - std::string&& fileMoniker) - : File(std::move(fileId), std::move(url), sizeInBytes, std::move(hashes)) - , m_fileMoniker(std::move(fileMoniker)) -{ -} - -Result AppFile::Make(std::string fileId, - std::string url, - uint64_t sizeInBytes, - std::unordered_map hashes, - std::vector architectures, - std::vector platformApplicabilityForPackage, - std::string fileMoniker, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr details; - RETURN_IF_FAILED( - ApplicabilityDetails::Make(std::move(architectures), std::move(platformApplicabilityForPackage), details)); - - std::unique_ptr tmp( - new AppFile(std::move(fileId), std::move(url), sizeInBytes, std::move(hashes), std::move(fileMoniker))); - tmp->m_applicabilityDetails = std::move(details); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -AppFile::AppFile(AppFile&& other) noexcept : File(std::move(other)) -{ - m_applicabilityDetails = std::move(other.m_applicabilityDetails); - m_fileMoniker = std::move(other.m_fileMoniker); -} - -const ApplicabilityDetails& AppFile::GetApplicabilityDetails() const noexcept -{ - return *m_applicabilityDetails; -} - -const std::string& AppFile::GetFileMoniker() const noexcept -{ - return m_fileMoniker; -} diff --git a/src/SfsClient/sfs-client/client/src/ApplicabilityDetails.cpp b/src/SfsClient/sfs-client/client/src/ApplicabilityDetails.cpp deleted file mode 100644 index c3fcb97e96..0000000000 --- a/src/SfsClient/sfs-client/client/src/ApplicabilityDetails.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ApplicabilityDetails.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -Result ApplicabilityDetails::Make(std::vector architectures, - std::vector platformApplicabilityForPackage, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new ApplicabilityDetails()); - tmp->m_architectures = std::move(architectures); - tmp->m_platformApplicabilityForPackage = std::move(platformApplicabilityForPackage); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -const std::vector& ApplicabilityDetails::GetArchitectures() const noexcept -{ - return m_architectures; -} - -const std::vector& ApplicabilityDetails::GetPlatformApplicabilityForPackage() const noexcept -{ - return m_platformApplicabilityForPackage; -} diff --git a/src/SfsClient/sfs-client/client/src/Content.cpp b/src/SfsClient/sfs-client/client/src/Content.cpp deleted file mode 100644 index 6b44bb7a3c..0000000000 --- a/src/SfsClient/sfs-client/client/src/Content.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Content.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -Result Content::Make(std::string contentNameSpace, - std::string contentName, - std::string contentVersion, - const std::vector& files, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new Content()); - RETURN_IF_FAILED(ContentId::Make(std::move(contentNameSpace), - std::move(contentName), - std::move(contentVersion), - tmp->m_contentId)); - - for (const auto& file : files) - { - std::unique_ptr clone; - RETURN_IF_FAILED(file.Clone(clone)); - tmp->m_files.push_back(std::move(*clone)); - } - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -Result Content::Make(std::string contentNameSpace, - std::string contentName, - std::string contentVersion, - std::vector&& files, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new Content()); - RETURN_IF_FAILED(ContentId::Make(std::move(contentNameSpace), - std::move(contentName), - std::move(contentVersion), - tmp->m_contentId)); - tmp->m_files = std::move(files); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -Result Content::Make(std::unique_ptr&& contentId, - std::vector&& files, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new Content()); - tmp->m_contentId = std::move(contentId); - tmp->m_files = std::move(files); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -Content::Content(Content&& other) noexcept -{ - m_contentId = std::move(other.m_contentId); - m_files = std::move(other.m_files); -} - -const ContentId& Content::GetContentId() const noexcept -{ - return *m_contentId; -} - -const std::vector& Content::GetFiles() const noexcept -{ - return m_files; -} diff --git a/src/SfsClient/sfs-client/client/src/ContentId.cpp b/src/SfsClient/sfs-client/client/src/ContentId.cpp deleted file mode 100644 index b6d616f857..0000000000 --- a/src/SfsClient/sfs-client/client/src/ContentId.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentId.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -Result ContentId::Make(std::string nameSpace, - std::string name, - std::string version, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new ContentId()); - tmp->m_nameSpace = std::move(nameSpace); - tmp->m_name = std::move(name); - tmp->m_version = std::move(version); - - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -ContentId::ContentId(ContentId&& other) noexcept -{ - m_nameSpace = std::move(other.m_nameSpace); - m_name = std::move(other.m_name); - m_version = std::move(other.m_version); -} - -const std::string& ContentId::GetNameSpace() const noexcept -{ - return m_nameSpace; -} - -const std::string& ContentId::GetName() const noexcept -{ - return m_name; -} - -const std::string& ContentId::GetVersion() const noexcept -{ - return m_version; -} diff --git a/src/SfsClient/sfs-client/client/src/File.cpp b/src/SfsClient/sfs-client/client/src/File.cpp deleted file mode 100644 index 5f2773c0ce..0000000000 --- a/src/SfsClient/sfs-client/client/src/File.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "File.h" - -#include "details/ErrorHandling.h" - -using namespace SFS; - -File::File(std::string&& fileId, - std::string&& url, - uint64_t sizeInBytes, - std::unordered_map&& hashes) - : m_fileId(std::move(fileId)) - , m_url(std::move(url)) - , m_sizeInBytes(sizeInBytes) - , m_hashes(std::move(hashes)) -{ -} - -Result File::Make(std::string fileId, - std::string url, - uint64_t sizeInBytes, - std::unordered_map hashes, - std::unique_ptr& out) noexcept -try -{ - out.reset(); - - std::unique_ptr tmp(new File(std::move(fileId), std::move(url), sizeInBytes, std::move(hashes))); - out = std::move(tmp); - - return Result::Success; -} -SFS_CATCH_RETURN() - -Result File::Clone(std::unique_ptr& out) const noexcept -{ - return Make(m_fileId, m_url, m_sizeInBytes, m_hashes, out); -} - -File::File(File&& other) noexcept -{ - m_fileId = std::move(other.m_fileId); - m_url = std::move(other.m_url); - m_sizeInBytes = other.m_sizeInBytes; - m_hashes = std::move(other.m_hashes); -} - -const std::string& File::GetFileId() const noexcept -{ - return m_fileId; -} - -const std::string& File::GetUrl() const noexcept -{ - return m_url; -} - -uint64_t File::GetSizeInBytes() const noexcept -{ - return m_sizeInBytes; -} - -const std::unordered_map& File::GetHashes() const noexcept -{ - return m_hashes; -} diff --git a/src/SfsClient/sfs-client/client/src/Logging.cpp b/src/SfsClient/sfs-client/client/src/Logging.cpp deleted file mode 100644 index 35ee5f6368..0000000000 --- a/src/SfsClient/sfs-client/client/src/Logging.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Logging.h" - -using SFS::LogSeverity; - -std::string_view SFS::ToString(LogSeverity severity) noexcept -{ - switch (severity) - { - case LogSeverity::Info: - return "Info"; - case LogSeverity::Warning: - return "Warning"; - case LogSeverity::Error: - return "Error"; - case LogSeverity::Verbose: - return "Verbose"; - } - return ""; -} diff --git a/src/SfsClient/sfs-client/client/src/Result.cpp b/src/SfsClient/sfs-client/client/src/Result.cpp deleted file mode 100644 index c65d600916..0000000000 --- a/src/SfsClient/sfs-client/client/src/Result.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Result.h" - -#include - -using namespace SFS; - -Result::Result(Code code) noexcept : m_code(code) -{ -} - -Result::Result(Code code, std::string message) noexcept : Result(code) -{ - try - { - m_message = std::move(message); - } - catch (...) - { - // Ignore exception, leave message empty - } -} - -Result::Code Result::GetCode() const noexcept -{ - return m_code; -} - -const std::string& Result::GetMsg() const noexcept -{ - return m_message; -} - -bool Result::IsSuccess() const noexcept -{ - return GetCode() == Success; -} - -bool Result::IsFailure() const noexcept -{ - return !IsSuccess(); -} - -Result::operator bool() const noexcept -{ - return IsSuccess(); -} - -bool Result::operator==(Code resultCode) const noexcept -{ - return GetCode() == resultCode; -} - -bool Result::operator!=(Code resultCode) const noexcept -{ - return GetCode() != resultCode; -} - -std::string_view SFS::ToString(Result::Code code) noexcept -{ - switch (code) - { - case Result::Success: - return "Success"; - - // - // Failure codes - // - - // Generic errors - case Result::InvalidArg: - return "InvalidArg"; - case Result::NotImpl: - return "NotImpl"; - case Result::NotSet: - return "NotSet"; - case Result::OutOfMemory: - return "OutOfMemory"; - case Result::Unexpected: - return "Unexpected"; - - // Connection errors - case Result::ConnectionSetupFailed: - return "ConnectionSetupFailed"; - case Result::ConnectionUnexpectedError: - return "ConnectionUnexpectedError"; - case Result::ConnectionUrlSetupFailed: - return "ConnectionUrlSetupFailed"; - - // Http Errors - case Result::HttpTimeout: - return "HttpTimeout"; - case Result::HttpUnexpected: - return "HttpUnexpected"; - case Result::HttpBadRequest: - return "HttpBadRequest"; - case Result::HttpNotFound: - return "HttpNotFound"; - case Result::HttpMethodNotAllowed: - return "HttpMethodNotAllowed"; - case Result::HttpTooManyRequests: - return "HttpTooManyRequests"; - case Result::HttpServiceNotAvailable: - return "HttpServiceNotAvailable"; - - // Service errors - case Result::ServiceInvalidResponse: - return "ServiceInvalidResponse"; - case Result::ServiceUnexpectedContentType: - return "ServiceUnexpectedContentType"; - } - return ""; -} - -std::ostream& operator<<(std::ostream& os, const Result& result) -{ - os << ToString(result.GetCode()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const Result::Code& code) -{ - os << ToString(code); - return os; -} diff --git a/src/SfsClient/sfs-client/client/src/SFSClient.cpp b/src/SfsClient/sfs-client/client/src/SFSClient.cpp deleted file mode 100644 index c0b62ba805..0000000000 --- a/src/SfsClient/sfs-client/client/src/SFSClient.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "SFSClient.h" - -#include "details/ErrorHandling.h" -#include "details/ReportingHandler.h" -#include "details/SFSClientImpl.h" -#include "details/connection/CurlConnectionManager.h" - -using namespace SFS; -using namespace SFS::details; - -// Defining the constructor and destructor here allows us to use a unique_ptr to SFSClientImpl in the header file -SFSClient::SFSClient() noexcept = default; -SFSClient::~SFSClient() noexcept = default; - -Result SFSClient::Make(ClientConfig config, std::unique_ptr& out) noexcept -try -{ - out.reset(); - std::unique_ptr tmp(new SFSClient()); - tmp->m_impl = std::make_unique>(std::move(config)); - out = std::move(tmp); - - LOG_INFO(out->m_impl->GetReportingHandler(), "SFSClient instance created successfully. Version: %s", GetVersion()); - - return Result::Success; -} -SFS_CATCH_RETURN() - -Result SFSClient::GetLatestDownloadInfo(const RequestParams& requestParams, - std::vector& contents) const noexcept -try -{ - contents = m_impl->GetLatestDownloadInfo(requestParams); - return Result::Success; -} -SFS_CATCH_RETURN() - -Result SFSClient::GetLatestAppDownloadInfo(const RequestParams& requestParams, - std::vector& contents) const noexcept -try -{ - contents = m_impl->GetLatestAppDownloadInfo(requestParams); - return Result::Success; -} -SFS_CATCH_RETURN() - -const char* SFSClient::GetVersion() noexcept -{ -#ifdef SFS_GIT_INFO - return SFS_VERSION " (" SFS_GIT_INFO ")"; -#else - return SFS_VERSION; -#endif -} diff --git a/src/SfsClient/sfs-client/client/src/details/ContentUtil.cpp b/src/SfsClient/sfs-client/client/src/details/ContentUtil.cpp deleted file mode 100644 index 3f6a175a92..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ContentUtil.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" - -#include - -using namespace SFS; -using namespace SFS::details; - -bool contentutil::operator==(const ContentId& lhs, const ContentId& rhs) -{ - // String characters can be UTF-8 encoded, so we need to compare them in a case-sensitive manner. - return lhs.GetNameSpace() == rhs.GetNameSpace() && lhs.GetName() == rhs.GetName() && - lhs.GetVersion() == rhs.GetVersion(); -} - -bool contentutil::operator!=(const ContentId& lhs, const ContentId& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const File& lhs, const File& rhs) -{ - // String characters can be UTF-8 encoded, so we need to compare them in a case-sensitive manner. - return lhs.GetFileId() == rhs.GetFileId() && lhs.GetUrl() == rhs.GetUrl() && - lhs.GetSizeInBytes() == rhs.GetSizeInBytes() && lhs.GetHashes() == rhs.GetHashes(); -} - -bool contentutil::operator!=(const File& lhs, const File& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const ApplicabilityDetails& lhs, const ApplicabilityDetails& rhs) -{ - // String characters can be UTF-8 encoded, so we need to compare them in a case-sensitive manner. - return lhs.GetArchitectures() == rhs.GetArchitectures() && - lhs.GetPlatformApplicabilityForPackage() == rhs.GetPlatformApplicabilityForPackage(); -} - -bool contentutil::operator!=(const ApplicabilityDetails& lhs, const ApplicabilityDetails& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const AppFile& lhs, const AppFile& rhs) -{ - // String characters can be UTF-8 encoded, so we need to compare them in a case-sensitive manner. - return lhs.GetFileId() == rhs.GetFileId() && lhs.GetUrl() == rhs.GetUrl() && - lhs.GetSizeInBytes() == rhs.GetSizeInBytes() && lhs.GetHashes() == rhs.GetHashes() && - lhs.GetApplicabilityDetails() == rhs.GetApplicabilityDetails() && - lhs.GetFileMoniker() == rhs.GetFileMoniker(); -} - -bool contentutil::operator!=(const AppFile& lhs, const AppFile& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const Content& lhs, const Content& rhs) -{ - return lhs.GetContentId() == rhs.GetContentId() && - (std::is_permutation(lhs.GetFiles().begin(), - lhs.GetFiles().end(), - rhs.GetFiles().begin(), - rhs.GetFiles().end(), - [](const File& flhs, const File& frhs) { return flhs == frhs; })); -} - -bool contentutil::operator!=(const Content& lhs, const Content& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const AppPrerequisiteContent& lhs, const AppPrerequisiteContent& rhs) -{ - auto areFilesEqual = [&lhs, &rhs]() { - return std::is_permutation(lhs.GetFiles().begin(), - lhs.GetFiles().end(), - rhs.GetFiles().begin(), - rhs.GetFiles().end(), - [](const AppFile& flhs, const AppFile& frhs) { return flhs == frhs; }); - }; - return lhs.GetContentId() == rhs.GetContentId() && areFilesEqual(); -} - -bool contentutil::operator!=(const AppPrerequisiteContent& lhs, const AppPrerequisiteContent& rhs) -{ - return !(lhs == rhs); -} - -bool contentutil::operator==(const AppContent& lhs, const AppContent& rhs) -{ - auto arePrerequisitesEqual = [&lhs, &rhs]() { - return std::equal( - lhs.GetPrerequisites().begin(), - lhs.GetPrerequisites().end(), - rhs.GetPrerequisites().begin(), - rhs.GetPrerequisites().end(), - [](const AppPrerequisiteContent& clhs, const AppPrerequisiteContent& crhs) { return clhs == crhs; }); - }; - auto areFilesEqual = [&lhs, &rhs]() { - return std::is_permutation(lhs.GetFiles().begin(), - lhs.GetFiles().end(), - rhs.GetFiles().begin(), - rhs.GetFiles().end(), - [](const AppFile& flhs, const AppFile& frhs) { return flhs == frhs; }); - }; - return lhs.GetContentId() == rhs.GetContentId() && lhs.GetUpdateId() == rhs.GetUpdateId() && - arePrerequisitesEqual() && areFilesEqual(); -} - -bool contentutil::operator!=(const AppContent& lhs, const AppContent& rhs) -{ - return !(lhs == rhs); -} diff --git a/src/SfsClient/sfs-client/client/src/details/ContentUtil.h b/src/SfsClient/sfs-client/client/src/details/ContentUtil.h deleted file mode 100644 index 64c36d6541..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ContentUtil.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "AppContent.h" -#include "Content.h" - -namespace SFS::details -{ -class ReportingHandler; - -namespace contentutil -{ -// -// Comparison operators -// - -/// @brief Compares two ContentId objects for equality. The values of members are strictly compared. -bool operator==(const ContentId& lhs, const ContentId& rhs); - -/// @brief Compares two ContentId objects for inequality. The values of members are strictly compared. -bool operator!=(const ContentId& lhs, const ContentId& rhs); - -/// @brief Compares two File objects for equality. The values of members are strictly compared. -bool operator==(const File& lhs, const File& rhs); - -/// @brief Compares two File objects for inequality. The values of members are strictly compared. -bool operator!=(const File& lhs, const File& rhs); - -/// @brief Compares two ApplicabilityDetails objects for equality. The values of members are strictly compared. -bool operator==(const ApplicabilityDetails& lhs, const ApplicabilityDetails& rhs); - -/// @brief Compares two ApplicabilityDetails objects for inequality. The values of members are strictly compared. -bool operator!=(const ApplicabilityDetails& lhs, const ApplicabilityDetails& rhs); - -/// @brief Compares two AppFile objects for equality. The values of members are strictly compared. -bool operator==(const AppFile& lhs, const AppFile& rhs); - -/// @brief Compares two AppFile objects for inequality. The values of members are strictly compared. -bool operator!=(const AppFile& lhs, const AppFile& rhs); - -/// @brief Compares two Content objects for equality. The values of members are strictly compared. -bool operator==(const Content& lhs, const Content& rhs); - -/// @brief Compares two Content objects for inequality. The values of members are strictly compared. -bool operator!=(const Content& lhs, const Content& rhs); - -/// @brief Compares two AppPrerequisiteContent objects for equality. The values of members are strictly compared. -bool operator==(const AppPrerequisiteContent& lhs, const AppPrerequisiteContent& rhs); - -/// @brief Compares two AppPrerequisiteContent objects for inequality. The values of members are strictly compared. -bool operator!=(const AppPrerequisiteContent& lhs, const AppPrerequisiteContent& rhs); - -/// @brief Compares two AppContent objects for equality. The values of members are strictly compared. -bool operator==(const AppContent& lhs, const AppContent& rhs); - -/// @brief Compares two AppContent objects for inequality. The values of members are strictly compared. -bool operator!=(const AppContent& lhs, const AppContent& rhs); -} // namespace contentutil -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/CorrelationVector.cpp b/src/SfsClient/sfs-client/client/src/details/CorrelationVector.cpp deleted file mode 100644 index 5aae74ce33..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/CorrelationVector.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "CorrelationVector.h" -#include "ErrorHandling.h" - -#include - -#include - -using namespace SFS::details; -using namespace microsoft; - -CorrelationVector::CorrelationVector() : m_cv(std::make_unique()) -{ -} - -CorrelationVector::CorrelationVector(const std::string& cv, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(InvalidArg, cv.empty(), handler, "cv must not be empty"); - - try - { - m_cv = std::make_unique(correlation_vector::extend(cv)); - } - catch (std::invalid_argument& e) - { - THROW_LOG(Result(Result::InvalidArg, "baseCV is not a valid correlation vector: " + std::string(e.what())), - handler); - } -} - -CorrelationVector::~CorrelationVector() = default; -CorrelationVector::CorrelationVector(CorrelationVector&&) = default; -CorrelationVector& CorrelationVector::operator=(CorrelationVector&&) = default; - -std::string CorrelationVector::IncrementAndGet() -{ - // Only increment after it's used at least once - if (m_isFirstUse) - { - m_isFirstUse = false; - } - else - { - Increment(); - } - - return m_cv->to_string(); -} - -void CorrelationVector::Increment() -{ - m_cv->increment(); -} diff --git a/src/SfsClient/sfs-client/client/src/details/CorrelationVector.h b/src/SfsClient/sfs-client/client/src/details/CorrelationVector.h deleted file mode 100644 index b5055c70df..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/CorrelationVector.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include - -namespace microsoft -{ -class correlation_vector; -} - -namespace SFS::details -{ -class ReportingHandler; - -class CorrelationVector -{ - public: - CorrelationVector(); - CorrelationVector(const std::string& cv, const ReportingHandler& handler); - - ~CorrelationVector(); - - CorrelationVector(CorrelationVector&&); - CorrelationVector& operator=(CorrelationVector&&); - - /** - * @brief Returns the current correlation vector and increments the internal state - */ - std::string IncrementAndGet(); - - private: - void Increment(); - - std::unique_ptr m_cv; - bool m_isFirstUse = true; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/Env.cpp b/src/SfsClient/sfs-client/client/src/details/Env.cpp deleted file mode 100644 index da04e74c3a..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/Env.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Env.h" - -#include "ErrorHandling.h" - -#include - -using namespace SFS::details; -using SFS::details::env::ScopedEnv; - -std::optional env::GetEnv(const std::string& varName) -{ - if (varName.empty()) - { - return std::nullopt; - } -#ifdef _WIN32 - size_t len; - char* buf; - if (_dupenv_s(&buf, &len, varName.c_str()) == 0 && buf != nullptr) - { - std::string result{buf}; - free(buf); - return result; - } -#else - if (const char* envValue = std::getenv(varName.c_str())) - { - return std::string(envValue); - } -#endif - // Return std::nullopt if the environment variable is not set or in case of failure - return std::nullopt; -} - -bool env::SetEnv(const std::string& varName, const std::string& value) -{ - if (varName.empty() || value.empty()) - { - return false; - } -#ifdef _WIN32 - return _putenv_s(varName.c_str(), value.c_str()) == 0; -#else - return setenv(varName.c_str(), value.c_str(), 1 /*overwrite*/) == 0; -#endif -} - -bool env::UnsetEnv(const std::string& varName) -{ - if (varName.empty()) - { - return false; - } -#ifdef _WIN32 - // On Windows, setting the value to an empty string is equivalent to unsetting the variable - return _putenv_s(varName.c_str(), "") == 0; -#else - return unsetenv(varName.c_str()) == 0; -#endif -} - -ScopedEnv::ScopedEnv(std::string varName, const std::string& value) : m_varName(std::move(varName)) -{ - m_oldValue = GetEnv(m_varName); - if (!SetEnv(m_varName, value)) - { - throw SFSException(Result::Unexpected, "Failed to set environment variable"); - } -} - -ScopedEnv::~ScopedEnv() -{ - if (m_oldValue) - { - SetEnv(m_varName, *m_oldValue); - } - else - { - UnsetEnv(m_varName); - } -} diff --git a/src/SfsClient/sfs-client/client/src/details/Env.h b/src/SfsClient/sfs-client/client/src/details/Env.h deleted file mode 100644 index b33487e895..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/Env.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include - -namespace SFS::details::env -{ -/** - * @brief Get the value of an environment variable. - * @details std::nullopt is returned if the environment variable is not set or in case of failure. - * The returned string is encoded in ASCII in Windows, not UTF-8. - */ -std::optional GetEnv(const std::string& varName); - -/** - * @brief Set the value of an environment variable. - * @return false in case of failure. - */ -bool SetEnv(const std::string& varName, const std::string& value); - -/** - * @brief Unset the value of an environment variable. - * @return false in case of failure. If the environment variable didn't exist, it still returns true. - */ -bool UnsetEnv(const std::string& varName); - -class ScopedEnv -{ - public: - ScopedEnv(std::string varName, const std::string& value); - ~ScopedEnv(); - - private: - std::string m_varName; - std::optional m_oldValue; -}; -} // namespace SFS::details::env diff --git a/src/SfsClient/sfs-client/client/src/details/ErrorHandling.cpp b/src/SfsClient/sfs-client/client/src/details/ErrorHandling.cpp deleted file mode 100644 index 6e465a21b1..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ErrorHandling.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ErrorHandling.h" - -#include "ReportingHandler.h" - -using namespace SFS::details; - -void SFS::details::LogFailedResult(const SFS::Result& result, - const ReportingHandler& handler, - const char* file, - unsigned line) -{ - if (result.IsFailure()) - { - LOG_ERROR(handler, - "FAILED [%s] %s%s(%s:%u)", - std::string(ToString(result.GetCode())).c_str(), - result.GetMsg().c_str(), - result.GetMsg().empty() ? "" : " ", - file, - line); - } -} - -void SFS::details::LogIfFailed(const Result& result, const ReportingHandler& handler, const char* file, unsigned line) -{ - if (result.IsFailure()) - { - LogFailedResult(result, handler, file, line); - } -} - -void SFS::details::ThrowLog(Result result, const ReportingHandler& handler, const char* file, unsigned line) -{ - assert(result.IsFailure()); - LogFailedResult(result, handler, file, line); - throw SFSException(std::move(result)); -} - -void SFS::details::ThrowIfFailedLog(Result result, const ReportingHandler& handler, const char* file, unsigned line) -{ - if (result.IsFailure()) - { - LogFailedResult(result, handler, file, line); - throw SFSException(std::move(result)); - } -} - -void SFS::details::ThrowCodeIf(Result::Code code, bool condition, std::string message) -{ - if (condition) - { - Result result(code, std::move(message)); - assert(result.IsFailure()); - throw SFSException(std::move(result)); - } -} - -void SFS::details::ThrowCodeIfLog(Result::Code code, - bool condition, - const ReportingHandler& handler, - const char* file, - unsigned line, - std::string message) -{ - if (condition) - { - Result result(code, std::move(message)); - assert(result.IsFailure()); - LogFailedResult(result, handler, file, line); - throw SFSException(std::move(result)); - } -} diff --git a/src/SfsClient/sfs-client/client/src/details/ErrorHandling.h b/src/SfsClient/sfs-client/client/src/details/ErrorHandling.h deleted file mode 100644 index d01a905713..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ErrorHandling.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" -#include "SFSException.h" - -#include - -#define SFS_CATCH_RETURN() \ - catch (const std::bad_alloc&) \ - { \ - return Result::OutOfMemory; \ - } \ - catch (const SFS::details::SFSException& e) \ - { \ - return e.GetResult(); \ - } \ - catch (const std::exception&) \ - { \ - return Result::Unexpected; \ - } \ - catch (...) \ - { \ - return Result::Unexpected; \ - } - -#define SFS_CATCH_LOG_RETHROW(handler) \ - catch (const SFS::details::SFSException& e) \ - { \ - SFS::details::LogFailedResult(e.GetResult(), handler, __FILE__, __LINE__); \ - throw; \ - } - -#define RETURN_IF_FAILED(result) \ - do \ - { \ - auto __result = (result); /* Assigning to a variable ensures a code block gets called only once */ \ - if (__result.IsFailure()) \ - { \ - return __result; \ - } \ - } while ((void)0, 0) - -#define RETURN_IF_FAILED_LOG(result, handler) \ - do \ - { \ - auto __result = (result); /* Assigning to a variable ensures a code block gets called only once */ \ - if (__result.IsFailure()) \ - { \ - SFS::details::LogFailedResult(__result, handler, __FILE__, __LINE__); \ - return __result; \ - } \ - } while ((void)0, 0) - -#define LOG_IF_FAILED(result, handler) LogIfFailed(result, handler, __FILE__, __LINE__) - -#define THROW_LOG(result, handler) ThrowLog(result, handler, __FILE__, __LINE__) - -#define THROW_IF_FAILED_LOG(result, handler) ThrowIfFailedLog(result, handler, __FILE__, __LINE__) - -#define THROW_CODE_IF(code, condition, ...) ThrowCodeIf(SFS::Result::code, condition, ##__VA_ARGS__) - -#define THROW_CODE_IF_LOG(code, condition, handler, ...) \ - ThrowCodeIfLog(SFS::Result::code, condition, handler, __FILE__, __LINE__, ##__VA_ARGS__) - -#define THROW_CODE_IF_NOT_LOG(code, condition, handler, ...) \ - ThrowCodeIfLog(SFS::Result::code, !(condition), handler, __FILE__, __LINE__, ##__VA_ARGS__) - -namespace SFS::details -{ -class ReportingHandler; - -void LogFailedResult(const Result& result, const ReportingHandler& handler, const char* file, unsigned line); -void LogIfFailed(const Result& result, const ReportingHandler& handler, const char* file, unsigned line); - -void ThrowLog(Result result, const ReportingHandler& handler, const char* file, unsigned line); -void ThrowIfFailedLog(Result result, const ReportingHandler& handler, const char* file, unsigned line); -void ThrowCodeIf(Result::Code code, bool condition, std::string message = {}); -void ThrowCodeIfLog(Result::Code code, - bool condition, - const ReportingHandler& handler, - const char* file, - unsigned line, - std::string message = {}); -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/OSInfo.cpp b/src/SfsClient/sfs-client/client/src/details/OSInfo.cpp deleted file mode 100644 index 9786cf55fb..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/OSInfo.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "OSInfo.h" - -#ifdef _WIN32 - -#include - -#include - -#elif __linux__ - -#include - -#endif - -using namespace SFS::details; - -namespace -{ -#ifdef _WIN32 -std::string GetPlatform() -{ - return "Windows"; -} - -std::string GetOSMachineInfo() -{ - SYSTEM_INFO systemInfo; - GetNativeSystemInfo(&systemInfo); - - switch (systemInfo.wProcessorArchitecture) - { - case PROCESSOR_ARCHITECTURE_AMD64: - return "x64"; - case PROCESSOR_ARCHITECTURE_ARM: - return "ARM"; - case PROCESSOR_ARCHITECTURE_ARM64: - return "ARM64"; - case PROCESSOR_ARCHITECTURE_INTEL: - return "x86"; - default: - return "Unknown"; - } -} - -#elif __linux__ - -std::string GetPlatform() -{ - // Return kernel name, usually "Linux" - utsname buf; - if (uname(&buf) >= 0) - { - return buf.sysname; - } - return "Unknown"; -} - -std::string GetOSMachineInfo() -{ - utsname buf; - if (uname(&buf) >= 0) - { - return buf.machine; - } - return "Unknown"; -} - -#else -#error "Unsupported platform" -#endif -} // namespace - -std::string osinfo::GetPlatform() -{ - return ::GetPlatform(); -} - -std::string osinfo::GetOSMachineInfo() -{ - return ::GetOSMachineInfo(); -} diff --git a/src/SfsClient/sfs-client/client/src/details/OSInfo.h b/src/SfsClient/sfs-client/client/src/details/OSInfo.h deleted file mode 100644 index 357af007e7..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/OSInfo.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace SFS::details::osinfo -{ -std::string GetPlatform(); -std::string GetOSMachineInfo(); -} // namespace SFS::details::osinfo diff --git a/src/SfsClient/sfs-client/client/src/details/ReportingHandler.cpp b/src/SfsClient/sfs-client/client/src/details/ReportingHandler.cpp deleted file mode 100644 index 427817c2c2..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ReportingHandler.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ReportingHandler.h" - -#include - -using namespace SFS; -using namespace SFS::details; - -void ReportingHandler::SetLoggingCallback(LoggingCallbackFn&& callback) -{ - std::lock_guard guard(m_loggingCallbackFnMutex); - m_loggingCallbackFn = std::move(callback); -} - -void ReportingHandler::CallLoggingCallback(LogSeverity severity, - const char* message, - const char* file, - unsigned line, - const char* function) const -{ - std::lock_guard guard(m_loggingCallbackFnMutex); - if (m_loggingCallbackFn) - { - m_loggingCallbackFn({severity, message, file, line, function, std::chrono::system_clock::now()}); - } -} diff --git a/src/SfsClient/sfs-client/client/src/details/ReportingHandler.h b/src/SfsClient/sfs-client/client/src/details/ReportingHandler.h deleted file mode 100644 index 44f5a5d1b8..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/ReportingHandler.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Logging.h" - -#include -#include - -#define MAX_LOG_MESSAGE_SIZE 1024 - -#define LOG_INFO(handler, format, ...) \ - handler.LogWithSeverity(SFS::LogSeverity::Info, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) -#define LOG_WARNING(handler, format, ...) \ - handler.LogWithSeverity(SFS::LogSeverity::Warning, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) -#define LOG_ERROR(handler, format, ...) \ - handler.LogWithSeverity(SFS::LogSeverity::Error, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) -#define LOG_VERBOSE(handler, format, ...) \ - handler.LogWithSeverity(SFS::LogSeverity::Verbose, __FILE__, __LINE__, __FUNCTION__, format, ##__VA_ARGS__) - -namespace SFS::details -{ -/** - * @brief This class enables thread-safe access to the externally set logging callback function. - * @details Each SFSClient instance will have one ReportingHandler instance, and access to the logging callback function - * is controlled by a mutex that makes sure that only one thread can access the logging callback function at a time. - */ -class ReportingHandler -{ - public: - ReportingHandler() = default; - - ReportingHandler(const ReportingHandler&) = delete; - ReportingHandler& operator=(const ReportingHandler&) = delete; - - /** - * @brief Sets the logging callback function. - * @details This function is thread-safe. - * @param callback The logging callback function. To reset, pass a nullptr. - */ - void SetLoggingCallback(LoggingCallbackFn&& callback); - - /** - * @brief Logs a message with the given severity. - * @details Prefer calling it with macros LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_VERBOSE so file, line and function - * are automatically populated and the message can be formatted. - */ - template - void LogWithSeverity(LogSeverity severity, - const char* file, - unsigned line, - const char* function, - const char* format, - const Args&... args) const - { - constexpr std::size_t n = sizeof...(Args); - if constexpr (n == 0) - { - CallLoggingCallback(severity, format, file, line, function); - } - else - { - char message[MAX_LOG_MESSAGE_SIZE]; - snprintf(message, MAX_LOG_MESSAGE_SIZE, format, args...); - CallLoggingCallback(severity, message, file, line, function); - } - } - - private: - void CallLoggingCallback(LogSeverity severity, - const char* message, - const char* file, - unsigned line, - const char* function) const; - - LoggingCallbackFn m_loggingCallbackFn; - mutable std::mutex m_loggingCallbackFnMutex; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.cpp b/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.cpp deleted file mode 100644 index 87373e55cd..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.cpp +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "SFSClientImpl.h" - -#include "AppContent.h" -#include "Content.h" -#include "ErrorHandling.h" -#include "Logging.h" -#include "TestOverride.h" -#include "Util.h" -#include "connection/Connection.h" -#include "connection/ConnectionManager.h" -#include "connection/CurlConnectionManager.h" -#include "connection/mock/MockConnectionManager.h" - -#include - -#include - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::util; -using json = nlohmann::json; - -constexpr const char* c_defaultInstanceId = "default"; -constexpr const char* c_defaultNameSpace = "default"; - -namespace -{ -void ValidateClientConfig(const ClientConfig& config, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(InvalidArg, config.accountId.empty(), handler, "ClientConfig::accountId must not be empty"); - - if (config.instanceId) - { - THROW_CODE_IF_LOG(InvalidArg, - config.instanceId->empty(), - handler, - "ClientConfig::instanceId must not be empty"); - } - - if (config.nameSpace) - { - THROW_CODE_IF_LOG(InvalidArg, config.nameSpace->empty(), handler, "ClientConfig::nameSpace must not be empty"); - } -} - -void LogIfTestOverridesAllowed(const ReportingHandler& handler) -{ - if (test::AreTestOverridesAllowed()) - { - LOG_INFO(handler, "Test overrides are allowed"); - } -} - -void ThrowInvalidResponseIfFalse(bool condition, const std::string& message, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(ServiceInvalidResponse, !condition, handler, message); -} - -json ParseServerMethodStringToJson(const std::string& data, const std::string& method, const ReportingHandler& handler) -{ - try - { - return json::parse(data); - } - catch (json::parse_error& ex) - { - THROW_LOG( - Result(Result::ServiceInvalidResponse, "(" + method + ") JSON Parsing error: " + std::string(ex.what())), - handler); - return json(); // Unreachable code, but the compiler doesn't know that. - } -} - -VersionEntities ConvertLatestVersionBatchResponseToVersionEntities(const json& data, const ReportingHandler& handler) -{ - // Expected format: - // [ - // { - // "ContentId": { - // "Namespace": , - // "Name": , - // "Version": - // } - // }, - // ... - // ] - // - - ThrowInvalidResponseIfFalse(data.is_array(), "Response is not a JSON array", handler); - ThrowInvalidResponseIfFalse(data.size() > 0, "Response does not have the expected size", handler); - - VersionEntities entities; - for (const auto& obj : data) - { - entities.push_back(std::move(VersionEntity::FromJson(obj, handler))); - } - - return entities; -} - -bool VerifyVersionResponseMatchesProduct(const ContentIdEntity& contentId, - std::string_view nameSpace, - std::string_view name) -{ - return contentId.nameSpace == nameSpace && contentId.name == name; -} - -void ValidateVersionEntity(const VersionEntity& versionEntity, - const std::string& nameSpace, - const std::string& product, - const ReportingHandler& handler) -{ - THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, - VerifyVersionResponseMatchesProduct(versionEntity.contentId, nameSpace, product), - handler, - "Response does not match the requested product"); -} - -void ValidateBatchVersionEntity(const VersionEntities& versionEntities, - const std::string& nameSpace, - const std::unordered_set& requestedProducts, - const ReportingHandler& handler) -{ - for (const auto& entity : versionEntities) - { - THROW_CODE_IF_LOG(ServiceInvalidResponse, - requestedProducts.count(entity->contentId.name) == 0, - handler, - "Received product [" + entity->contentId.name + - "] which is not one of the requested products"); - THROW_CODE_IF_LOG(ServiceInvalidResponse, - AreNotEqualI(entity->contentId.nameSpace, nameSpace), - handler, - "Received product [" + entity->contentId.name + "] with a namespace [" + - entity->contentId.nameSpace + "] that does not match the requested namespace"); - - LOG_INFO(handler, - "Received a response for product [%s] with version %s", - entity->contentId.name.c_str(), - entity->contentId.version.c_str()); - } -} - -void ValidateRequestParams(const RequestParams& requestParams, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(InvalidArg, requestParams.productRequests.empty(), handler, "productRequests cannot be empty"); - - // TODO #78: Add support for multiple product requests - THROW_CODE_IF_LOG(NotImpl, - requestParams.productRequests.size() > 1, - handler, - "There cannot be more than 1 productRequest at the moment"); - - for (const auto& [product, _] : requestParams.productRequests) - { - THROW_CODE_IF_LOG(InvalidArg, product.empty(), handler, "product must not be empty"); - } -} -} // namespace - -template -SFSClientImpl::SFSClientImpl(ClientConfig&& config) -{ - if (config.logCallbackFn) - { - m_reportingHandler.SetLoggingCallback(std::move(*config.logCallbackFn)); - } - - ValidateClientConfig(config, m_reportingHandler); - - m_accountId = std::move(config.accountId); - m_instanceId = - (config.instanceId && !config.instanceId->empty()) ? std::move(*config.instanceId) : c_defaultInstanceId; - m_nameSpace = (config.nameSpace && !config.nameSpace->empty()) ? std::move(*config.nameSpace) : c_defaultNameSpace; - - static_assert(std::is_base_of::value, - "ConnectionManagerT not derived from ConnectionManager"); - m_connectionManager = std::make_unique(m_reportingHandler); - - LogIfTestOverridesAllowed(m_reportingHandler); -} - -template -std::unique_ptr SFSClientImpl::GetLatestVersion(const ProductRequest& productRequest, - Connection& connection) const -try -{ - const auto& [product, attributes] = productRequest; - const std::string url{MakeUrlBuilder().GetLatestVersionUrl(product)}; - - LOG_INFO(m_reportingHandler, "Requesting latest version of [%s] from URL [%s]", product.c_str(), url.c_str()); - - const json body = {{"TargetingAttributes", attributes}}; - LOG_VERBOSE(m_reportingHandler, "Request body [%s]", body.dump().c_str()); - - const std::string postResponse{connection.Post(url, body.dump())}; - const json versionResponse = ParseServerMethodStringToJson(postResponse, "GetLatestVersion", m_reportingHandler); - - auto versionEntity = VersionEntity::FromJson(versionResponse, m_reportingHandler); - ValidateVersionEntity(*versionEntity, m_nameSpace, product, m_reportingHandler); - - LOG_INFO(m_reportingHandler, "Received a response with version %s", versionEntity->contentId.version.c_str()); - - return versionEntity; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -VersionEntities SFSClientImpl::GetLatestVersionBatch( - const std::vector& productRequests, - Connection& connection) const -try -{ - const std::string url{MakeUrlBuilder().GetLatestVersionBatchUrl()}; - - LOG_INFO(m_reportingHandler, "Requesting latest version of multiple products from URL [%s]", url.c_str()); - - // Creating request body - std::unordered_set requestedProducts; - json body = json::array(); - for (const auto& [product, attributes] : productRequests) - { - LOG_INFO(m_reportingHandler, "Product #%zu: [%s]", body.size() + size_t{1}, product.c_str()); - requestedProducts.insert(product); - - body.push_back({{"TargetingAttributes", attributes}, {"Product", product}}); - } - - LOG_VERBOSE(m_reportingHandler, "Request body [%s]", body.dump().c_str()); - - const std::string postResponse{connection.Post(url, body.dump())}; - - const json versionResponse = - ParseServerMethodStringToJson(postResponse, "GetLatestVersionBatch", m_reportingHandler); - - auto entities = ConvertLatestVersionBatchResponseToVersionEntities(versionResponse, m_reportingHandler); - ValidateBatchVersionEntity(entities, m_nameSpace, requestedProducts, m_reportingHandler); - - return entities; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -std::unique_ptr SFSClientImpl::GetSpecificVersion(const std::string& product, - const std::string& version, - Connection& connection) const -try -{ - const std::string url{MakeUrlBuilder().GetSpecificVersionUrl(product, version)}; - - LOG_INFO(m_reportingHandler, - "Requesting version [%s] of [%s] from URL [%s]", - version.c_str(), - product.c_str(), - url.c_str()); - - const std::string getResponse{connection.Get(url)}; - - const json versionResponse = ParseServerMethodStringToJson(getResponse, "GetSpecificVersion", m_reportingHandler); - - auto versionEntity = VersionEntity::FromJson(versionResponse, m_reportingHandler); - ValidateVersionEntity(*versionEntity, m_nameSpace, product, m_reportingHandler); - - LOG_INFO(m_reportingHandler, - "Received the expected response with version %s", - versionEntity->contentId.version.c_str()); - - return versionEntity; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -FileEntities SFSClientImpl::GetDownloadInfo(const std::string& product, - const std::string& version, - Connection& connection) const -try -{ - const std::string url{MakeUrlBuilder().GetDownloadInfoUrl(product, version)}; - - LOG_INFO(m_reportingHandler, - "Requesting download info of version [%s] of [%s] from URL [%s]", - version.c_str(), - product.c_str(), - url.c_str()); - - const std::string postResponse{connection.Post(url)}; - - const json downloadInfoResponse = - ParseServerMethodStringToJson(postResponse, "GetDownloadInfo", m_reportingHandler); - - auto files = FileEntity::DownloadInfoResponseToFileEntities(downloadInfoResponse, m_reportingHandler); - - LOG_INFO(m_reportingHandler, "Received a response with %zu files", files.size()); - - return files; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -std::vector SFSClientImpl::GetLatestDownloadInfo(const RequestParams& requestParams) const -try -{ - ValidateRequestParams(requestParams, m_reportingHandler); - - const auto connection = MakeConnection(ConnectionConfig(requestParams)); - - auto versionEntity = GetLatestVersion(requestParams.productRequests[0], *connection); - auto contentId = VersionEntity::ToContentId(std::move(*versionEntity), m_reportingHandler); - - const auto& product = requestParams.productRequests[0].product; - auto fileEntities = GetDownloadInfo(product, contentId->GetVersion(), *connection); - auto files = GenericFileEntity::FileEntitiesToFileVector(std::move(fileEntities), m_reportingHandler); - - std::unique_ptr content; - THROW_IF_FAILED_LOG(Content::Make(std::move(contentId), std::move(files), content), m_reportingHandler); - - std::vector contents; - contents.push_back(std::move(*content)); - - return contents; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -std::vector SFSClientImpl::GetLatestAppDownloadInfo( - const RequestParams& requestParams) const -try -{ - ValidateRequestParams(requestParams, m_reportingHandler); - - // TODO #150: For now apps are only coming from the "storeapps" instanceId and the service has requested - // we double check for it. In the future we should remove this check and allow the user to specify any instanceId - THROW_CODE_IF_LOG(Unexpected, - AreNotEqualI(m_instanceId, "storeapps"), - m_reportingHandler, - "At this moment only the \"storeapps\" instanceId can send app requests"); - - const auto connection = MakeConnection(ConnectionConfig(requestParams)); - - auto versionEntity = GetLatestVersion(requestParams.productRequests[0], *connection); - - auto appVersionEntity = AppVersionEntity::GetAppVersionEntityPtr(versionEntity, m_reportingHandler); - auto contentId = AppVersionEntity::ToContentId(std::move(*appVersionEntity), m_reportingHandler); - - LOG_INFO(m_reportingHandler, "Getting download info for main app content"); - const auto& product = requestParams.productRequests[0].product; - auto fileEntities = GetDownloadInfo(product, contentId->GetVersion(), *connection); - auto files = AppFileEntity::FileEntitiesToAppFileVector(std::move(fileEntities), m_reportingHandler); - - std::vector prerequisites; - for (auto& prereq : appVersionEntity->prerequisites) - { - LOG_INFO(m_reportingHandler, "Getting download info for prerequisite [%s]", prereq.contentId.name.c_str()); - auto prereqContentId = GenericVersionEntity::ToContentId(std::move(prereq), m_reportingHandler); - - auto prereqFileEntities = - GetDownloadInfo(prereqContentId->GetName(), prereqContentId->GetVersion(), *connection); - auto prereqFiles = - AppFileEntity::FileEntitiesToAppFileVector(std::move(prereqFileEntities), m_reportingHandler); - - std::unique_ptr prereqContent; - THROW_IF_FAILED_LOG( - AppPrerequisiteContent::Make(std::move(prereqContentId), std::move(prereqFiles), prereqContent), - m_reportingHandler); - - prerequisites.push_back(std::move(*prereqContent)); - } - - std::unique_ptr content; - THROW_IF_FAILED_LOG(AppContent::Make(std::move(contentId), - std::move(appVersionEntity->updateId), - std::move(prerequisites), - std::move(files), - content), - m_reportingHandler); - - std::vector contents; - contents.push_back(std::move(*content)); - - return contents; -} -SFS_CATCH_LOG_RETHROW(m_reportingHandler) - -template -std::unique_ptr SFSClientImpl::MakeConnection(const ConnectionConfig& config) const -{ - return m_connectionManager->MakeConnection(config); -} - -template -void SFSClientImpl::SetCustomBaseUrl(std::string customBaseUrl) -{ - m_customBaseUrl = std::move(customBaseUrl); -} - -template -SFSUrlBuilder SFSClientImpl::MakeUrlBuilder() const -{ - if (auto envVar = test::GetTestOverride(test::TestOverride::BaseUrl)) - { - return SFSUrlBuilder(SFSCustomUrl(*envVar), m_instanceId, m_nameSpace, m_reportingHandler); - } - if (m_customBaseUrl) - { - return SFSUrlBuilder(SFSCustomUrl(*m_customBaseUrl), m_instanceId, m_nameSpace, m_reportingHandler); - } - - return SFSUrlBuilder(m_accountId, m_instanceId, m_nameSpace, m_reportingHandler); -} - -template class SFS::details::SFSClientImpl; -template class SFS::details::SFSClientImpl; diff --git a/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.h b/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.h deleted file mode 100644 index 016c5f48e6..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSClientImpl.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "SFSClientInterface.h" - -#include "ClientConfig.h" -#include "Content.h" -#include "Logging.h" -#include "Result.h" -#include "SFSUrlBuilder.h" - -#include -#include -#include - -namespace SFS::details -{ -template -class SFSClientImpl : public SFSClientInterface -{ - public: - SFSClientImpl(ClientConfig&& config); - ~SFSClientImpl() override = default; - - // - // Combined API calls for retrieval of metadata & download URLs - // - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified products - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - */ - std::vector GetLatestDownloadInfo(const RequestParams& requestParams) const override; - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified apps - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - */ - std::vector GetLatestAppDownloadInfo(const RequestParams& requestParams) const override; - - // - // Individual APIs 1:1 with service endpoints (SFSClientInterface) - // - - /** - * @brief Gets the metadata for the latest available version for the specified product request - * @return Entity that describes the latest version of the product - * @throws SFSException if the request fails - */ - std::unique_ptr GetLatestVersion(const ProductRequest& productRequest, - Connection& connection) const override; - - /** - * @brief Gets the metadata for the latest available version for the specified product requests - * @return Vector of entities that describe the latest version of the products - * @throws SFSException if the request fails - */ - VersionEntities GetLatestVersionBatch(const std::vector& productRequests, - Connection& connection) const override; - - /** - * @brief Gets the metadata for a specific version of the specified product - * @return Entity that describes the latest version of the product - * @throws SFSException if the request fails - */ - std::unique_ptr GetSpecificVersion(const std::string& product, - const std::string& version, - Connection& connection) const override; - - /** - * @brief Gets the files metadata for a specific version of the specified product - * @return Vector of File entities for the specific version of the product - * @throws SFSException if the request fails - */ - FileEntities GetDownloadInfo(const std::string& product, - const std::string& version, - Connection& connection) const override; - - /** - * @brief Returns a new Connection to be used by the SFSClient to make requests - * @param config Configurations for the connection object - */ - std::unique_ptr MakeConnection(const ConnectionConfig& config) const override; - - // - // Configuration methods - // - - /** - * @brief Allows one to override the base URL used to make calls to the SFS service - * @details Not exposed to the user. Used for testing purposes only - * @param customBaseUrl The custom base URL to use - */ - void SetCustomBaseUrl(std::string customBaseUrl); - - /** - * @return A SFSUrlBuilder object that can be used to build URLs for the SFS service - */ - SFSUrlBuilder MakeUrlBuilder() const; - - private: - std::string m_accountId; - std::string m_instanceId; - std::string m_nameSpace; - - std::unique_ptr m_connectionManager; - - std::optional m_customBaseUrl; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/SFSClientInterface.h b/src/SfsClient/sfs-client/client/src/details/SFSClientInterface.h deleted file mode 100644 index 3381f34ac4..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSClientInterface.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Logging.h" -#include "ReportingHandler.h" -#include "RequestParams.h" -#include "entity/FileEntity.h" -#include "entity/VersionEntity.h" - -#include -#include - -namespace SFS -{ -class AppContent; -class Content; - -namespace details -{ -class Connection; -class ConnectionManager; -struct ConnectionConfig; - -class SFSClientInterface -{ - public: - virtual ~SFSClientInterface() - { - } - - // - // Combined API calls for retrieval of metadata & download URLs - // - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified products - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - */ - virtual std::vector GetLatestDownloadInfo(const RequestParams& requestParams) const = 0; - - /** - * @brief Retrieve combined metadata & download URLs from the latest version of specified apps - * @note At the moment only a single product request is supported - * @param requestParams Parameters that define this request - */ - virtual std::vector GetLatestAppDownloadInfo(const RequestParams& requestParams) const = 0; - - // - // Individual APIs 1:1 with service endpoints - // - - /** - * @brief Gets the metadata for the latest available version for the specified product request - * @return Entity that describes the latest version of the product - * @throws SFSException if the request fails - */ - virtual std::unique_ptr GetLatestVersion(const ProductRequest& productRequest, - Connection& connection) const = 0; - - /** - * @brief Gets the metadata for the latest available version for the specified product requests - * @return Vector of entities that describe the latest version of the products - * @throws SFSException if the request fails - */ - virtual VersionEntities GetLatestVersionBatch(const std::vector& productRequests, - Connection& connection) const = 0; - - /** - * @brief Gets the metadata for a specific version of the specified product - * @return Entity that describes the latest version of the product - * @throws SFSException if the request fails - */ - virtual std::unique_ptr GetSpecificVersion(const std::string& product, - const std::string& version, - Connection& connection) const = 0; - - /** - * @brief Gets the files metadata for a specific version of the specified product - * @return Vector of File entities for the specific version of the product - * @throws SFSException if the request fails - */ - virtual FileEntities GetDownloadInfo(const std::string& product, - const std::string& version, - Connection& connection) const = 0; - - /** - * @brief Returns a new Connection to be used by the SFSClient to make requests - * @param config Configurations for the connection object - */ - virtual std::unique_ptr MakeConnection(const ConnectionConfig& config) const = 0; - - const ReportingHandler& GetReportingHandler() const - { - return m_reportingHandler; - } - - protected: - ReportingHandler m_reportingHandler; -}; -} // namespace details -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/src/details/SFSException.cpp b/src/SfsClient/sfs-client/client/src/details/SFSException.cpp deleted file mode 100644 index 2f5bf1261c..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSException.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "SFSException.h" - -using namespace SFS::details; - -SFSException::SFSException(SFS::Result result) : m_result(std::move(result)) -{ -} - -SFSException::SFSException(SFS::Result::Code code) : m_result(Result(code)) -{ -} - -SFSException::SFSException(SFS::Result::Code code, std::string message) : m_result(Result(code, std::move(message))) -{ -} - -const SFS::Result& SFSException::GetResult() const noexcept -{ - return m_result; -} - -const char* SFSException::what() const noexcept -{ - return m_result.GetMsg().c_str(); -} diff --git a/src/SfsClient/sfs-client/client/src/details/SFSException.h b/src/SfsClient/sfs-client/client/src/details/SFSException.h deleted file mode 100644 index 0d281c5263..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSException.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include -#include - -namespace SFS::details -{ -class SFSException : public std::exception -{ - public: - SFSException() = default; - explicit SFSException(SFS::Result result); - - explicit SFSException(SFS::Result::Code code); - SFSException(SFS::Result::Code code, std::string message); - - const SFS::Result& GetResult() const noexcept; - const char* what() const noexcept override; - - private: - SFS::Result m_result; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.cpp b/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.cpp deleted file mode 100644 index 592adc3f07..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "SFSUrlBuilder.h" - -using namespace SFS::details; - -constexpr const char* c_apiVersion = "v2"; -constexpr const char* c_apiDomain = "api.cdp.microsoft.com"; - -SFSUrlBuilder::SFSUrlBuilder(const std::string& accountId, - std::string instanceId, - std::string nameSpace, - const ReportingHandler& handler) - : UrlBuilder(handler) - , m_instanceId(std::move(instanceId)) - , m_nameSpace(std::move(nameSpace)) -{ - SetScheme(Scheme::Https); - SetHost(accountId + "." + std::string(c_apiDomain)); -} - -SFSUrlBuilder::SFSUrlBuilder(const SFSCustomUrl& customUrl, - std::string instanceId, - std::string nameSpace, - const ReportingHandler& handler) - : UrlBuilder(handler) - , m_instanceId(std::move(instanceId)) - , m_nameSpace(std::move(nameSpace)) -{ - SetUrl(customUrl.url); -} - -std::string SFSUrlBuilder::GetLatestVersionUrl(const std::string& product) -{ - SetVersionsUrlPath(product); - AppendPath("latest"); - SetQuery("action", "select"); - return GetUrl(); -} - -std::string SFSUrlBuilder::GetLatestVersionBatchUrl() -{ - SetNamesUrlPath(); - SetQuery("action", "BatchUpdates"); - return GetUrl(); -} - -std::string SFSUrlBuilder::GetSpecificVersionUrl(const std::string& product, const std::string& version) -{ - SetVersionsUrlPath(product); - AppendPathEncoded(version); - return GetUrl(); -} - -std::string SFSUrlBuilder::GetDownloadInfoUrl(const std::string& product, const std::string& version) -{ - SetVersionsUrlPath(product); - AppendPathEncoded(version); - AppendPath("files"); - SetQuery("action", "GenerateDownloadInfo"); - return GetUrl(); -} - -SFSUrlBuilder& SFSUrlBuilder::SetNamesUrlPath() -{ - ResetPath().ResetQuery(); - AppendPath("api").AppendPath(c_apiVersion); - AppendPath("contents").AppendPathEncoded(m_instanceId); - AppendPath("namespaces").AppendPathEncoded(m_nameSpace); - AppendPath("names"); - return *this; -} - -SFSUrlBuilder& SFSUrlBuilder::SetVersionsUrlPath(const std::string& product) -{ - SetNamesUrlPath(); - AppendPathEncoded(product); - AppendPath("versions"); - return *this; -} diff --git a/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.h b/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.h deleted file mode 100644 index 0c57b55177..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/SFSUrlBuilder.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "UrlBuilder.h" - -namespace SFS::details -{ -// Wrapper struct that only exists to allow the SFSUrlBuilder to have a constructor that takes a custom URL -struct SFSCustomUrl -{ - explicit SFSCustomUrl(std::string url) : url(std::move(url)) - { - } - - std::string url; -}; - -class SFSUrlBuilder : private UrlBuilder -{ - public: - SFSUrlBuilder(const std::string& accountId, - std::string instanceId, - std::string nameSpace, - const ReportingHandler& handler); - - SFSUrlBuilder(const SFSCustomUrl& customUrl, - std::string instanceId, - std::string nameSpace, - const ReportingHandler& handler); - - std::string GetLatestVersionUrl(const std::string& product); - std::string GetLatestVersionBatchUrl(); - std::string GetSpecificVersionUrl(const std::string& product, const std::string& version); - std::string GetDownloadInfoUrl(const std::string& product, const std::string& version); - - using UrlBuilder::GetUrl; - - private: - SFSUrlBuilder& SetNamesUrlPath(); - SFSUrlBuilder& SetVersionsUrlPath(const std::string& product); - - std::string m_instanceId; - std::string m_nameSpace; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/TestOverride.cpp b/src/SfsClient/sfs-client/client/src/details/TestOverride.cpp deleted file mode 100644 index 0ada93a231..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/TestOverride.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "TestOverride.h" - -using namespace SFS; -using SFS::test::ScopedTestOverride; -using SFS::test::TestOverride; - -bool test::AreTestOverridesAllowed() -{ -#ifdef SFS_ENABLE_TEST_OVERRIDES - return true; -#else - return false; -#endif -} - -std::string test::GetEnvVarNameFromOverride(TestOverride override) -{ - switch (override) - { - case TestOverride::BaseRetryDelayMs: - return "SFS_TEST_BASE_RETRY_DELAY_MS"; - case TestOverride::BaseUrl: - return "SFS_TEST_OVERRIDE_BASE_URL"; - } - return ""; -} - -std::optional test::GetTestOverride(TestOverride override) -{ - if (!AreTestOverridesAllowed()) - { - return std::nullopt; - } - - return details::env::GetEnv(GetEnvVarNameFromOverride(override)); -} - -std::optional test::GetTestOverrideAsInt(TestOverride override) -{ - auto str = GetTestOverride(override); - if (str) - { - return std::stoi(*str); - } - return std::nullopt; -} - -ScopedTestOverride::ScopedTestOverride(TestOverride override, const std::string& value) - : m_scopedEnv(GetEnvVarNameFromOverride(override), value) -{ -} - -ScopedTestOverride::ScopedTestOverride(TestOverride override, int value) - : m_scopedEnv(GetEnvVarNameFromOverride(override), std::to_string(value)) -{ -} diff --git a/src/SfsClient/sfs-client/client/src/details/TestOverride.h b/src/SfsClient/sfs-client/client/src/details/TestOverride.h deleted file mode 100644 index 87dd76d08d..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/TestOverride.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Env.h" - -#include -#include - -namespace SFS::test -{ -/** - * @brief Check if test overrides are allowed and logs if so. - * @details Test overrides are allowed if the SFS_ENABLE_TEST_OVERRIDES macro is defined. - */ -bool AreTestOverridesAllowed(); - -enum class TestOverride -{ - BaseRetryDelayMs, // Integer. Allows one to override the base retry delay. - BaseUrl, // String. Allows one to override the base URL used for all requests -}; - -/** - * @brief Get the environment variable name for a given test override - */ -std::string GetEnvVarNameFromOverride(TestOverride override); - -/** - * @brief Get the value of a test override - * @details std::nullopt is returned if the environment variable is not set or in case of failure. - * The returned string may be different in Win32 due to the encoding of the environment variables. - */ -std::optional GetTestOverride(TestOverride override); - -/** - * @brief Get the value of a test override as int - * @details std::nullopt is returned if the environment variable is not set or in case of failure. - */ -std::optional GetTestOverrideAsInt(TestOverride override); - -class ScopedTestOverride -{ - public: - ScopedTestOverride(TestOverride override, const std::string& value); - ScopedTestOverride(TestOverride override, int value); - - private: - SFS::details::env::ScopedEnv m_scopedEnv; -}; -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/client/src/details/UrlBuilder.cpp b/src/SfsClient/sfs-client/client/src/details/UrlBuilder.cpp deleted file mode 100644 index 95d2abd8f5..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/UrlBuilder.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "UrlBuilder.h" - -#include "ErrorHandling.h" -#include "ReportingHandler.h" - -#include - -#include - -#define THROW_IF_CURL_URL_ERROR(curlCall, error) \ - do \ - { \ - auto __curlUrlCode = (curlCall); \ - THROW_CODE_IF_NOT_LOG(error, __curlUrlCode == CURLUE_OK, m_handler, GetCurlUrlStrError(__curlUrlCode)); \ - } while ((void)0, 0) - -#define THROW_IF_CURL_URL_SETUP_ERROR(curlCall) THROW_IF_CURL_URL_ERROR(curlCall, ConnectionUrlSetupFailed) - -using namespace SFS::details; - -namespace -{ -struct CurlCharDeleter -{ - void operator()(char* val) - { - if (val) - { - curl_free(val); - } - } -}; - -using CurlCharPtr = std::unique_ptr; - -std::string GetCurlUrlStrError(CURLUcode code) -{ - return "Curl URL error: " + std::string(curl_url_strerror(code)); -} -} // namespace - -UrlBuilder::UrlBuilder(const ReportingHandler& handler) : m_handler(handler) -{ - m_handle = curl_url(); - THROW_CODE_IF_NOT_LOG(ConnectionUrlSetupFailed, m_handle, m_handler, "Curl URL error: Failed to create URL"); -} - -UrlBuilder::UrlBuilder(const std::string& url, const ReportingHandler& handler) : UrlBuilder(handler) -{ - SetUrl(url); -} - -UrlBuilder::~UrlBuilder() -{ - curl_url_cleanup(m_handle); -} - -std::string UrlBuilder::GetUrl() const -{ - CurlCharPtr url; - char* urlPtr = url.get(); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_get(m_handle, CURLUPART_URL, &urlPtr, 0 /*flags*/)); - return urlPtr; -} - -std::string UrlBuilder::GetPath() const -{ - CurlCharPtr path; - char* pathPtr = path.get(); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_get(m_handle, CURLUPART_PATH, &pathPtr, 0 /*flags*/)); - return pathPtr; -} - -std::string UrlBuilder::GetQuery() const -{ - CurlCharPtr query; - char* queryPtr = query.get(); - const auto queryResult = curl_url_get(m_handle, CURLUPART_QUERY, &queryPtr, 0 /*flags*/); - switch (queryResult) - { - case CURLUE_OK: - return queryPtr; - case CURLUE_NO_QUERY: - return {}; - default: - THROW_IF_CURL_URL_SETUP_ERROR(queryResult); - } - return {}; -} - -UrlBuilder& UrlBuilder::SetScheme(Scheme scheme) -{ - switch (scheme) - { - case Scheme::Https: - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_SCHEME, "https", 0 /*flags*/)); - break; - } - return *this; -} - -UrlBuilder& UrlBuilder::SetHost(const std::string& host) -{ - THROW_CODE_IF_LOG(InvalidArg, host.empty(), m_handler, "Host must not empty"); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_HOST, host.c_str(), 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::SetPath(const std::string& path) -{ - THROW_CODE_IF_LOG(InvalidArg, path.empty(), m_handler, "Path must not empty"); - m_path = path; - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_PATH, m_path.c_str(), 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::AppendPath(const std::string& path) -{ - return AppendPath(path, false /*encode*/); -} - -UrlBuilder& UrlBuilder::AppendPathEncoded(const std::string& path) -{ - return AppendPath(path, true /*encode*/); -} - -UrlBuilder& UrlBuilder::AppendPath(const std::string& path, bool encode) -{ - THROW_CODE_IF_LOG(InvalidArg, path.empty(), m_handler, "Path must not empty"); - if (!m_path.empty() && m_path.back() != '/') - { - m_path += '/'; - } - - if (encode) - { - m_path += URLEncode(path); - } - else - { - m_path += path; - } - - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_PATH, m_path.c_str(), 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::ResetPath() -{ - m_path.clear(); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_PATH, "", 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::SetQuery(const std::string& key, const std::string& value) -{ - THROW_CODE_IF_LOG(InvalidArg, key.empty() || value.empty(), m_handler, "Query key and value must not empty"); - const std::string query = URLEncode(key) + "=" + URLEncode(value); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_QUERY, query.c_str(), 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::AppendQuery(const std::string& key, const std::string& value) -{ - THROW_CODE_IF_LOG(InvalidArg, key.empty() || value.empty(), m_handler, "Query key and value must not empty"); - const std::string query = URLEncode(key) + "=" + URLEncode(value); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_QUERY, query.c_str(), CURLU_APPENDQUERY)); - return *this; -} - -UrlBuilder& UrlBuilder::ResetQuery() -{ - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_QUERY, "", 0 /*flags*/)); - return *this; -} - -UrlBuilder& UrlBuilder::SetUrl(const std::string& url) -{ - THROW_CODE_IF_LOG(InvalidArg, url.empty(), m_handler, "Url must not empty"); - THROW_IF_CURL_URL_SETUP_ERROR(curl_url_set(m_handle, CURLUPART_URL, url.c_str(), 0 /*flags*/)); - return *this; -} - -std::string UrlBuilder::URLEncode(const std::string& str) const -{ - CurlCharPtr encodedStr{curl_easy_escape(nullptr /*ignored*/, str.c_str(), static_cast(str.length()))}; - THROW_CODE_IF_NOT_LOG(ConnectionUrlSetupFailed, encodedStr, m_handler, "Failed to URL-encode string"); - return encodedStr.get(); -} diff --git a/src/SfsClient/sfs-client/client/src/details/UrlBuilder.h b/src/SfsClient/sfs-client/client/src/details/UrlBuilder.h deleted file mode 100644 index cf738dab1e..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/UrlBuilder.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -// Forward declaration -struct Curl_URL; -typedef struct Curl_URL CURLU; - -namespace SFS::details -{ -class ReportingHandler; - -enum class Scheme -{ - Https, -}; - -class UrlBuilder -{ - public: - /** - * @brief Construct a new Url Builder object with an empty URL - */ - explicit UrlBuilder(const ReportingHandler& handler); - - /** - * @brief Construct a new Url Builder object with an existing URL - * @param url The URL to set - */ - UrlBuilder(const std::string& url, const ReportingHandler& handler); - - ~UrlBuilder(); - - UrlBuilder(const UrlBuilder&) = delete; - UrlBuilder& operator=(const UrlBuilder&) = delete; - - std::string GetUrl() const; - - std::string GetPath() const; - std::string GetQuery() const; - - /** - * @brief Set the scheme for the URL - * @param scheme The scheme to set for the URL Ex: Https - * @return The reference to the current object - */ - UrlBuilder& SetScheme(Scheme scheme); - - /** - * @brief Set the host for the URL - * @param host The host to set for the URL. Ex: www.example.com - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& SetHost(const std::string& host); - - /** - * @brief Set a path to the URL - * @param path The path to set for the URL. Ex: index.html - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& SetPath(const std::string& path); - - /** - * @brief Append a path to the URL - * @param path The path to be appended for the URL. Ex: index.html - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& AppendPath(const std::string& path); - - /** - * @brief Append a path to the URL. The new path element will be URL encoded, including forward slashes - * @param path The path to be appended for the URL. Ex: index.html - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& AppendPathEncoded(const std::string& path); - - /** - * @brief Reset the path of the URL - * @return The reference to the current object - */ - UrlBuilder& ResetPath(); - - /** - * @brief Set a query to the URL (?key=value) - * @param key The key of the query string. Ex: value - * @param value The value of the query string. Ex: value - * @note Both key and value will be URL encoded - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& SetQuery(const std::string& key, const std::string& value); - - /** - * @brief Append a query to the URL (&key=value) - * @param key The key of the query string. Ex: value - * @param value The value of the query string. Ex: value - * @note Both key and value will be URL encoded - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& AppendQuery(const std::string& key, const std::string& value); - - /** - * @brief Reset the query of the URL - * @return The reference to the current object - */ - UrlBuilder& ResetQuery(); - - /** - * @brief Set the URL through a string. Other methods can still be called later to modify the URl - * @param url The string to set as URL. Ex: http://www.example.com/index.html - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& SetUrl(const std::string& url); - - private: - /** - * @brief Append a path to the URL - * @param path The path to be appended for the URL. Ex: index.html - * @param encode If true, the new path element will be URL encoded, including forward slashes - * @throws SFSException if the string is invalid - * @return The reference to the current object - */ - UrlBuilder& AppendPath(const std::string& path, bool encode); - - /** - * @brief URL-encode (or percent-encode) a given string - */ - std::string URLEncode(const std::string& str) const; - - const ReportingHandler& m_handler; - - CURLU* m_handle = nullptr; - std::string m_path; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/Util.cpp b/src/SfsClient/sfs-client/client/src/details/Util.cpp deleted file mode 100644 index 8912505544..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/Util.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Util.h" - -#include -#include - -using namespace SFS::details; - -bool util::AreEqualI(std::string_view a, std::string_view b) -{ - return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { - return std::tolower(static_cast(a)) == std::tolower(static_cast(b)); - }); -} - -bool util::AreNotEqualI(std::string_view a, std::string_view b) -{ - return !AreEqualI(a, b); -} diff --git a/src/SfsClient/sfs-client/client/src/details/Util.h b/src/SfsClient/sfs-client/client/src/details/Util.h deleted file mode 100644 index 8e7545ba09..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/Util.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace SFS::details::util -{ -bool AreEqualI(std::string_view a, std::string_view b); -bool AreNotEqualI(std::string_view a, std::string_view b); -} // namespace SFS::details::util diff --git a/src/SfsClient/sfs-client/client/src/details/connection/Connection.cpp b/src/SfsClient/sfs-client/client/src/details/connection/Connection.cpp deleted file mode 100644 index 6669fa11b9..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/Connection.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Connection.h" - -using namespace SFS; -using namespace SFS::details; - -Connection::Connection(const ConnectionConfig& config, const ReportingHandler& handler) : m_handler(handler) -{ - if (config.baseCV) - { - m_cv = std::move(CorrelationVector(*config.baseCV, m_handler)); - } - m_maxRetries = config.maxRetries; -} - -std::string Connection::Post(const std::string& url) -{ - return Post(url, {}); -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/Connection.h b/src/SfsClient/sfs-client/client/src/details/connection/Connection.h deleted file mode 100644 index 0fc1589345..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/Connection.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "../CorrelationVector.h" -#include "ConnectionConfig.h" - -#include - -namespace SFS::details -{ -class ReportingHandler; - -class Connection -{ - public: - Connection(const ConnectionConfig& config, const ReportingHandler& handler); - - virtual ~Connection() - { - } - - Connection(const Connection&) = delete; - Connection& operator=(const Connection&) = delete; - - /** - * @brief Perform a GET request to the given @param url - * @return The response body - * @throws SFSException if the request fails - */ - virtual std::string Get(const std::string& url) = 0; - - /** - * @brief Perform a POST request to the given @param url with @param data as the request body - * @return The response body - * @throws SFSException if the request fails - */ - virtual std::string Post(const std::string& url, const std::string& data) = 0; - - /** - * @brief Perform a POST request to the given @param url - * @return The response body - * @throws SFSException if the request fails - */ - std::string Post(const std::string& url); - - protected: - const ReportingHandler& m_handler; - - /// @brief The correlation vector to use for requests - CorrelationVector m_cv; - - /// @brief Expected number of retries for a web request after a failed attempt - unsigned m_maxRetries{3}; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.cpp b/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.cpp deleted file mode 100644 index 319d5a132c..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ConnectionConfig.h" - -#include "RequestParams.h" - -using namespace SFS; -using namespace SFS::details; - -ConnectionConfig::ConnectionConfig(const SFS::RequestParams& requestParams) - : maxRetries(requestParams.retryOnError ? c_maxRetries : 0) - , baseCV(requestParams.baseCV) - , proxy(requestParams.proxy) -{ -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.h b/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.h deleted file mode 100644 index 30d963ae9f..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionConfig.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include - -namespace SFS -{ -struct RequestParams; - -namespace details -{ -struct ConnectionConfig -{ - ConnectionConfig() = default; - explicit ConnectionConfig(const RequestParams& requestParams); - - /// @brief Expected number of retries for a web request after a failed attempt - unsigned maxRetries{3}; - - /// @brief The correlation vector to use for requests - std::optional baseCV; - - /// @brief Proxy setting which can be used to establish connections with the server - std::optional proxy; -}; -} // namespace details -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.cpp b/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.cpp deleted file mode 100644 index 1c711a9d83..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ConnectionManager.h" - -#include "../ReportingHandler.h" - -using namespace SFS::details; - -ConnectionManager::ConnectionManager(const ReportingHandler& handler) : m_handler(handler) -{ -} - -ConnectionManager::~ConnectionManager() -{ -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.h b/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.h deleted file mode 100644 index 82d32d9b55..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/ConnectionManager.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace SFS::details -{ -class Connection; -class ReportingHandler; -struct ConnectionConfig; - -class ConnectionManager -{ - public: - ConnectionManager(const ReportingHandler& handler); - virtual ~ConnectionManager(); - - ConnectionManager(const ConnectionManager&) = delete; - ConnectionManager& operator=(const ConnectionManager&) = delete; - - virtual std::unique_ptr MakeConnection(const ConnectionConfig& config) = 0; - - protected: - const ReportingHandler& m_handler; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.cpp b/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.cpp deleted file mode 100644 index c7c0683141..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.cpp +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "CurlConnection.h" - -#include "../ErrorHandling.h" -#include "../ReportingHandler.h" -#include "../TestOverride.h" -#include "HttpHeader.h" - -#include - -#include -#include -#include -#include - -#define THROW_IF_CURL_ERROR(curlCall, error) \ - do \ - { \ - auto __curlCode = (curlCall); \ - std::string __message = "Curl error: " + std::string(curl_easy_strerror(__curlCode)); \ - THROW_CODE_IF_NOT_LOG(error, __curlCode == CURLE_OK, m_handler, std::move(__message)); \ - } while ((void)0, 0) - -#define THROW_IF_CURL_SETUP_ERROR(curlCall) THROW_IF_CURL_ERROR(curlCall, ConnectionSetupFailed) -#define THROW_IF_CURL_UNEXPECTED_ERROR(curlCall) THROW_IF_CURL_ERROR(curlCall, ConnectionUnexpectedError) - -// Setting a hard limit of 100k characters for the response to avoid rogue servers sending huge amounts of data -#define MAX_RESPONSE_CHARACTERS 100000 - -using namespace SFS; -using namespace SFS::details; -using namespace std::chrono_literals; - -namespace -{ -// Curl callback for writing data to a std::string. Must return the number of bytes written. -// This callback may be called multiple times for a single request, and will keep appending -// to userData until the request is complete. The data received is not null-terminated. -// For SFS, this data will likely be a JSON string. -size_t WriteCallback(char* contents, size_t sizeInBytes, size_t numElements, void* userData) -{ - auto readBufferPtr = static_cast(userData); - if (readBufferPtr) - { - size_t totalSize = sizeInBytes * numElements; - - // Checking final response size to avoid unexpected amounts of data - if ((readBufferPtr->length() + totalSize) > MAX_RESPONSE_CHARACTERS) - { - return CURL_WRITEFUNC_ERROR; - } - - readBufferPtr->append(contents, totalSize); - return totalSize; - } - return CURL_WRITEFUNC_ERROR; -} - -struct CurlErrorBuffer -{ - public: - CurlErrorBuffer(CURL* handle, const ReportingHandler& reportingHandler) - : m_handle(handle) - , m_reportingHandler(reportingHandler) - { - m_errorBuffer[0] = '\0'; - SetBuffer(); - } - - ~CurlErrorBuffer() - { - LOG_IF_FAILED(UnsetBuffer(), m_reportingHandler); - } - - void SetBuffer() - { - THROW_CODE_IF_NOT_LOG(ConnectionSetupFailed, - curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_errorBuffer) == CURLE_OK, - m_reportingHandler, - "Failed to set up error buffer for curl"); - } - - Result UnsetBuffer() - { - return curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, nullptr) == CURLE_OK - ? Result::Success - : Result(Result::ConnectionSetupFailed, "Failed to unset curl error buffer"); - } - - char* Get() - { - return m_errorBuffer; - } - - private: - CURL* m_handle; - const ReportingHandler& m_reportingHandler; - - char m_errorBuffer[CURL_ERROR_SIZE]; -}; - -Result CurlCodeToResult(CURLcode curlCode, char* errorBuffer) -{ - Result::Code code; - switch (curlCode) - { - case CURLE_OPERATION_TIMEDOUT: - code = Result::HttpTimeout; - break; - default: - code = Result::ConnectionUnexpectedError; - break; - } - - const bool isErrorStringRegistered = errorBuffer && errorBuffer[0] != '\0'; - std::string message = isErrorStringRegistered ? errorBuffer : "Curl error"; - - return Result(code, std::move(message)); -} - -bool IsSuccessfulSFSHttpCode(long httpCode) -{ - return httpCode == 200; -} - -Result HttpCodeToResult(long httpCode) -{ - if (IsSuccessfulSFSHttpCode(httpCode)) - { - return Result::Success; - } - - switch (httpCode) - { - case 400: - { - return Result(Result::HttpBadRequest, "400 Bad Request"); - } - case 404: - { - return Result(Result::HttpNotFound, "404 Not Found"); - } - case 405: - { - return Result(Result::HttpMethodNotAllowed, "405 Method Not Allowed"); - } - case 429: - { - return Result(Result::HttpTooManyRequests, "429 Too Many Requests"); - } - case 503: - { - return Result(Result::HttpServiceNotAvailable, "503 Service Unavailable"); - } - default: - { - return Result(Result::HttpUnexpected, "Unexpected HTTP code " + std::to_string(httpCode)); - } - } -} - -bool IsRetriableHttpError(long httpCode) -{ - switch (httpCode) - { - case 429: // Too Many Requests - Rate Limiting - case 500: // InternalServerError - Can be triggered within server timeouts, network issue - case 502: // BadGateway - Likely an issue with routing - case 503: // ServerBusy - case 504: // GatewayTimeout - return true; - default: - return false; - } -} - -std::optional GetResponseHeader(CURL* handle, - HttpHeader httpHeader, - const ReportingHandler& reportingHandler) -{ - const std::string headerName = ToString(httpHeader); - - // This struct only represents data inside the CURL handle, and must not be manually freed - curl_header* header; - const int lastRequest = -1; - CURLHcode curlhCode = curl_easy_header(handle, headerName.c_str(), 0 /*index*/, CURLH_HEADER, lastRequest, &header); - switch (curlhCode) - { - case CURLHE_OK: - return header->value; - case CURLHE_BADINDEX: - case CURLHE_MISSING: - case CURLHE_NOHEADERS: - return std::nullopt; - default: - THROW_LOG( - Result(Result::ConnectionUnexpectedError, - "Failed to get response header " + headerName + " with CURLH code " + std::to_string(curlhCode)), - reportingHandler); - } - return std::nullopt; -} - -std::chrono::milliseconds ParseRetryAfterValue(const std::string& retryAfter, const ReportingHandler& reportingHandler) -{ - LOG_VERBOSE(reportingHandler, "Parsing Retry-After value [%s]", retryAfter.c_str()); - std::chrono::seconds retryAfterSec{0}; - try - { - retryAfterSec = std::chrono::seconds(std::stoi(retryAfter)); - } - catch (std::invalid_argument&) - { - // Value is not an integer, but may still be in HTTP Date format - const time_t retryAfterSecSinceEpoch = curl_getdate(retryAfter.c_str(), nullptr /*unused*/); - if (retryAfterSecSinceEpoch == -1) - { - THROW_LOG(Result(Result::ConnectionUnexpectedError, - "Retry-After header value could not be converted to an integer or an HTTP Date"), - reportingHandler); - } - - // Get number of seconds since epoch for now to calculate the difference - const auto epoch = std::chrono::system_clock::now().time_since_epoch(); - const auto nowSecSinceEpoch = std::chrono::duration_cast(epoch); - - retryAfterSec = std::chrono::seconds(retryAfterSecSinceEpoch) - nowSecSinceEpoch; - } - catch (std::out_of_range&) - { - THROW_LOG(Result(Result::ConnectionUnexpectedError, "Retry-After header value is not in the expected range"), - reportingHandler); - } - if (retryAfterSec <= 0s) - { - THROW_LOG(Result(Result::ConnectionUnexpectedError, "Invalid Retry-After header value"), reportingHandler); - } - return retryAfterSec; -} -} // namespace - -namespace SFS::details -{ -struct CurlHeaderList -{ - public: - CurlHeaderList() = default; - - ~CurlHeaderList() - { - curl_slist_free_all(m_slist); - } - - /** - * @throws SFSException if the header cannot be added to the list. - */ - void Add(HttpHeader header, const std::string& value) - { - const std::string data = ToString(header) + ": " + value; - const auto ret = curl_slist_append(m_slist, data.c_str()); - if (!ret) - { - throw SFSException(Result::ConnectionSetupFailed, "Failed to add header " + data + " to CurlHeaderList"); - } - m_slist = ret; - } - - struct curl_slist* m_slist{nullptr}; -}; -} // namespace SFS::details - -CurlConnection::CurlConnection(const ConnectionConfig& config, const ReportingHandler& handler) - : Connection(config, handler) -{ - m_handle = curl_easy_init(); - THROW_CODE_IF_NOT_LOG(ConnectionSetupFailed, m_handle, m_handler, "Failed to init curl connection"); - - // Turning timeout signals off to avoid issues with threads - // See https://curl.se/libcurl/c/threadsafe.html - THROW_CODE_IF_NOT_LOG(ConnectionSetupFailed, - curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL, 1L) == CURLE_OK, - m_handler, - "Failed to set up curl"); - - if (config.proxy) - { - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_PROXY, config.proxy->c_str())); - } - - // TODO #41: Pass AAD token in the header if it is available - // TODO #42: Cert pinning with service -} - -CurlConnection::~CurlConnection() -{ - if (m_handle) - { - curl_easy_cleanup(m_handle); - } -} - -std::string CurlConnection::Get(const std::string& url) -{ - THROW_CODE_IF_LOG(InvalidArg, url.empty(), m_handler, "url cannot be empty"); - - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_HTTPGET, 1L)); - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, nullptr)); - - CurlHeaderList headers; - return CurlPerform(url, headers); -} - -std::string CurlConnection::Post(const std::string& url, const std::string& data) -{ - THROW_CODE_IF_LOG(InvalidArg, url.empty(), m_handler, "url cannot be empty"); - - CurlHeaderList headerList; - headerList.Add(HttpHeader::ContentType, "application/json"); - - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_POST, 1L)); - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_COPYPOSTFIELDS, data.c_str())); - - CurlHeaderList headers; - headers.Add(HttpHeader::ContentType, "application/json"); - return CurlPerform(url, headers); -} - -std::string CurlConnection::CurlPerform(const std::string& url, CurlHeaderList& headers) -{ - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_URL, url.c_str())); - - const std::string cv = m_cv.IncrementAndGet(); - headers.Add(HttpHeader::MSCV, cv); - headers.Add(HttpHeader::UserAgent, GetUserAgentValue()); - - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_HTTPHEADER, headers.m_slist)); - - // Setting up error buffer where error messages get written - this gets unset in the destructor - CurlErrorBuffer errorBuffer(m_handle, m_handler); - - std::string readBuffer; - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, WriteCallback)); - THROW_IF_CURL_SETUP_ERROR(curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, &readBuffer)); - - // Retry the connection a specified number of times - const unsigned totalAttempts = 1 + m_maxRetries; - for (unsigned i = 0; i < totalAttempts; i++) - { - const unsigned attempt = i + 1; - LOG_INFO(m_handler, "Request attempt %u out of %u (cv: %s)", attempt, totalAttempts, cv.c_str()); - const bool lastAttempt = attempt == totalAttempts; - - // Clear the buffer before each attempt - readBuffer.clear(); - - // Perform the request - const auto result = curl_easy_perform(m_handle); - if (result != CURLE_OK) - { - THROW_LOG(CurlCodeToResult(result, errorBuffer.Get()), m_handler); - } - - // Check request status to stop or retry - long httpCode = 0; - THROW_IF_CURL_UNEXPECTED_ERROR(curl_easy_getinfo(m_handle, CURLINFO_RESPONSE_CODE, &httpCode)); - - if (IsSuccessfulSFSHttpCode(httpCode)) - { - break; - } - - const Result httpResult = HttpCodeToResult(httpCode); - if (!CanRetryRequest(lastAttempt, httpCode)) - { - THROW_LOG(httpResult, m_handler); - } - - ProcessRetry(attempt, httpResult); - } - - return readBuffer; -} - -bool CurlConnection::CanRetryRequest(bool lastAttempt, long httpCode) -{ - if (lastAttempt) - { - LOG_INFO(m_handler, "No retry as this is the last attempt"); - return false; - } - - if (!IsRetriableHttpError(httpCode)) - { - LOG_INFO(m_handler, "Error %ld is not retriable, stopping", httpCode); - return false; - } - - return true; -} - -void CurlConnection::ProcessRetry(int attempt, const Result& httpResult) -{ - // Wait before retrying. Prefer the Retry-After information if available - std::chrono::milliseconds retryDelay{0}; - const std::optional retryAfter = GetResponseHeader(m_handle, HttpHeader::RetryAfter, m_handler); - if (retryAfter) - { - // TODO #93: Enforce Retry-After value across calls to avoid caller spamming server - retryDelay = ParseRetryAfterValue(*retryAfter, m_handler); - } - else - { - // Apply exponential back-off with a factor of 2 - static std::chrono::milliseconds s_baseRetryDelay = 15s; // Value recommended as interval by the service - - std::chrono::milliseconds baseRetryDelay = s_baseRetryDelay; - - // Value can be overriden in tests - if (auto override = test::GetTestOverrideAsInt(test::TestOverride::BaseRetryDelayMs)) - { - baseRetryDelay = std::chrono::milliseconds{*override}; - } - - retryDelay = baseRetryDelay * (1 << (attempt - 1)); - } - - LOG_IF_FAILED(httpResult, m_handler); - LOG_INFO(m_handler, "Sleeping for %lld ms", static_cast(retryDelay.count())); - std::this_thread::sleep_for(retryDelay); -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.h b/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.h deleted file mode 100644 index 618ea2d6b9..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnection.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Connection.h" - -#include - -// Forward declaration -typedef void CURL; - -namespace SFS -{ -class Result; - -namespace details -{ -struct CurlHeaderList; -class ReportingHandler; - -class CurlConnection : public Connection -{ - public: - CurlConnection(const ConnectionConfig& config, const ReportingHandler& handler); - ~CurlConnection() override; - - /** - * @brief Perform a GET request to the given @param url - * @return The response body - * @throws SFSException if the request fails - */ - std::string Get(const std::string& url) override; - - /** - * @brief Perform a POST request to the given @param url with @param data as the request body - * @return The response body - * @throws SFSException if the request fails - */ - std::string Post(const std::string& url, const std::string& data) override; - - private: - /** - * @brief Perform checks that the request can be retried - */ - bool CanRetryRequest(bool lastAttempt, long httpCode); - - /** - * @brief Process retry and perform wait logic before retrying the request - */ - void ProcessRetry(int attempt, const Result& httpResult); - - protected: - /** - * @brief Perform a REST request to the given @param url with the given @param headers - * @return The response body - * @throws SFSException if the request fails - */ - virtual std::string CurlPerform(const std::string& url, CurlHeaderList& headers); - - CURL* m_handle; -}; -} // namespace details -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.cpp b/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.cpp deleted file mode 100644 index 9623387aa7..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "CurlConnectionManager.h" - -#include "../ErrorHandling.h" -#include "CurlConnection.h" - -#include - -using namespace SFS; -using namespace SFS::details; - -namespace -{ -// Curl recommends checking for expected features in runtime -void CheckCurlFeatures(const ReportingHandler& handler) -{ - curl_version_info_data* ver = curl_version_info(CURLVERSION_NOW); - THROW_CODE_IF_NOT_LOG(HttpUnexpected, ver, handler); - - THROW_CODE_IF_NOT_LOG(HttpUnexpected, (ver->features & CURL_VERSION_SSL), handler, "Curl was not built with SSL"); - THROW_CODE_IF_NOT_LOG(HttpUnexpected, - (ver->features & CURL_VERSION_THREADSAFE), - handler, - "Curl is not thread safe"); - - // For thread safety we need the DNS resolutions to be asynchronous (which happens because of c-ares) - THROW_CODE_IF_NOT_LOG(HttpUnexpected, - (ver->features & CURL_VERSION_ASYNCHDNS), - handler, - "Curl was not built with async DNS resolutions"); -} -} // namespace - -CurlConnectionManager::CurlConnectionManager(const ReportingHandler& handler) : ConnectionManager(handler) -{ - THROW_CODE_IF_NOT_LOG(HttpUnexpected, - curl_global_init(CURL_GLOBAL_ALL) == CURLE_OK, - m_handler, - "Curl failed to initialize"); - CheckCurlFeatures(m_handler); -} - -CurlConnectionManager::~CurlConnectionManager() -{ - curl_global_cleanup(); -} - -std::unique_ptr CurlConnectionManager::MakeConnection(const ConnectionConfig& config) -{ - return std::make_unique(config, m_handler); -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.h b/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.h deleted file mode 100644 index 98e0a5216b..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/CurlConnectionManager.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "ConnectionManager.h" - -#include - -namespace SFS::details -{ -class Connection; -class ReportingHandler; -struct ConnectionConfig; - -class CurlConnectionManager : public ConnectionManager -{ - public: - CurlConnectionManager(const ReportingHandler& handler); - ~CurlConnectionManager() override; - - std::unique_ptr MakeConnection(const ConnectionConfig& config) override; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.cpp b/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.cpp deleted file mode 100644 index e09cd22573..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "HttpHeader.h" - -#include "../OSInfo.h" - -#include - -using namespace std::string_literals; - -constexpr const char* c_userAgent = "Microsoft-SFSClient/" SFS_VERSION; - -std::string SFS::details::ToString(HttpHeader header) -{ - switch (header) - { - case HttpHeader::ContentType: - return "Content-Type"; - case HttpHeader::MSCV: - return microsoft::correlation_vector::HEADER_NAME; - case HttpHeader::RetryAfter: - return "Retry-After"; - case HttpHeader::UserAgent: - return "User-Agent"; - } - - return ""; -} - -std::string SFS::details::GetUserAgentValue() -{ - // Examples: - // - Microsoft-SFSClient/1.0.0 (Windows; x64) - // - Microsoft-SFSClient/1.0.0 (Linux; x86_64) - - return c_userAgent + " ("s + osinfo::GetPlatform() + "; "s + osinfo::GetOSMachineInfo() + ")"s; -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.h b/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.h deleted file mode 100644 index 9a7df689b6..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/HttpHeader.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace SFS::details -{ -enum class HttpHeader -{ - ContentType, - MSCV, - RetryAfter, - UserAgent, -}; - -std::string ToString(HttpHeader header); - -std::string GetUserAgentValue(); -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.cpp b/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.cpp deleted file mode 100644 index 217a44850b..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "MockConnection.h" - -using namespace SFS; -using namespace SFS::details; - -MockConnection::MockConnection(const ConnectionConfig& config, const ReportingHandler& handler) - : Connection(config, handler) -{ -} - -MockConnection::~MockConnection() -{ -} - -std::string MockConnection::Get(const std::string&) -{ - return {}; -} - -std::string MockConnection::Post(const std::string&, const std::string&) -{ - return {}; -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.h b/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.h deleted file mode 100644 index 81eb86b929..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnection.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "../Connection.h" - -#include - -namespace SFS::details -{ -class ReportingHandler; - -class MockConnection : public Connection -{ - public: - MockConnection(const ConnectionConfig& config, const ReportingHandler& handler); - ~MockConnection() override; - - std::string Get(const std::string& url) override; - std::string Post(const std::string& url, const std::string& data) override; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.cpp b/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.cpp deleted file mode 100644 index 1d879ecf03..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "MockConnectionManager.h" - -#include "MockConnection.h" - -using namespace SFS::details; - -MockConnectionManager::MockConnectionManager(const ReportingHandler& handler) : ConnectionManager(handler) -{ -} - -MockConnectionManager::~MockConnectionManager() -{ -} - -std::unique_ptr MockConnectionManager::MakeConnection(const ConnectionConfig& config) -{ - return std::make_unique(config, m_handler); -} diff --git a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.h b/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.h deleted file mode 100644 index 9a8965bdd1..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/connection/mock/MockConnectionManager.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "../ConnectionManager.h" - -#include - -namespace SFS::details -{ -class Connection; -class ReportingHandler; -struct ConnectionConfig; - -class MockConnectionManager : public ConnectionManager -{ - public: - MockConnectionManager(const ReportingHandler& handler); - ~MockConnectionManager() override; - - std::unique_ptr MakeConnection(const ConnectionConfig& config) override; -}; -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/entity/ContentType.cpp b/src/SfsClient/sfs-client/client/src/details/entity/ContentType.cpp deleted file mode 100644 index ce5680f166..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/ContentType.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentType.h" - -using namespace SFS::details; - -std::string SFS::details::ToString(ContentType type) -{ - switch (type) - { - case ContentType::Generic: - return "Generic"; - case ContentType::App: - return "App"; - default: - return "Unknown"; - } -} diff --git a/src/SfsClient/sfs-client/client/src/details/entity/ContentType.h b/src/SfsClient/sfs-client/client/src/details/entity/ContentType.h deleted file mode 100644 index 2b9ccc4aa5..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/ContentType.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace SFS::details -{ -enum class ContentType -{ - Generic, - App, -}; - -std::string ToString(ContentType type); -} // namespace SFS::details diff --git a/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.cpp b/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.cpp deleted file mode 100644 index 53e8f114e7..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "FileEntity.h" - -#include "../ErrorHandling.h" -#include "../ReportingHandler.h" -#include "../Util.h" -#include "AppFile.h" -#include "File.h" - -#include - -#define THROW_INVALID_RESPONSE_IF_NOT(condition, message, handler) \ - THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, condition, handler, message) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::util; -using json = nlohmann::json; - -namespace -{ -HashType HashTypeFromString(const std::string& hashType, const ReportingHandler& handler) -{ - if (AreEqualI(hashType, "Sha1")) - { - return HashType::Sha1; - } - else if (AreEqualI(hashType, "Sha256")) - { - return HashType::Sha256; - } - else - { - THROW_LOG(Result(Result::Unexpected, "Unknown hash type: " + hashType), handler); - return HashType::Sha1; // Unreachable code, but the compiler doesn't know that. - } -} - -Architecture ArchitectureFromString(const std::string& arch, const ReportingHandler& handler) -{ - if (AreEqualI(arch, "None")) - { - return Architecture::None; - } - else if (AreEqualI(arch, "x86")) - { - return Architecture::x86; - } - else if (AreEqualI(arch, "amd64")) - { - return Architecture::Amd64; - } - else if (AreEqualI(arch, "arm")) - { - return Architecture::Arm; - } - else if (AreEqualI(arch, "arm64")) - { - return Architecture::Arm64; - } - else - { - THROW_LOG(Result(Result::Unexpected, "Unknown architecture: " + arch), handler); - return Architecture::None; // Unreachable code, but the compiler doesn't know that. - } -} - -void ValidateContentType(const FileEntity& entity, ContentType expectedType, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(Result::ServiceUnexpectedContentType, - entity.GetContentType() != expectedType, - handler, - "The service returned file \"" + entity.fileId + "\" with content type [" + - ToString(entity.GetContentType()) + "] while the expected type was [" + - ToString(expectedType) + "]"); -} -} // namespace - -std::unique_ptr FileEntity::FromJson(const nlohmann::json& file, const ReportingHandler& handler) -{ - // Expected format for a generic file entity: - // { - // "FileId": , - // "Url": , - // "SizeInBytes": , - // "Hashes": { - // "Sha1": , - // "Sha256": - // }, - // "DeliveryOptimization": {} // ignored, not used by the client. - // } - // - // Expected extra elements for an app version entity: - // { - // ... - // "ApplicabilityDetails": { - // "Architectures": [ - // "" - // ], - // "PlatformApplicabilityForPackage": [ - // "" - // ] - // }, - // "FileMoniker": "", - // } - - THROW_INVALID_RESPONSE_IF_NOT(file.is_object(), "File is not a JSON object", handler); - - std::unique_ptr tmp; - const bool isAppEntity = file.contains("FileMoniker"); - if (isAppEntity) - { - tmp = std::make_unique(); - } - else - { - tmp = std::make_unique(); - } - - THROW_INVALID_RESPONSE_IF_NOT(file.contains("FileId"), "Missing File.FileId in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(file["FileId"].is_string(), "File.FileId is not a string", handler); - tmp->fileId = file["FileId"]; - - THROW_INVALID_RESPONSE_IF_NOT(file.contains("Url"), "Missing File.Url in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(file["Url"].is_string(), "File.Url is not a string", handler); - tmp->url = file["Url"]; - - THROW_INVALID_RESPONSE_IF_NOT(file.contains("SizeInBytes"), "Missing File.SizeInBytes in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(file["SizeInBytes"].is_number_unsigned(), - "File.SizeInBytes is not an unsigned number", - handler); - tmp->sizeInBytes = file["SizeInBytes"]; - - THROW_INVALID_RESPONSE_IF_NOT(file.contains("Hashes"), "Missing File.Hashes in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(file["Hashes"].is_object(), "File.Hashes is not an object", handler); - - for (const auto& [hashType, hashValue] : file["Hashes"].items()) - { - THROW_INVALID_RESPONSE_IF_NOT(hashValue.is_string(), "File.Hashes object value is not a string", handler); - tmp->hashes[hashType] = hashValue; - } - - if (isAppEntity) - { - auto appEntity = dynamic_cast(tmp.get()); - - THROW_INVALID_RESPONSE_IF_NOT(file["FileMoniker"].is_string(), "File.FileMoniker is not a string", handler); - appEntity->fileMoniker = file["FileMoniker"]; - - THROW_INVALID_RESPONSE_IF_NOT(file.contains("ApplicabilityDetails"), - "Missing File.ApplicabilityDetails in response", - handler); - - const auto& details = file["ApplicabilityDetails"]; - THROW_INVALID_RESPONSE_IF_NOT(details.is_object(), "File.ApplicabilityDetails is not an object", handler); - - THROW_INVALID_RESPONSE_IF_NOT(details.contains("Architectures"), - "Missing File.ApplicabilityDetails.Architectures in response", - handler); - THROW_INVALID_RESPONSE_IF_NOT(details["Architectures"].is_array(), - "File.ApplicabilityDetails.Architectures is not an array", - handler); - for (const auto& arch : details["Architectures"]) - { - THROW_INVALID_RESPONSE_IF_NOT(arch.is_string(), - "File.ApplicabilityDetails.Architectures array value is not a string", - handler); - } - appEntity->applicabilityDetails.architectures = details["Architectures"]; - - THROW_INVALID_RESPONSE_IF_NOT(details.contains("PlatformApplicabilityForPackage"), - "Missing File.ApplicabilityDetails.PlatformApplicabilityForPackage in response", - handler); - THROW_INVALID_RESPONSE_IF_NOT(details["PlatformApplicabilityForPackage"].is_array(), - "File.ApplicabilityDetails.PlatformApplicabilityForPackage is not an array", - handler); - for (const auto& app : details["PlatformApplicabilityForPackage"]) - { - THROW_INVALID_RESPONSE_IF_NOT( - app.is_string(), - "File.ApplicabilityDetails.PlatformApplicabilityForPackage array value is not a string", - handler); - } - appEntity->applicabilityDetails.platformApplicabilityForPackage = details["PlatformApplicabilityForPackage"]; - } - - return tmp; -} - -FileEntities FileEntity::DownloadInfoResponseToFileEntities(const nlohmann::json& data, const ReportingHandler& handler) -{ - // Expected format is an array of FileEntity - THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, data.is_array(), handler, "Response is not a JSON array"); - - FileEntities tmp; - for (const auto& fileData : data) - { - THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, - fileData.is_object(), - handler, - "Array element is not a JSON object"); - tmp.push_back(std::move(FileEntity::FromJson(fileData, handler))); - } - - return tmp; -} - -ContentType GenericFileEntity::GetContentType() const -{ - return ContentType::Generic; -} - -std::unique_ptr GenericFileEntity::ToFile(FileEntity&& entity, const ReportingHandler& handler) -{ - ValidateContentType(entity, ContentType::Generic, handler); - - std::unordered_map hashes; - for (auto& [hashType, hashValue] : entity.hashes) - { - hashes[HashTypeFromString(hashType, handler)] = std::move(hashValue); - } - - std::unique_ptr tmp; - THROW_IF_FAILED_LOG( - File::Make(std::move(entity.fileId), std::move(entity.url), entity.sizeInBytes, std::move(hashes), tmp), - handler); - return tmp; -} - -std::vector GenericFileEntity::FileEntitiesToFileVector(FileEntities&& entities, const ReportingHandler& handler) -{ - std::vector tmp; - for (auto& entity : entities) - { - tmp.push_back(std::move(*GenericFileEntity::ToFile(std::move(*entity), handler))); - } - - return tmp; -} - -ContentType AppFileEntity::GetContentType() const -{ - return ContentType::App; -} - -std::unique_ptr AppFileEntity::ToAppFile(FileEntity&& entity, const ReportingHandler& handler) -{ - ValidateContentType(entity, ContentType::App, handler); - - auto appEntity = dynamic_cast(entity); - - std::unordered_map hashes; - for (auto& [hashType, hashValue] : appEntity.hashes) - { - hashes[HashTypeFromString(hashType, handler)] = std::move(hashValue); - } - - std::vector architectures; - for (auto& arch : appEntity.applicabilityDetails.architectures) - { - architectures.push_back(ArchitectureFromString(arch, handler)); - } - - std::unique_ptr tmp; - THROW_IF_FAILED_LOG(AppFile::Make(std::move(appEntity.fileId), - std::move(appEntity.url), - appEntity.sizeInBytes, - std::move(hashes), - std::move(architectures), - std::move(appEntity.applicabilityDetails.platformApplicabilityForPackage), - std::move(appEntity.fileMoniker), - tmp), - handler); - return tmp; -} - -std::vector AppFileEntity::FileEntitiesToAppFileVector(std::vector>&& entities, - const ReportingHandler& handler) -{ - std::vector tmp; - for (auto& entity : entities) - { - tmp.push_back(std::move(*AppFileEntity::ToAppFile(std::move(*entity), handler))); - } - - return tmp; -} diff --git a/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.h b/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.h deleted file mode 100644 index 2bc5f4a788..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/FileEntity.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "ContentType.h" - -#include -#include -#include -#include - -#include - -namespace SFS -{ -class File; -class AppFile; - -namespace details -{ -class ReportingHandler; - -struct FileEntity; - -using FileEntities = std::vector>; - -struct FileEntity -{ - virtual ~FileEntity() - { - } - - virtual ContentType GetContentType() const = 0; - - std::string fileId; - std::string url; - uint64_t sizeInBytes; - std::unordered_map hashes; - - static std::unique_ptr FromJson(const nlohmann::json& file, const ReportingHandler& handler); - static FileEntities DownloadInfoResponseToFileEntities(const nlohmann::json& data, const ReportingHandler& handler); -}; - -struct GenericFileEntity : public FileEntity -{ - ContentType GetContentType() const override; - - static std::unique_ptr ToFile(FileEntity&& entity, const ReportingHandler& handler); - static std::vector FileEntitiesToFileVector(FileEntities&& entities, const ReportingHandler& handler); -}; - -struct ApplicabilityDetailsEntity -{ - std::vector architectures; - std::vector platformApplicabilityForPackage; -}; - -struct AppFileEntity : public FileEntity -{ - ContentType GetContentType() const override; - - std::string fileMoniker; - ApplicabilityDetailsEntity applicabilityDetails; - - static std::unique_ptr ToAppFile(FileEntity&& entity, const ReportingHandler& handler); - static std::vector FileEntitiesToAppFileVector(FileEntities&& entities, const ReportingHandler& handler); -}; - -} // namespace details -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.cpp b/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.cpp deleted file mode 100644 index eacd847e88..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "VersionEntity.h" - -#include "../ErrorHandling.h" -#include "../ReportingHandler.h" -#include "ContentId.h" - -#include - -#define THROW_INVALID_RESPONSE_IF_NOT(condition, message, handler) \ - THROW_CODE_IF_NOT_LOG(ServiceInvalidResponse, condition, handler, message) - -using namespace SFS; -using namespace SFS::details; -using json = nlohmann::json; - -namespace -{ -void ValidateContentType(const VersionEntity& entity, ContentType expectedType, const ReportingHandler& handler) -{ - THROW_CODE_IF_LOG(Result::ServiceUnexpectedContentType, - entity.GetContentType() != expectedType, - handler, - "The service returned entity \"" + entity.contentId.name + "\" with content type [" + - ToString(entity.GetContentType()) + "] while the expected type was [" + - ToString(expectedType) + "]"); -} -} // namespace - -std::unique_ptr VersionEntity::FromJson(const nlohmann::json& data, const ReportingHandler& handler) -{ - // Expected format for a generic version entity: - // { - // "ContentId": { - // "Namespace": , - // "Name": , - // "Version": - // } - // } - // - // Expected extra elements for an app version entity: - // { - // ... - // "UpdateId": "", - // "Prerequisites": [ - // { - // "Namespace": "", - // "Name": "", - // "Version": "" - // } - // ] - // } - - THROW_INVALID_RESPONSE_IF_NOT(data.is_object(), "Response is not a JSON object", handler); - - std::unique_ptr tmp; - const bool isAppEntity = data.contains("UpdateId"); - if (isAppEntity) - { - tmp = std::make_unique(); - } - else - { - tmp = std::make_unique(); - } - - THROW_INVALID_RESPONSE_IF_NOT(data.contains("ContentId"), "Missing ContentId in response", handler); - - const auto& contentId = data["ContentId"]; - THROW_INVALID_RESPONSE_IF_NOT(contentId.is_object(), "ContentId is not a JSON object", handler); - - THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Namespace"), "Missing ContentId.Namespace in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(contentId["Namespace"].is_string(), "ContentId.Namespace is not a string", handler); - tmp->contentId.nameSpace = contentId["Namespace"]; - - THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Name"), "Missing ContentId.Name in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(contentId["Name"].is_string(), "ContentId.Name is not a string", handler); - tmp->contentId.name = contentId["Name"]; - - THROW_INVALID_RESPONSE_IF_NOT(contentId.contains("Version"), "Missing ContentId.Version in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(contentId["Version"].is_string(), "ContentId.Version is not a string", handler); - tmp->contentId.version = contentId["Version"]; - - if (isAppEntity) - { - auto appEntity = dynamic_cast(tmp.get()); - - THROW_INVALID_RESPONSE_IF_NOT(data["UpdateId"].is_string(), "UpdateId is not a string", handler); - appEntity->updateId = data["UpdateId"]; - - THROW_INVALID_RESPONSE_IF_NOT(data.contains("Prerequisites"), "Missing Prerequisites in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(data["Prerequisites"].is_array(), "Prerequisites is not an array", handler); - - for (const auto& prereq : data["Prerequisites"]) - { - THROW_INVALID_RESPONSE_IF_NOT(prereq.is_object(), "Prerequisite element is not a JSON object", handler); - - GenericVersionEntity prereqEntity; - THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Namespace"), - "Missing Prerequisite.Namespace in response", - handler); - THROW_INVALID_RESPONSE_IF_NOT(prereq["Namespace"].is_string(), - "Prerequisite.Namespace is not a string", - handler); - prereqEntity.contentId.nameSpace = prereq["Namespace"]; - - THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Name"), "Missing Prerequisite.Name in response", handler); - THROW_INVALID_RESPONSE_IF_NOT(prereq["Name"].is_string(), "Prerequisite.Name is not a string", handler); - prereqEntity.contentId.name = prereq["Name"]; - - THROW_INVALID_RESPONSE_IF_NOT(prereq.contains("Version"), - "Missing Prerequisite.Version in response", - handler); - THROW_INVALID_RESPONSE_IF_NOT(prereq["Version"].is_string(), - "Prerequisite.Version is not a string", - handler); - prereqEntity.contentId.version = prereq["Version"]; - - appEntity->prerequisites.push_back(std::move(prereqEntity)); - } - } - return tmp; -} - -std::unique_ptr VersionEntity::ToContentId(VersionEntity&& entity, const ReportingHandler& handler) -{ - std::unique_ptr tmp; - THROW_IF_FAILED_LOG(ContentId::Make(std::move(entity.contentId.nameSpace), - std::move(entity.contentId.name), - std::move(entity.contentId.version), - tmp), - handler); - return tmp; -} - -ContentType GenericVersionEntity::GetContentType() const -{ - return ContentType::Generic; -} - -ContentType AppVersionEntity::GetContentType() const -{ - return ContentType::App; -} - -AppVersionEntity* AppVersionEntity::GetAppVersionEntityPtr(std::unique_ptr& versionEntity, - const ReportingHandler& handler) -{ - ValidateContentType(*versionEntity, ContentType::App, handler); - return dynamic_cast(versionEntity.get()); -} diff --git a/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.h b/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.h deleted file mode 100644 index 3b6cb42705..0000000000 --- a/src/SfsClient/sfs-client/client/src/details/entity/VersionEntity.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "ContentType.h" - -#include -#include -#include - -#include - -namespace SFS -{ -class ContentId; - -namespace details -{ -class ReportingHandler; - -struct ContentIdEntity -{ - std::string nameSpace; - std::string name; - std::string version; -}; - -struct VersionEntity -{ - virtual ~VersionEntity() - { - } - - virtual ContentType GetContentType() const = 0; - - ContentIdEntity contentId; - - static std::unique_ptr FromJson(const nlohmann::json& data, const ReportingHandler& handler); - static std::unique_ptr ToContentId(VersionEntity&& entity, const ReportingHandler& handler); -}; - -struct GenericVersionEntity : public VersionEntity -{ - ContentType GetContentType() const override; -}; - -struct AppVersionEntity : public VersionEntity -{ - ContentType GetContentType() const override; - - std::string updateId; - std::vector prerequisites; - - static AppVersionEntity* GetAppVersionEntityPtr(std::unique_ptr& versionEntity, - const ReportingHandler& handler); -}; - -using VersionEntities = std::vector>; -} // namespace details -} // namespace SFS diff --git a/src/SfsClient/sfs-client/client/tests/CMakeLists.txt b/src/SfsClient/sfs-client/client/tests/CMakeLists.txt deleted file mode 100644 index 5782701e10..0000000000 --- a/src/SfsClient/sfs-client/client/tests/CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -find_package(Catch2 CONFIG REQUIRED) -include(Catch) - -# For mock http server -find_package(httplib CONFIG REQUIRED) - -project(SFSClientTests LANGUAGES CXX) - -add_executable(${PROJECT_NAME}) - -target_sources( - ${PROJECT_NAME} - PRIVATE functional/details/CurlConnectionTests.cpp - functional/details/SFSClientImplTests.cpp - functional/SFSClientTests.cpp - mock/MockWebServer.cpp - mock/ProxyServer.cpp - mock/ServerCommon.cpp - unit/AppContentTests.cpp - unit/AppFileTests.cpp - unit/ApplicabilityDetailsTests.cpp - unit/ContentIdTests.cpp - unit/ContentTests.cpp - unit/details/CurlConnectionManagerTests.cpp - unit/details/CurlConnectionTests.cpp - unit/details/entity/FileEntityTests.cpp - unit/details/entity/VersionEntityTests.cpp - unit/details/EnvTests.cpp - unit/details/ErrorHandlingTests.cpp - unit/details/ReportingHandlerTests.cpp - unit/details/SFSClientImplTests.cpp - unit/details/SFSUrlBuilderTests.cpp - unit/details/TestOverrideTests.cpp - unit/details/UrlBuilderTests.cpp - unit/details/UtilTests.cpp - unit/FileTests.cpp - unit/ResultTests.cpp - unit/SFSClientTests.cpp - util/SFSExceptionMatcher.cpp - util/TestHelper.cpp) - -set(SFS_CLIENT_LIB_NAME "Microsoft::${CMAKE_PROJECT_NAME}") -add_dependencies(${PROJECT_NAME} ${SFS_CLIENT_LIB_NAME}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${SFS_CLIENT_LIB_NAME}) -target_link_libraries(${PROJECT_NAME} PRIVATE Catch2::Catch2WithMain) -target_link_libraries(${PROJECT_NAME} PRIVATE httplib::httplib) - -target_include_directories(${PROJECT_NAME} PUBLIC ../include) -target_include_directories(${PROJECT_NAME} PUBLIC ../src/details) - -catch_discover_tests(${PROJECT_NAME}) - -set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY - ${CMAKE_BINARY_DIR}/tests/bin/) - -set_compile_options_for_target(${PROJECT_NAME}) - -if(SFS_ENABLE_TEST_OVERRIDES) - target_compile_definitions(${PROJECT_NAME} - PRIVATE SFS_ENABLE_TEST_OVERRIDES=1) -endif() diff --git a/src/SfsClient/sfs-client/client/tests/functional/SFSClientTests.cpp b/src/SfsClient/sfs-client/client/tests/functional/SFSClientTests.cpp deleted file mode 100644 index f5b90bc942..0000000000 --- a/src/SfsClient/sfs-client/client/tests/functional/SFSClientTests.cpp +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../mock/MockWebServer.h" -#include "../mock/ProxyServer.h" -#include "../util/TestHelper.h" -#include "TestOverride.h" -#include "sfsclient/SFSClient.h" - -#include - -#include - -#define TEST(...) TEST_CASE("[Functional][SFSClientTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::test; -using namespace std::chrono; - -const std::string c_instanceId = "testInstanceId"; -const std::string c_namespace = "testNamespace"; -const std::string c_productName = "testProduct"; -const std::string c_version = "0.0.1"; -const std::string c_nextVersion = "0.0.2"; - -namespace -{ -void CheckContentId(const ContentId& contentId, const std::string& name, const std::string& version) -{ - REQUIRE(contentId.GetNameSpace() == c_namespace); - REQUIRE(contentId.GetName() == name); - REQUIRE(contentId.GetVersion() == version); -} - -void CheckFiles(const std::vector& files) -{ - REQUIRE(files.size() == 2); - REQUIRE(files[0].GetFileId() == (c_productName + ".json")); - REQUIRE(files[0].GetUrl() == ("http://localhost/1.json")); - REQUIRE(files[1].GetFileId() == (c_productName + ".bin")); - REQUIRE(files[1].GetUrl() == ("http://localhost/2.bin")); -} - -void CheckAppFiles(const std::vector& files, const std::string& name) -{ - REQUIRE(files.size() == 2); - REQUIRE(files[0].GetFileId() == (name + ".json")); - REQUIRE(files[0].GetUrl() == ("http://localhost/1.json")); - REQUIRE_FALSE(files[0].GetFileMoniker().empty()); - REQUIRE_FALSE(files[0].GetApplicabilityDetails().GetArchitectures().empty()); - REQUIRE_FALSE(files[0].GetApplicabilityDetails().GetPlatformApplicabilityForPackage().empty()); - REQUIRE(files[1].GetFileId() == (name + ".bin")); - REQUIRE(files[1].GetUrl() == ("http://localhost/2.bin")); - REQUIRE_FALSE(files[1].GetFileMoniker().empty()); - REQUIRE_FALSE(files[1].GetApplicabilityDetails().GetArchitectures().empty()); - REQUIRE_FALSE(files[1].GetApplicabilityDetails().GetPlatformApplicabilityForPackage().empty()); -} - -void CheckMockContent(const Content& content, const std::string& version) -{ - CheckContentId(content.GetContentId(), c_productName, version); - CheckFiles(content.GetFiles()); -} - -void CheckMockAppContent(const AppContent& content, - const std::string& version, - const std::vector mockPrereqs) -{ - CheckContentId(content.GetContentId(), c_productName, version); - REQUIRE_FALSE(content.GetUpdateId().empty()); - CheckAppFiles(content.GetFiles(), c_productName); - - REQUIRE(content.GetPrerequisites().size() == mockPrereqs.size()); - for (size_t i = 0; i < mockPrereqs.size(); ++i) - { - const auto& prereq = content.GetPrerequisites()[i]; - const auto& mockPrereq = mockPrereqs[i]; - CheckContentId(prereq.GetContentId(), mockPrereq.name, mockPrereq.version); - - CheckAppFiles(prereq.GetFiles(), mockPrereq.name); - } -} -} // namespace - -TEST("Testing SFSClient::GetLatestDownloadInfo()") -{ - if (!AreTestOverridesAllowed()) - { - return; - } - - test::MockWebServer server; - ScopedTestOverride override(TestOverride::BaseUrl, server.GetBaseUrl()); - - std::unique_ptr sfsClient; - REQUIRE(SFSClient::Make({"testAccountId", c_instanceId, c_namespace, LogCallbackToTest}, sfsClient) == - Result::Success); - REQUIRE(sfsClient != nullptr); - - server.RegisterProduct(c_productName, c_version); - - std::vector contents; - - SECTION("Single product request") - { - RequestParams params; - params.baseCV = "aaaaaaaaaaaaaaaa.1"; - SECTION("No attributes") - { - params.productRequests = {{c_productName, {}}}; - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockContent(contents[0], c_version); - } - - SECTION("No attributes + proxy") - { - test::ProxyServer proxy; - - params.productRequests = {{c_productName, {}}}; - params.proxy = proxy.GetBaseUrl(); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockContent(contents[0], c_version); - - REQUIRE(proxy.Stop() == Result::Success); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - params.productRequests = {{c_productName, attributes}}; - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockContent(contents[0], c_version); - } - - SECTION("Wrong product name") - { - params.productRequests = {{"badName", {}}}; - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpNotFound); - REQUIRE(contents.empty()); - - const TargetingAttributes attributes{{"attr1", "value"}}; - params.productRequests = {{"badName", attributes}}; - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpNotFound); - REQUIRE(contents.empty()); - } - - SECTION("Adding new version") - { - server.RegisterProduct(c_productName, c_nextVersion); - - params.productRequests = {{c_productName, {}}}; - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockContent(contents[0], c_nextVersion); - } - } - - REQUIRE(server.Stop() == Result::Success); -} - -TEST("Testing SFSClient::GetLatestAppDownloadInfo()") -{ - if (!AreTestOverridesAllowed()) - { - return; - } - - test::MockWebServer server; - ScopedTestOverride override(TestOverride::BaseUrl, server.GetBaseUrl()); - - const std::string prereq1 = "prereq1"; - const std::string prereq1Version = "1.0"; - const std::string prereq2 = "prereq2"; - const std::string prereq2Version = "2.0"; - - std::unique_ptr sfsClient; - REQUIRE(SFSClient::Make({"testAccountId", "storeapps", c_namespace, LogCallbackToTest}, sfsClient) == - Result::Success); - REQUIRE(sfsClient != nullptr); - - SECTION("App products") - { - auto CheckApp = [&](const std::vector& mockPrereqs) { - server.RegisterAppProduct(c_productName, c_version, mockPrereqs); - - std::vector contents; - - RequestParams params; - params.baseCV = "aaaaaaaaaaaaaaaa.1"; - SECTION("No attributes") - { - params.productRequests = {{c_productName, {}}}; - REQUIRE(sfsClient->GetLatestAppDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockAppContent(contents[0], c_version, mockPrereqs); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - params.productRequests = {{c_productName, attributes}}; - REQUIRE(sfsClient->GetLatestAppDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockAppContent(contents[0], c_version, mockPrereqs); - } - - SECTION("Wrong product name") - { - params.productRequests = {{"badName", {}}}; - REQUIRE(sfsClient->GetLatestAppDownloadInfo(params, contents) == Result::HttpNotFound); - REQUIRE(contents.empty()); - - const TargetingAttributes attributes{{"attr1", "value"}}; - params.productRequests = {{"badName", attributes}}; - REQUIRE(sfsClient->GetLatestAppDownloadInfo(params, contents) == Result::HttpNotFound); - REQUIRE(contents.empty()); - } - - SECTION("Adding new version") - { - server.RegisterAppProduct(c_productName, c_nextVersion, mockPrereqs); - - params.productRequests = {{c_productName, {}}}; - REQUIRE(sfsClient->GetLatestAppDownloadInfo(params, contents) == Result::Success); - REQUIRE(contents.size() == 1); - CheckMockAppContent(contents[0], c_nextVersion, mockPrereqs); - } - }; - - SECTION("No prerequisites") - { - CheckApp({}); - } - - SECTION("1 prereq") - { - CheckApp({{prereq1, prereq1Version}}); - } - - SECTION("2 prereqs") - { - CheckApp({{prereq1, prereq1Version}, {prereq2, prereq2Version}}); - } - } - - SECTION("Non-app products") - { - server.RegisterProduct(c_productName, c_version); - - std::vector contents; - - RequestParams params; - params.productRequests = {{c_productName, {}}}; - auto result = sfsClient->GetLatestAppDownloadInfo(params, contents); - REQUIRE(result.GetCode() == Result::ServiceUnexpectedContentType); - REQUIRE( - result.GetMsg() == - R"(The service returned entity "testProduct" with content type [Generic] while the expected type was [App])"); - REQUIRE(contents.empty()); - } - - REQUIRE(server.Stop() == Result::Success); -} - -TEST("Testing SFSClient retry behavior") -{ - if (!AreTestOverridesAllowed()) - { - INFO("Skipping. Test overrides not enabled"); - return; - } - - MockWebServer server; - ScopedTestOverride urlOverride(TestOverride::BaseUrl, server.GetBaseUrl()); - - server.RegisterProduct(c_productName, c_version); - RequestParams params; - params.productRequests = {{c_productName, {}}}; - std::vector contents; - - std::unique_ptr sfsClient; - ClientConfig clientConfig{"testAccountId", c_instanceId, c_namespace, LogCallbackToTest}; - - SECTION("Test exponential backoff") - { - INFO("Sets the retry delay to 50ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 50); - - REQUIRE(SFSClient::Make(clientConfig, sfsClient)); - REQUIRE(sfsClient != nullptr); - - const int retriableError = 503; // ServerBusy - - auto RunTimedGet = [&](bool success = true) -> long long { - auto begin = steady_clock::now(); - if (success) - { - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents)); - REQUIRE(contents.size() == 1); - } - else - { - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpServiceNotAvailable); - REQUIRE(contents.empty()); - } - auto end = steady_clock::now(); - return duration_cast(end - begin).count(); - }; - - long long allowedTimeDeviation = 200LL; - std::queue forcedHttpErrors({retriableError}); - SECTION("Should take at least 50ms with a single retriable error") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 50LL); - REQUIRE(time < 50LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 150ms (50ms + 2*50ms) with two retriable errors") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 150LL); - REQUIRE(time < 150LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 300ms (50ms + 2*50ms + 3*50ms) with three retriable errors") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 300LL); - REQUIRE(time < 300LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 300ms (50ms + 2*50ms + 3*50ms) with four retriable errors, but fail") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(false /*success*/); - REQUIRE(time >= 300LL); - REQUIRE(time < 300LL + allowedTimeDeviation); - } - } - - SECTION("Test retriable errors with Retry-After headers") - { - INFO("Sets the retry delay to 200ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 200); - - REQUIRE(SFSClient::Make(clientConfig, sfsClient)); - REQUIRE(sfsClient != nullptr); - - const int retriableError = 503; // ServerBusy - const int retriableError2 = 502; // BadGateway - - auto RunTimedGet = [&]() -> long long { - auto begin = steady_clock::now(); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents)); - REQUIRE(contents.size() == 1); - auto end = steady_clock::now(); - return duration_cast(end - begin).count(); - }; - - std::unordered_map headersByCode; - headersByCode[retriableError] = {{"Retry-After", "1"}}; // 1s delay - server.SetResponseHeaders(headersByCode); - - long long allowedTimeDeviation = 200LL; - std::queue forcedHttpErrors({retriableError}); - SECTION("Should take at least 1000ms with a single retriable error with 1s in Retry-After") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 1000LL); - REQUIRE(time < 1000LL + allowedTimeDeviation); - } - - SECTION( - "Should take at least 1000ms + 200ms with a retriable error with 1s in Retry-After and one with 200ms*2 as default value") - { - forcedHttpErrors.push(retriableError2); - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 1400LL); - REQUIRE(time < 1400LL + allowedTimeDeviation); - } - } - - SECTION("Test maxRetries") - { - INFO("Sets the retry delay to 1ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 1); - - const int retriableError = 503; // ServerBusy - - SECTION("With default retries") - { - REQUIRE(SFSClient::Make(clientConfig, sfsClient)); - REQUIRE(sfsClient != nullptr); - - SECTION("Should pass with 3 errors") - { - server.SetForcedHttpErrors(std::queue({retriableError, retriableError, retriableError})); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents)); - } - - SECTION("Should fail with 4 errors") - { - server.SetForcedHttpErrors( - std::queue({retriableError, retriableError, retriableError, retriableError})); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpServiceNotAvailable); - } - } - - SECTION("Forcing retries to 3") - { - params.retryOnError = true; - REQUIRE(SFSClient::Make(clientConfig, sfsClient)); - REQUIRE(sfsClient != nullptr); - - SECTION("Should pass with 3 errors") - { - server.SetForcedHttpErrors(std::queue({retriableError, retriableError, retriableError})); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents)); - } - - SECTION("Should fail with 4 errors") - { - server.SetForcedHttpErrors( - std::queue({retriableError, retriableError, retriableError, retriableError})); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpServiceNotAvailable); - } - } - - SECTION("Reducing retries to 0") - { - params.retryOnError = false; - REQUIRE(SFSClient::Make(clientConfig, sfsClient)); - REQUIRE(sfsClient != nullptr); - - SECTION("Should pass with no errors") - { - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents)); - } - - SECTION("Should fail with 1 error") - { - server.SetForcedHttpErrors(std::queue({retriableError})); - REQUIRE(sfsClient->GetLatestDownloadInfo(params, contents) == Result::HttpServiceNotAvailable); - } - } - } - - REQUIRE(server.Stop() == Result::Success); -} diff --git a/src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp b/src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp deleted file mode 100644 index 77c114395a..0000000000 --- a/src/SfsClient/sfs-client/client/tests/functional/details/CurlConnectionTests.cpp +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../mock/MockWebServer.h" -#include "../../mock/ProxyServer.h" -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "SFSUrlBuilder.h" -#include "TestOverride.h" -#include "connection/CurlConnection.h" -#include "connection/CurlConnectionManager.h" -#include "connection/HttpHeader.h" - -#include -#include -#include -#include - -#include -#include - -#define TEST(...) TEST_CASE("[Functional][CurlConnectionTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using namespace std::chrono; -using json = nlohmann::json; - -const std::string c_instanceId = "default"; -const std::string c_namespace = "default"; -const std::string c_productName = "testProduct"; -const std::string c_version = "0.0.1"; -const std::string c_nextVersion = "0.0.2"; - -namespace -{ -class CurlConnectionTimeout : public CurlConnection -{ - public: - CurlConnectionTimeout(const ConnectionConfig& config, const ReportingHandler& handler) - : CurlConnection(config, handler) - { - } - - std::string Get(const std::string& url) override - { - // Timeout within 1ms - curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 1L); - return CurlConnection::Get(url); - } - - std::string Post(const std::string& url, const std::string& data) override - { - // Timeout within 1ms - curl_easy_setopt(m_handle, CURLOPT_TIMEOUT_MS, 1L); - return CurlConnection::Post(url, data); - } -}; - -class CurlConnectionTimeoutManager : public CurlConnectionManager -{ - public: - CurlConnectionTimeoutManager(const ReportingHandler& handler) : CurlConnectionManager(handler) - { - } - - std::unique_ptr MakeConnection(const ConnectionConfig& config) override - { - return std::make_unique(config, m_handler); - } -}; - -std::string TimestampToHttpDateString(std::chrono::time_point time) -{ - auto timer = system_clock::to_time_t(time); - - std::stringstream timeStream; - struct tm gmTime; -#ifdef _WIN32 - gmtime_s(&gmTime, &timer); // gmtime_s is the safe version of gmtime, not available on Linux -#else - gmTime = (*std::gmtime(&timer)); -#endif - timeStream << std::put_time(&gmTime, "%a, %d %b %Y %X GMT"); // day, dd Mmm yyyy HH:MM:SS - - return timeStream.str(); -} -} // namespace - -TEST("Testing CurlConnection()") -{ - test::MockWebServer server; - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - CurlConnectionManager connectionManager(handler); - auto connection = connectionManager.MakeConnection({}); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - server.RegisterExpectedRequestHeader(HttpHeader::UserAgent, GetUserAgentValue()); - - SECTION("Testing CurlConnection::Get()") - { - const std::string url = urlBuilder.GetSpecificVersionUrl(c_productName, c_version); - - json expectedResponse; - expectedResponse["ContentId"] = {{"Namespace", "default"}, {"Name", c_productName}, {"Version", c_version}}; - - SECTION("Direct connection") - { - // Before registering the product, the URL returns 404 Not Found - REQUIRE_THROWS_CODE(connection->Get(url), HttpNotFound); - - // Register the product - server.RegisterProduct(c_productName, c_version); - - // After registering the product, the URL returns 200 OK - std::string out; - REQUIRE_NOTHROW(out = connection->Get(url)); - - REQUIRE(json::parse(out) == expectedResponse); - } - - SECTION("With proxy") - { - test::ProxyServer proxy; - - ConnectionConfig config; - config.proxy = proxy.GetBaseUrl(); - connection = connectionManager.MakeConnection(config); - - // Before registering the product, the URL returns 404 Not Found - REQUIRE_THROWS_CODE(connection->Get(url), HttpNotFound); - - // Register the product - server.RegisterProduct(c_productName, c_version); - - // After registering the product, the URL returns 200 OK - std::string out; - REQUIRE_NOTHROW(out = connection->Get(url)); - - REQUIRE(json::parse(out) == expectedResponse); - - REQUIRE(proxy.Stop() == Result::Success); - } - } - - SECTION("Testing CurlConnection::Post()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - - SECTION("With GetLatestVersionBatch mock") - { - const std::string url = urlBuilder.GetLatestVersionBatchUrl(); - - // Missing proper body returns HttpBadRequest - REQUIRE_THROWS_CODE(connection->Post(url), HttpBadRequest); - - // Before registering the product, the URL returns 404 Not Found - const json body = {{{"TargetingAttributes", {}}, {"Product", c_productName}}}; - REQUIRE_THROWS_CODE(connection->Post(url, body.dump()), HttpNotFound); - - // Register the product - server.RegisterProduct(c_productName, c_version); - - // After registering the product, the URL returns 200 OK - std::string out; - REQUIRE_NOTHROW(out = connection->Post(url, body.dump())); - - json expectedResponse = json::array(); - expectedResponse.push_back( - {{"ContentId", {{"Namespace", "default"}, {"Name", c_productName}, {"Version", c_version}}}}); - REQUIRE(json::parse(out) == expectedResponse); - - // Returns the next version now - server.RegisterProduct(c_productName, c_nextVersion); - REQUIRE_NOTHROW(out = connection->Post(url, body.dump())); - - expectedResponse = json::array(); - expectedResponse.push_back( - {{"ContentId", {{"Namespace", "default"}, {"Name", c_productName}, {"Version", c_nextVersion}}}}); - REQUIRE(json::parse(out) == expectedResponse); - - SECTION("Testing with proxy") - { - test::ProxyServer proxy; - - ConnectionConfig config; - config.proxy = proxy.GetBaseUrl(); - connection = connectionManager.MakeConnection(config); - - REQUIRE_NOTHROW(out = connection->Post(url, body.dump())); - - REQUIRE(json::parse(out) == expectedResponse); - - REQUIRE(proxy.Stop() == Result::Success); - } - } - - SECTION("With GetDownloadInfo mock") - { - const std::string url = urlBuilder.GetDownloadInfoUrl(c_productName, c_version); - - // Before registering the product, the URL returns 404 Not Found - REQUIRE_THROWS_CODE(connection->Post(url), HttpNotFound); - - // Register the product - server.RegisterProduct(c_productName, c_version); - - // After registering the product, the URL returns 200 OK - std::string out; - REQUIRE_NOTHROW(out = connection->Post(url)); - - json expectedResponse = json::array(); - expectedResponse.push_back({{"Url", "http://localhost/1.json"}, - {"FileId", c_productName + ".json"}, - {"SizeInBytes", 100}, - {"Hashes", {{"Sha1", "123"}, {"Sha256", "456"}}}}); - expectedResponse[0]["DeliveryOptimization"] = {{"CatalogId", "789"}}; - expectedResponse[0]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/1.json"}, {"HashOfHashes", "abc"}}}}; - - expectedResponse.push_back({{"Url", "http://localhost/2.bin"}, - {"FileId", c_productName + ".bin"}, - {"SizeInBytes", 200}, - {"Hashes", {{"Sha1", "421"}, {"Sha256", "132"}}}}); - expectedResponse[1]["DeliveryOptimization"] = {{"CatalogId", "14"}}; - expectedResponse[1]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/2.bin"}, {"HashOfHashes", "abcd"}}}}; - - REQUIRE(json::parse(out) == expectedResponse); - - SECTION("Testing with proxy") - { - test::ProxyServer proxy; - - ConnectionConfig config; - config.proxy = proxy.GetBaseUrl(); - connection = connectionManager.MakeConnection(config); - - REQUIRE_NOTHROW(out = connection->Post(url)); - - REQUIRE(json::parse(out) == expectedResponse); - - REQUIRE(proxy.Stop() == Result::Success); - } - } - } - - SECTION("Testing with a malformed url") - { - std::string url = server.GetBaseUrl(); - REQUIRE_THROWS_CODE(connection->Get(url), HttpNotFound); - REQUIRE_THROWS_CODE(connection->Get(url + "/names"), HttpNotFound); - REQUIRE_THROWS_CODE(connection->Post(url + "/names", "{}"), HttpNotFound); - - url = server.GetBaseUrl() + "/api/v1/contents/" + c_instanceId + "/namespaces/" + c_namespace + "/names/" + - c_productName + "/versYions/" + c_version + "/files?action=GenerateDownloadInfo"; - - std::string out; - REQUIRE_THROWS_CODE(connection->Get(url), HttpNotFound); - REQUIRE_THROWS_CODE(connection->Post(url, std::string()), HttpNotFound); - REQUIRE_THROWS_CODE(out = connection->Post(url, {}), HttpNotFound); - CHECK(out.empty()); - } - - REQUIRE(server.Stop() == Result::Success); -} - -TEST("Testing CurlConnection when the server is not reachable") -{ - // Using a custom override class just to time out faster on an invalid URL - ReportingHandler handler; - CurlConnectionTimeoutManager connectionManager(handler); - handler.SetLoggingCallback(LogCallbackToTest); - auto connection = connectionManager.MakeConnection({}); - - // Using a non-routable IP address to ensure the server is not reachable - // https://www.rfc-editor.org/rfc/rfc5737#section-3: The blocks 192.0.2.0/24 (...) are provided for use in - // documentation. - std::string url = "192.0.2.0"; - std::string out; - REQUIRE_THROWS_CODE(connection->Get(url), HttpTimeout); - REQUIRE_THROWS_CODE(connection->Post(url + "/names", "{}"), HttpTimeout); - - url = "192.0.2.0/files?action=GenerateDownloadInfo"; - - REQUIRE_THROWS_CODE_MSG_MATCHES(connection->Get(url), - HttpTimeout, - Catch::Matchers::ContainsSubstring("timed out after")); - REQUIRE_THROWS_CODE_MSG_MATCHES(connection->Post(url, {}), - HttpTimeout, - Catch::Matchers::ContainsSubstring("timed out after")); -} - -TEST("Testing CurlConnection works from a second ConnectionManager") -{ - ReportingHandler handler; - - // Create a first connection manager and see it leave scope - curl initializes and uninitializes - { - CurlConnectionManager connectionManager(handler); - REQUIRE(connectionManager.MakeConnection({}) != nullptr); - } - - test::MockWebServer server; - CurlConnectionManager connectionManager(handler); - handler.SetLoggingCallback(LogCallbackToTest); - auto connection = connectionManager.MakeConnection({}); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - - const std::string url = urlBuilder.GetSpecificVersionUrl(c_productName, c_version); - - // Register the product - server.RegisterProduct(c_productName, c_version); - - // After registering the product, the URL returns 200 OK - REQUIRE_NOTHROW(connection->Get(url)); - - SECTION("A second connection also works") - { - auto connection2 = connectionManager.MakeConnection({}); - REQUIRE_NOTHROW(connection2->Get(url)); - } -} - -TEST("Testing a url that's too big throws 414") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - test::MockWebServer server; - CurlConnectionManager connectionManager(handler); - auto connection = connectionManager.MakeConnection({}); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - - // Will use a fake large product name to produce a large url - const std::string largeProductName(90000, 'a'); - - // Register the products - server.RegisterProduct(largeProductName, c_version); - - // Url produces: 414 URI Too Long - std::string out; - REQUIRE_THROWS_CODE_MSG(connection->Get(urlBuilder.GetSpecificVersionUrl(largeProductName, c_version)), - HttpUnexpected, - "Unexpected HTTP code 414"); -} - -TEST("Testing a response over the limit fails the operation") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - test::MockWebServer server; - CurlConnectionManager connectionManager(handler); - auto connection = connectionManager.MakeConnection({}); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - - // Will use a fake large product name to produce a response over the limit of 100k characters - const std::string largeProductName(90000, 'a'); - const std::string overLimitProductName(1000000, 'a'); - - // Register the products - server.RegisterProduct(largeProductName, c_version); - server.RegisterProduct(overLimitProductName, c_version); - - // Using GetLatestVersionBatch api since the product name is in the body and not in the url, to avoid a 414 error - // like on the test above - const std::string url = urlBuilder.GetLatestVersionBatchUrl(); - - // Large one works - json body = {{{"TargetingAttributes", {}}, {"Product", largeProductName}}}; - REQUIRE_NOTHROW(connection->Post(url, body.dump())); - - // Going over the limit fails with a message like "client returned ERROR on write of 16384 bytes" - body[0]["Product"] = overLimitProductName; - REQUIRE_THROWS_CODE_MSG_MATCHES(connection->Post(url, body.dump()), - ConnectionUnexpectedError, - Catch::Matchers::ContainsSubstring("client returned ERROR on write of")); -} - -TEST("Testing MS-CV is sent to server") -{ - test::MockWebServer server; - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - CurlConnectionManager connectionManager(handler); - - const std::string cv = "aaaaaaaaaaaaaaaa.1"; - ConnectionConfig config; - config.baseCV = cv; - auto connection = connectionManager.MakeConnection(config); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - - const std::string url = urlBuilder.GetSpecificVersionUrl(c_productName, c_version); - - server.RegisterProduct(c_productName, c_version); - - INFO("First value ends with .0"); - server.RegisterExpectedRequestHeader(HttpHeader::MSCV, cv + ".0"); - connection->Get(url); - - INFO("Value gets incremented on subsequent requests"); - server.RegisterExpectedRequestHeader(HttpHeader::MSCV, cv + ".1"); - connection->Get(url); - - server.RegisterExpectedRequestHeader(HttpHeader::MSCV, cv + ".2"); - connection->Get(url); -} - -TEST("Testing retry behavior") -{ - if (!AreTestOverridesAllowed()) - { - INFO("Skipping. Test overrides not enabled"); - return; - } - - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - MockWebServer server; - CurlConnectionManager connectionManager(handler); - SFSUrlBuilder urlBuilder(SFSCustomUrl(server.GetBaseUrl()), c_instanceId, c_namespace, handler); - - server.RegisterProduct(c_productName, c_version); - const std::string url = urlBuilder.GetSpecificVersionUrl(c_productName, c_version); - - SECTION("Test exponential backoff") - { - auto connection = connectionManager.MakeConnection({}); - - INFO("Sets the retry delay to 50ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 50); - - const int retriableError = 503; // ServerBusy - - auto RunTimedGet = [&](bool success = true) -> long long { - std::string out; - auto begin = steady_clock::now(); - if (success) - { - REQUIRE_NOTHROW(out = connection->Get(url)); - REQUIRE_FALSE(out.empty()); - } - else - { - REQUIRE_THROWS_CODE(out = connection->Get(url), HttpServiceNotAvailable); - REQUIRE(out.empty()); - } - auto end = steady_clock::now(); - return duration_cast(end - begin).count(); - }; - - long long allowedTimeDeviation = 200LL; - std::queue forcedHttpErrors({retriableError}); - SECTION("Should take at least 50ms with a single retriable error") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 50LL); - REQUIRE(time < 50LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 150ms (50ms + 2*50ms) with two retriable errors") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 150LL); - REQUIRE(time < 150LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 300ms (50ms + 2*50ms + 3*50ms) with three retriable errors") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 300LL); - REQUIRE(time < 300LL + allowedTimeDeviation); - } - - forcedHttpErrors.push(retriableError); - SECTION("Should take at least 300ms (50ms + 2*50ms + 3*50ms) with four retriable errors, but fail") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(false /*success*/); - REQUIRE(time >= 300LL); - REQUIRE(time < 300LL + allowedTimeDeviation); - } - } - - SECTION("Test retriable errors with Retry-After headers") - { - auto connection = connectionManager.MakeConnection({}); - - INFO("Sets the retry delay to 200ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 200); - - const int retriableError = 503; // ServerBusy - const int retriableError2 = 502; // BadGateway - - auto RunTimedGet = [&]() -> long long { - std::string out; - auto begin = steady_clock::now(); - REQUIRE_NOTHROW(out = connection->Get(url)); - REQUIRE_FALSE(out.empty()); - auto end = steady_clock::now(); - return duration_cast(end - begin).count(); - }; - - auto testForRetryAfterValue = [&](const std::string& retryAfterValue) -> void { - INFO("retryAfterValue = " << retryAfterValue); - - std::unordered_map headersByCode; - headersByCode[retriableError] = {{"Retry-After", retryAfterValue}}; - server.SetResponseHeaders(headersByCode); - - long long allowedTimeDeviation = 200LL; - std::queue forcedHttpErrors({retriableError}); - SECTION("Should take at least 2000ms with a single retriable error, with 2s in Retry-After") - { - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 2000LL); - REQUIRE(time < 2000LL + allowedTimeDeviation); - } - - SECTION( - "Should take at least 2000ms + 200ms*2 (exponential backoff) with two retriable errors, with 2s in Retry-After of the first error") - { - forcedHttpErrors.push(retriableError2); - server.SetForcedHttpErrors(forcedHttpErrors); - const auto time = RunTimedGet(); - REQUIRE(time >= 2400LL); - REQUIRE(time < 2400LL + allowedTimeDeviation); - } - }; - - SECTION("Using seconds") - { - testForRetryAfterValue("2"); // 2s delay - } - SECTION("Using date") - { - const auto time = std::chrono::system_clock::now() + std::chrono::seconds(2); - testForRetryAfterValue(TimestampToHttpDateString(time)); - } - } - - SECTION("Test maxRetries") - { - INFO("Sets the retry delay to 1ms to speed up the test"); - ScopedTestOverride override(TestOverride::BaseRetryDelayMs, 1); - - const int retriableError = 503; // ServerBusy - - SECTION("Should pass with 3 errors") - { - auto connection = connectionManager.MakeConnection({}); - server.SetForcedHttpErrors(std::queue({retriableError, retriableError, retriableError})); - REQUIRE_NOTHROW(connection->Get(url)); - } - - SECTION("Should fail with 4 errors") - { - auto connection = connectionManager.MakeConnection({}); - server.SetForcedHttpErrors( - std::queue({retriableError, retriableError, retriableError, retriableError})); - REQUIRE_THROWS_CODE(connection->Get(url), HttpServiceNotAvailable); - } - - SECTION("Reducing retries to 2") - { - ConnectionConfig config; - config.maxRetries = 2; - auto connection = connectionManager.MakeConnection(config); - - SECTION("Should pass with 2 errors") - { - server.SetForcedHttpErrors(std::queue({retriableError, retriableError})); - REQUIRE_NOTHROW(connection->Get(url)); - } - - SECTION("Should fail with 3 errors") - { - server.SetForcedHttpErrors(std::queue({retriableError, retriableError, retriableError})); - REQUIRE_THROWS_CODE(connection->Get(url), HttpServiceNotAvailable); - } - } - - SECTION("Reducing retries to 0") - { - ConnectionConfig config; - config.maxRetries = 0; - auto connection = connectionManager.MakeConnection(config); - - SECTION("Should pass with no errors") - { - REQUIRE_NOTHROW(connection->Get(url)); - } - - SECTION("Should fail with 1 error") - { - server.SetForcedHttpErrors(std::queue({retriableError})); - REQUIRE_THROWS_CODE(connection->Get(url), HttpServiceNotAvailable); - } - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/functional/details/SFSClientImplTests.cpp b/src/SfsClient/sfs-client/client/tests/functional/details/SFSClientImplTests.cpp deleted file mode 100644 index 1b6fa1b6ea..0000000000 --- a/src/SfsClient/sfs-client/client/tests/functional/details/SFSClientImplTests.cpp +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../mock/MockWebServer.h" -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "SFSClientImpl.h" -#include "connection/Connection.h" -#include "connection/CurlConnectionManager.h" -#include "connection/HttpHeader.h" - -#include - -#include - -#define TEST(...) TEST_CASE("[Functional][SFSClientImplTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; - -namespace -{ -void CheckProduct(const VersionEntity& entity, std::string_view ns, std::string_view name, std::string_view version) -{ - REQUIRE(entity.GetContentType() == ContentType::Generic); - REQUIRE(entity.contentId.nameSpace == ns); - REQUIRE(entity.contentId.name == name); - REQUIRE(entity.contentId.version == version); -} - -void CheckAppProduct(const VersionEntity& entity, std::string_view ns, std::string_view name, std::string_view version) -{ - REQUIRE(entity.GetContentType() == ContentType::App); - auto appEntity = dynamic_cast(entity); - REQUIRE(appEntity.contentId.nameSpace == ns); - REQUIRE(appEntity.contentId.name == name); - REQUIRE(appEntity.contentId.version == version); - REQUIRE_FALSE(appEntity.updateId.empty()); - REQUIRE(appEntity.prerequisites.empty()); -} - -void CheckProducts(const VersionEntities& entities, - std::string_view ns, - const std::set>& nameVersionPairs) -{ - // json arrays don't guarantee order, and they are the underlying structure, so we need to check using sets - std::set> uniqueNameVersionPairs; - for (const auto& entity : entities) - { - REQUIRE(entity->contentId.nameSpace == ns); - uniqueNameVersionPairs.emplace(entity->contentId.name, entity->contentId.version); - } - - for (const auto& nameVersionPair : nameVersionPairs) - { - REQUIRE(uniqueNameVersionPairs.count(nameVersionPair)); - } -} - -void CheckDownloadInfo(const FileEntities& files, const std::string& name) -{ - REQUIRE(files.size() == 2); - REQUIRE(files[0]->GetContentType() == ContentType::Generic); - REQUIRE(files[0]->fileId == (name + ".json")); - REQUIRE(files[0]->url == ("http://localhost/1.json")); - REQUIRE(files[1]->GetContentType() == ContentType::Generic); - REQUIRE(files[1]->fileId == (name + ".bin")); - REQUIRE(files[1]->url == ("http://localhost/2.bin")); -} - -void CheckAppDownloadInfo(const FileEntities& files, const std::string& name) -{ - REQUIRE(files.size() == 2); - REQUIRE(files[0]->GetContentType() == ContentType::App); - REQUIRE(files[0]->fileId == (name + ".json")); - REQUIRE(files[0]->url == ("http://localhost/1.json")); - REQUIRE(files[1]->GetContentType() == ContentType::App); - REQUIRE(files[1]->fileId == (name + ".bin")); - REQUIRE(files[1]->url == ("http://localhost/2.bin")); -} -} // namespace - -TEST("Testing class SFSClientImpl()") -{ - test::MockWebServer server; - const std::string ns = "testNameSpace"; - SFSClientImpl sfsClient({"testAccountId", "testInstanceId", ns, LogCallbackToTest}); - sfsClient.SetCustomBaseUrl(server.GetBaseUrl()); - - const std::string cv = "aaaaaaaaaaaaaaaa.1"; - ConnectionConfig config; - config.baseCV = cv; - auto connection = sfsClient.MakeConnection(config); - server.RegisterExpectedRequestHeader(HttpHeader::UserAgent, GetUserAgentValue()); - - SECTION("Generic products") - { - server.RegisterProduct("productName", "0.0.0.2"); - server.RegisterProduct("productName", "0.0.0.1"); - - SECTION("Testing SFSClientImpl::GetLatestVersion()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - std::unique_ptr entity; - - SECTION("No attributes") - { - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({"productName", {}}, *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, "productName", "0.0.0.2"); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({"productName", attributes}, *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, "productName", "0.0.0.2"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", {}}, *connection), HttpNotFound); - REQUIRE(!entity); - - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", attributes}, *connection), - HttpNotFound); - REQUIRE(!entity); - } - } - - SECTION("Testing SFSClientImpl::GetLatestVersionBatch()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - VersionEntities entities; - - SECTION("No attributes") - { - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{"productName", {}}}, *connection)); - REQUIRE(!entities.empty()); - CheckProduct(*entities[0], ns, "productName", "0.0.0.2"); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{"productName", attributes}}, *connection)); - REQUIRE(!entities.empty()); - CheckProduct(*entities[0], ns, "productName", "0.0.0.2"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(entities = sfsClient.GetLatestVersionBatch({{"badName", {}}}, *connection), - HttpNotFound); - REQUIRE(entities.empty()); - - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_THROWS_CODE(entities = sfsClient.GetLatestVersionBatch({{"badName", attributes}}, *connection), - HttpNotFound); - REQUIRE(entities.empty()); - } - - SECTION("Multiple unique products") - { - server.RegisterProduct("productName2", "0.0.0.3"); - - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{"productName", {}}, {"productName2", {}}}, - *connection)); - REQUIRE(entities.size() == 2); - CheckProducts(entities, ns, {{"productName", "0.0.0.2"}, {"productName2", "0.0.0.3"}}); - - server.RegisterProduct("productName3", "0.0.0.4"); - - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch( - {{"productName", {}}, {"productName2", {}}, {"productName3", {}}}, - *connection)); - REQUIRE(entities.size() == 3); - CheckProducts(entities, - ns, - {{"productName", "0.0.0.2"}, {"productName2", "0.0.0.3"}, {"productName3", "0.0.0.4"}}); - } - - SECTION("Multiple repeated products") - { - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{"productName", {}}, {"productName", {}}}, - *connection)); - REQUIRE(entities.size() == 1); - CheckProduct(*entities[0], ns, "productName", "0.0.0.2"); - - server.RegisterProduct("productName2", "0.0.0.3"); - - REQUIRE_NOTHROW( - entities = sfsClient.GetLatestVersionBatch( - {{"productName", {}}, {"productName", {}}, {"productName2", {}}, {"productName2", {}}}, - *connection)); - REQUIRE(entities.size() == 2); - CheckProducts(entities, ns, {{"productName", "0.0.0.2"}, {"productName2", "0.0.0.3"}}); - } - - SECTION("Multiple wrong products returns 404") - { - REQUIRE_THROWS_CODE( - entities = sfsClient.GetLatestVersionBatch({{"badName", {}}, {"badName2", {}}}, *connection), - HttpNotFound); - REQUIRE(entities.empty()); - } - - SECTION("Multiple products, one wrong returns 200") - { - REQUIRE_NOTHROW( - entities = sfsClient.GetLatestVersionBatch({{"productName", {}}, {"badName", {}}}, *connection)); - REQUIRE(entities.size() == 1); - CheckProduct(*entities[0], ns, "productName", "0.0.0.2"); - } - } - - SECTION("Testing SFSClientImpl::GetSpecificVersion()") - { - std::unique_ptr entity; - SECTION("Getting 0.0.0.1") - { - REQUIRE_NOTHROW(entity = sfsClient.GetSpecificVersion("productName", "0.0.0.1", *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, "productName", "0.0.0.1"); - } - - SECTION("Getting 0.0.0.2") - { - REQUIRE_NOTHROW(entity = sfsClient.GetSpecificVersion("productName", "0.0.0.2", *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, "productName", "0.0.0.2"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(entity = sfsClient.GetSpecificVersion("badName", "0.0.0.2", *connection), - HttpNotFound); - REQUIRE(!entity); - } - - SECTION("Wrong version") - { - REQUIRE_THROWS_CODE(entity = sfsClient.GetSpecificVersion("productName", "0.0.0.3", *connection), - HttpNotFound); - REQUIRE(!entity); - } - } - - SECTION("Testing SFSClientImpl::GetDownloadInfo()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - FileEntities files; - - SECTION("Getting 0.0.0.1") - { - REQUIRE_NOTHROW(files = sfsClient.GetDownloadInfo("productName", "0.0.0.1", *connection)); - REQUIRE(!files.empty()); - CheckDownloadInfo(files, "productName"); - } - - SECTION("Getting 0.0.0.2") - { - REQUIRE_NOTHROW(files = sfsClient.GetDownloadInfo("productName", "0.0.0.2", *connection)); - REQUIRE(!files.empty()); - CheckDownloadInfo(files, "productName"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(files = sfsClient.GetDownloadInfo("badName", "0.0.0.2", *connection), HttpNotFound); - REQUIRE(files.empty()); - } - - SECTION("Wrong version") - { - REQUIRE_THROWS_CODE(files = sfsClient.GetDownloadInfo("productName", "0.0.0.3", *connection), - HttpNotFound); - REQUIRE(files.empty()); - } - } - } - - SECTION("App products") - { - server.RegisterAppProduct("productName", "0.0.0.2", {}); - server.RegisterAppProduct("productName", "0.0.0.1", {}); - - SECTION("Testing SFSClientImpl::GetLatestVersion()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - std::unique_ptr entity; - - SECTION("No attributes") - { - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({"productName", {}}, *connection)); - REQUIRE(entity); - CheckAppProduct(*entity, ns, "productName", "0.0.0.2"); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({"productName", attributes}, *connection)); - REQUIRE(entity); - CheckAppProduct(*entity, ns, "productName", "0.0.0.2"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", {}}, *connection), HttpNotFound); - REQUIRE(!entity); - - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", attributes}, *connection), - HttpNotFound); - REQUIRE(!entity); - } - } - - SECTION("Testing SFSClientImpl::GetDownloadInfo()") - { - server.RegisterExpectedRequestHeader(HttpHeader::ContentType, "application/json"); - FileEntities files; - - SECTION("Getting 0.0.0.1") - { - REQUIRE_NOTHROW(files = sfsClient.GetDownloadInfo("productName", "0.0.0.1", *connection)); - REQUIRE(!files.empty()); - CheckAppDownloadInfo(files, "productName"); - } - - SECTION("Getting 0.0.0.2") - { - REQUIRE_NOTHROW(files = sfsClient.GetDownloadInfo("productName", "0.0.0.2", *connection)); - REQUIRE(!files.empty()); - CheckAppDownloadInfo(files, "productName"); - } - - SECTION("Wrong product name") - { - REQUIRE_THROWS_CODE(files = sfsClient.GetDownloadInfo("badName", "0.0.0.2", *connection), HttpNotFound); - REQUIRE(files.empty()); - } - - SECTION("Wrong version") - { - REQUIRE_THROWS_CODE(files = sfsClient.GetDownloadInfo("productName", "0.0.0.3", *connection), - HttpNotFound); - REQUIRE(files.empty()); - } - } - } - - REQUIRE(server.Stop() == Result::Success); -} diff --git a/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.cpp b/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.cpp deleted file mode 100644 index be085ac05d..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.cpp +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "MockWebServer.h" - -#include "../util/TestHelper.h" -#include "ErrorHandling.h" -#include "ServerCommon.h" -#include "Util.h" -#include "connection/HttpHeader.h" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include - -#include -#include -#include -#include -#include - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::util; -using namespace SFS::test; -using namespace SFS::test::details; -using json = nlohmann::json; - -namespace -{ - -struct App -{ - std::string version; - std::vector prerequisites; -}; - -struct AppCmp -{ - bool operator()(App a, App b) const - { - return a.version < b.version; - } -}; - -json GenerateContentIdJsonObject(const std::string& name, const std::string& latestVersion, const std::string& ns) -{ - // { - // "ContentId": { - // "Namespace": , - // "Name": , - // "Version": - // } - // } - - return {{"ContentId", {{"Namespace", ns}, {"Name", name}, {"Version", latestVersion}}}}; -} - -json GenerateGetAppVersionJsonObject(const std::string& name, const App& app, const std::string& ns) -{ - // { - // "ContentId": { - // "Namespace": , - // "Name": , - // "Version": - // }, - // "UpdateId": "", - // "Prerequisites": [ - // { - // "Namespace": "", - // "Name": "", - // "Version": "" - // } - // ] - // } - - json prereqs = json::array(); - for (const auto& prereq : app.prerequisites) - { - prereqs.push_back({{"Namespace", ns}, {"Name", prereq.name}, {"Version", prereq.version}}); - } - return {{"ContentId", {{"Namespace", ns}, {"Name", name}, {"Version", app.version}}}, - {"UpdateId", "123"}, - {"Prerequisites", prereqs}}; -} - -json GeneratePostDownloadInfo(const std::string& name) -{ - // [ - // { - // "Url": , - // "FileId": , - // "SizeInBytes": , - // "Hashes": { - // "Sha1": , - // "Sha256": - // }, - // "DeliveryOptimization": { - // "CatalogId": , - // "Properties": { - // "IntegrityCheckInfo": { - // "PiecesHashFileUrl": , - // "HashOfHashes": - // } - // } - // } - // }, - // ... - // ] - - // Generating DeliveryOptimizationData to simulate the server response, but it's not being parsed by the Client - - json response; - response = json::array(); - response.push_back({{"Url", "http://localhost/1.json"}, - {"FileId", name + ".json"}, - {"SizeInBytes", 100}, - {"Hashes", {{"Sha1", "123"}, {"Sha256", "456"}}}}); - response[0]["DeliveryOptimization"] = {{"CatalogId", "789"}}; - response[0]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/1.json"}, {"HashOfHashes", "abc"}}}}; - - response.push_back({{"Url", "http://localhost/2.bin"}, - {"FileId", name + ".bin"}, - {"SizeInBytes", 200}, - {"Hashes", {{"Sha1", "421"}, {"Sha256", "132"}}}}); - response[1]["DeliveryOptimization"] = {{"CatalogId", "14"}}; - response[1]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/2.bin"}, {"HashOfHashes", "abcd"}}}}; - return response; -} - -json GeneratePostAppDownloadInfo(const std::string& name) -{ - // [ - // { - // "Url": , - // "FileId": , - // "SizeInBytes": , - // "Hashes": { - // "Sha1": , - // "Sha256": - // }, - // "DeliveryOptimization": { - // "CatalogId": , - // "Properties": { - // "IntegrityCheckInfo": { - // "PiecesHashFileUrl": , - // "HashOfHashes": - // } - // } - // }, - // "ApplicabilityDetails": { - // "Architectures": [ - // "" - // ], - // "PlatformApplicabilityForPackage": [ - // "" - // ] - // }, - // "FileMoniker": "" - // } - // ] - - // Generating DeliveryOptimizationData to simulate the server response, but it's not being parsed by the Client - - json response; - response = json::array(); - response.push_back({{"Url", "http://localhost/1.json"}, - {"FileId", name + ".json"}, - {"SizeInBytes", 100}, - {"Hashes", {{"Sha1", "123"}, {"Sha256", "456"}}}}); - response[0]["DeliveryOptimization"] = {{"CatalogId", "789"}}; - response[0]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/1.json"}, {"HashOfHashes", "abc"}}}}; - response[0]["ApplicabilityDetails"] = {{"Architectures", {"x86"}}, - {"PlatformApplicabilityForPackage", {"Windows"}}}; - response[0]["FileMoniker"] = "1.json"; - - response.push_back({{"Url", "http://localhost/2.bin"}, - {"FileId", name + ".bin"}, - {"SizeInBytes", 200}, - {"Hashes", {{"Sha1", "421"}, {"Sha256", "132"}}}}); - response[1]["DeliveryOptimization"] = {{"CatalogId", "14"}}; - response[1]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/2.bin"}, {"HashOfHashes", "abcd"}}}}; - response[1]["ApplicabilityDetails"] = {{"Architectures", {"amd64"}}, - {"PlatformApplicabilityForPackage", {"Linux"}}}; - response[1]["FileMoniker"] = "2.bin"; - - return response; -} - -void CheckApiVersion(const httplib::Request& req, std::string_view apiVersion) -{ - if (util::AreNotEqualI(req.path_params.at("apiVersion"), apiVersion)) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } -} -} // namespace - -namespace SFS::test::details -{ -class MockWebServerImpl : public BaseServerImpl -{ - public: - MockWebServerImpl() = default; - ~MockWebServerImpl() = default; - - MockWebServerImpl(const MockWebServerImpl&) = delete; - MockWebServerImpl& operator=(const MockWebServerImpl&) = delete; - - void RegisterProduct(std::string&& name, std::string&& version); - void RegisterAppProduct(std::string&& name, std::string&& version, std::vector&& prerequisites); - void RegisterExpectedRequestHeader(std::string&& header, std::string&& value); - void SetForcedHttpErrors(std::queue forcedErrors); - void SetResponseHeaders(std::unordered_map headersByCode); - - private: - void ConfigureRequestHandlers() override; - std::string GetLogIdentifier() override; - - void ConfigurePostLatestVersion(); - void ConfigurePostLatestVersionBatch(); - void ConfigureGetSpecificVersion(); - void ConfigurePostDownloadInfo(); - - void RunHttpCallback(const httplib::Request& req, - httplib::Response& res, - const std::string& methodName, - const std::string& apiVersion, - const std::function& callback); - void CheckRequestHeaders(const httplib::Request& req); - - using VersionList = std::set; - std::unordered_map m_products; - - using AppList = std::set; - std::unordered_map m_appProducts; - - std::unordered_map m_expectedRequestHeaders; - std::queue m_forcedHttpErrors; - std::unordered_map m_headersByCode; -}; -} // namespace SFS::test::details - -MockWebServer::MockWebServer() -{ - m_impl = std::make_unique(); - m_impl->Start(); -} - -MockWebServer::~MockWebServer() -{ - const auto ret = Stop(); - if (!ret) - { - TEST_UNSCOPED_INFO("Failed to stop: " + std::string(ToString(ret.GetCode()))); - } -} - -Result MockWebServer::Stop() -{ - return m_impl->Stop(); -} - -std::string MockWebServer::GetBaseUrl() const -{ - return m_impl->GetUrl(); -} - -void MockWebServer::RegisterProduct(std::string name, std::string version) -{ - m_impl->RegisterProduct(std::move(name), std::move(version)); -} - -void MockWebServer::RegisterAppProduct(std::string name, - std::string version, - std::vector prerequisites) -{ - m_impl->RegisterAppProduct(std::move(name), std::move(version), std::move(prerequisites)); -} - -void MockWebServer::RegisterExpectedRequestHeader(HttpHeader header, std::string value) -{ - std::string headerName = ToString(header); - m_impl->RegisterExpectedRequestHeader(std::move(headerName), std::move(value)); -} - -void MockWebServer::SetForcedHttpErrors(std::queue forcedErrors) -{ - m_impl->SetForcedHttpErrors(std::move(forcedErrors)); -} - -void MockWebServer::SetResponseHeaders(std::unordered_map headersByCode) -{ - m_impl->SetResponseHeaders(std::move(headersByCode)); -} - -void MockWebServerImpl::ConfigureRequestHandlers() -{ - ConfigurePostLatestVersion(); - ConfigurePostLatestVersionBatch(); - ConfigureGetSpecificVersion(); - ConfigurePostDownloadInfo(); -} - -std::string MockWebServerImpl::GetLogIdentifier() -{ - return "MockWebServer"; -} - -void MockWebServerImpl::ConfigurePostLatestVersion() -{ - // Path: /api//contents//namespaces//names//versions/latest?action=select - const std::string pattern = "/api/:apiVersion/contents/:instanceId/namespaces/:ns/names/:name/versions/latest"; - m_server.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) { - RunHttpCallback(req, res, "PostLatestVersion", "v2", [&](const httplib::Request& req, httplib::Response& res) { - // TODO: Ignoring instanceId for now - - if (!req.has_param("action") || util::AreNotEqualI(req.get_param_value("action"), "select")) - { - // TODO: SFS might throw a different error when the query string is unexpected - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - // Checking body has expected format, but won't use it for the response - { - if (req.body.empty()) - { - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - - json body; - try - { - body = json::parse(req.body); - } - catch (const json::parse_error& ex) - { - BUFFER_LOG("JSON parse error: " + std::string(ex.what())); - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - - // The GetLatestVersion API expects an object as a body, with a "TargetingAttributes" object element. - if (!body.is_object() || !body.contains("TargetingAttributes") || - !body["TargetingAttributes"].is_object()) - { - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - } - - const std::string ns = req.path_params.at("ns"); - - json response; - const std::string& name = req.path_params.at("name"); - if (auto it = m_products.find(name); it != m_products.end()) - { - const auto& versions = it->second; - if (versions.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - const auto& latestVersion = *versions.rbegin(); - response = GenerateContentIdJsonObject(name, latestVersion, ns); - } - else if (auto appIt = m_appProducts.find(name); appIt != m_appProducts.end()) - { - const auto& appList = appIt->second; - if (appList.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - const auto& latestApp = *appList.rbegin(); - response = GenerateGetAppVersionJsonObject(name, latestApp, ns); - } - else - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - res.set_content(response.dump(), "application/json"); - }); - }); -} - -void MockWebServerImpl::ConfigurePostLatestVersionBatch() -{ - // Path: /api//contents//namespaces//names?action=BatchUpdates - const std::string pattern = "/api/:apiVersion/contents/:instanceId/namespaces/:ns/names"; - m_server.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) { - RunHttpCallback( - req, - res, - "PostLatestVersionBatch", - "v2", - [&](const httplib::Request& req, httplib::Response& res) { - // TODO: Ignoring instanceId for now - - if (!req.has_param("action") || util::AreNotEqualI(req.get_param_value("action"), "BatchUpdates")) - { - // TODO: SFS might throw a different error when the query string is unexpected - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - if (req.body.empty()) - { - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - - json body; - try - { - body = json::parse(req.body); - } - catch (const json::parse_error& ex) - { - BUFFER_LOG("JSON parse error: " + std::string(ex.what())); - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - - // The BatchUpdates API returns an array of objects, each with a "Product" key. - // If repeated, the same product is only returned once. - // TODO: We are ignoring the TargetingAttributes for now. - if (!body.is_array()) - { - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - - // Iterate over the array and collect the unique products - std::unordered_map requestedProducts; - for (const auto& productRequest : body) - { - if (!productRequest.is_object() || !productRequest.contains("Product") || - !productRequest["Product"].is_string() || !productRequest.contains("TargetingAttributes")) - { - throw StatusCodeException(httplib::StatusCode::BadRequest_400); - } - if (requestedProducts.count(productRequest["Product"])) - { - continue; - } - requestedProducts.emplace(productRequest["Product"], productRequest["TargetingAttributes"]); - } - - // If at least one product exists, we will return a 200 OK with that. Non-existing products are ignored. - // Otherwise, a 404 is sent. - json response = json::array(); - for (const auto& [name, _] : requestedProducts) - { - auto it = m_products.find(name); - if (it == m_products.end()) - { - continue; - } - - const VersionList& versions = it->second; - if (versions.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - const std::string ns = req.path_params.at("ns"); - const auto& latestVersion = *versions.rbegin(); - - response.push_back(GenerateContentIdJsonObject(name, latestVersion, ns)); - } - - if (response.empty()) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - res.set_content(response.dump(), "application/json"); - }); - }); -} - -void MockWebServerImpl::ConfigureGetSpecificVersion() -{ - // Path: /api//contents//namespaces//names//versions/ - const std::string pattern = "/api/:apiVersion/contents/:instanceId/namespaces/:ns/names/:name/versions/:version"; - m_server.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) { - RunHttpCallback(req, res, "GetSpecificVersion", "v2", [&](const httplib::Request& req, httplib::Response& res) { - // TODO: Ignoring instanceId for now - - const std::string& name = req.path_params.at("name"); - auto it = m_products.find(name); - if (it == m_products.end()) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - const VersionList& versions = it->second; - if (versions.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - // TODO: Are apps suppported? - - const std::string& version = req.path_params.at("version"); - if (version.empty() || !versions.count(version)) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - const std::string ns = req.path_params.at("ns"); - - res.set_content(GenerateContentIdJsonObject(name, version, ns).dump(), "application/json"); - }); - }); -} - -void MockWebServerImpl::ConfigurePostDownloadInfo() -{ - // Path: - // /api//contents//namespaces//names//versions//files?action=GenerateDownloadInfo - const std::string pattern = - "/api/:apiVersion/contents/:instanceId/namespaces/:ns/names/:name/versions/:version/files"; - m_server.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) { - RunHttpCallback(req, res, "PostDownloadInfo", "v2", [&](const httplib::Request& req, httplib::Response& res) { - // TODO: Ignoring instanceId and ns for now - - if (!req.has_param("action") || util::AreNotEqualI(req.get_param_value("action"), "GenerateDownloadInfo")) - { - // TODO: SFS might throw a different error when the query string is unexpected - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - const std::string& version = req.path_params.at("version"); - if (version.empty()) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - json response; - const std::string& name = req.path_params.at("name"); - if (auto it = m_products.find(name); it != m_products.end()) - { - const auto& versions = it->second; - if (versions.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - if (!versions.count(version)) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - // Response is a dummy, doesn't use the version above - response = GeneratePostDownloadInfo(name); - } - else if (auto appIt = m_appProducts.find(name); appIt != m_appProducts.end()) - { - const auto& appList = appIt->second; - if (appList.empty()) - { - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - - auto app = std::find_if(appList.begin(), appList.end(), [&](const App& app) { - return app.version == version; - }); - if (app == appList.end()) - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - // Response is a dummy, doesn't use the version above - response = GeneratePostAppDownloadInfo(name); - } - else - { - throw StatusCodeException(httplib::StatusCode::NotFound_404); - } - - res.set_content(response.dump(), "application/json"); - }); - }); -} - -void MockWebServerImpl::RunHttpCallback(const httplib::Request& req, - httplib::Response& res, - const std::string& methodName, - const std::string& apiVersion, - const std::function& callback) -{ - if (m_forcedHttpErrors.size() > 0) - { - res.status = m_forcedHttpErrors.front(); - m_forcedHttpErrors.pop(); - - BUFFER_LOG("Forcing HTTP error: " + std::to_string(res.status)); - } - else - { - try - { - BUFFER_LOG("Matched " + methodName); - CheckApiVersion(req, apiVersion); - CheckRequestHeaders(req); - callback(req, res); - res.status = httplib::StatusCode::OK_200; - } - catch (const StatusCodeException& ex) - { - res.status = ex.GetStatusCode(); - } - catch (const std::exception&) - { - res.status = httplib::StatusCode::InternalServerError_500; - } - catch (...) - { - res.status = httplib::StatusCode::InternalServerError_500; - } - } - - if (m_headersByCode.count(res.status) > 0) - { - BUFFER_LOG("HTTP code " + std::to_string(res.status) + " has response headers to be sent"); - for (const auto& header : m_headersByCode[res.status]) - { - BUFFER_LOG("Adding header [" + header.first + "] with value [" + header.second + "]"); - res.set_header(header.first, header.second); - } - } -} - -void MockWebServerImpl::CheckRequestHeaders(const httplib::Request& req) -{ - for (const auto& header : m_expectedRequestHeaders) - { - std::optional errorMessage; - if (!req.has_header(header.first)) - { - errorMessage = "Expected header [" + header.first + "] not found"; - } - else if (util::AreNotEqualI(req.get_header_value(header.first), header.second)) - { - errorMessage = "Header [" + header.first + "] with value [" + req.get_header_value(header.first) + - "] does not match the expected value [" + header.second + "]"; - } - - if (errorMessage) - { - BUFFER_LOG(*errorMessage); - throw std::runtime_error(errorMessage->c_str()); - } - } -} - -void MockWebServerImpl::RegisterProduct(std::string&& name, std::string&& version) -{ - m_products[std::move(name)].emplace(std::move(version)); -} - -void MockWebServerImpl::RegisterAppProduct(std::string&& name, - std::string&& version, - std::vector&& prerequisites) -{ - for (const auto& prereq : prerequisites) - { - m_appProducts[prereq.name].emplace(App{prereq.version, {}}); - } - m_appProducts[std::move(name)].emplace(App{std::move(version), std::move(prerequisites)}); -} - -void MockWebServerImpl::RegisterExpectedRequestHeader(std::string&& header, std::string&& value) -{ - if (auto it = m_expectedRequestHeaders.find(header); it != m_expectedRequestHeaders.end()) - { - it->second = std::move(value); - return; - } - else - { - m_expectedRequestHeaders.emplace(std::move(header), std::move(value)); - } -} - -void MockWebServerImpl::SetForcedHttpErrors(std::queue forcedErrors) -{ - m_forcedHttpErrors = std::move(forcedErrors); -} - -void MockWebServerImpl::SetResponseHeaders(std::unordered_map headersByCode) -{ - m_headersByCode = std::move(headersByCode); -} diff --git a/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.h b/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.h deleted file mode 100644 index 3c05b3fd6e..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/MockWebServer.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include -#include -#include -#include - -namespace SFS::details -{ -enum class HttpHeader; -} - -namespace SFS::test -{ -namespace details -{ -class MockWebServerImpl; -} - -using HttpCode = int; -using HeaderMap = std::unordered_map; - -struct MockPrerequisite -{ - std::string name; - std::string version; -}; - -class MockWebServer -{ - public: - MockWebServer(); - ~MockWebServer(); - - MockWebServer(const MockWebServer&) = delete; - MockWebServer& operator=(const MockWebServer&) = delete; - - [[nodiscard]] Result Stop(); - - std::string GetBaseUrl() const; - - /// @brief Registers a product with the server. Will fill the other data with gibberish for testing purposes - void RegisterProduct(std::string name, std::string version); - - /// @brief Registers an app with the server. Will fill the other data with gibberish for testing purposes - void RegisterAppProduct(std::string name, std::string version, std::vector prerequisites); - - /// @brief Registers the expectation of a given header to the present in the request - void RegisterExpectedRequestHeader(SFS::details::HttpHeader header, std::string value); - - /** - * @brief Registers a sequence of HTTP error codes that will be sent by the server in the order in which they are - * passed. - */ - void SetForcedHttpErrors(std::queue forcedErrors); - - /// @brief Registers a set of headers that will be sent depending on the HTTP code - void SetResponseHeaders(std::unordered_map headersByCode); - - private: - std::unique_ptr m_impl; -}; -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.cpp b/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.cpp deleted file mode 100644 index 2ee330a338..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ProxyServer.h" - -#include "../util/TestHelper.h" -#include "ErrorHandling.h" -#include "ReportingHandler.h" -#include "ServerCommon.h" -#include "UrlBuilder.h" - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using namespace SFS::test::details; - -namespace SFS::test::details -{ -class ProxyServerImpl : public BaseServerImpl -{ - private: - void ConfigureRequestHandlers() override; - std::string GetLogIdentifier() override; -}; -} // namespace SFS::test::details - -ProxyServer::ProxyServer() -{ - m_impl = std::make_unique(); - m_impl->Start(); -} - -ProxyServer::~ProxyServer() -{ - const auto ret = Stop(); - if (!ret) - { - TEST_UNSCOPED_INFO("Failed to stop: " + std::string(ToString(ret.GetCode()))); - } -} - -Result ProxyServer::Stop() -{ - return m_impl->Stop(); -} - -std::string ProxyServer::GetBaseUrl() const -{ - return m_impl->GetUrl(); -} - -void ProxyServerImpl::ConfigureRequestHandlers() -{ - auto HandleRequest = [&](const httplib::Request& req, httplib::Response& res) { - // As a proxy, we'll parse the URL and Path/Query so we can reuse them in httplib::Client - ReportingHandler handler; - UrlBuilder urlBuilder(req.target.c_str(), handler); - const std::string path = urlBuilder.GetPath(); - const std::string query = urlBuilder.GetQuery(); - urlBuilder.ResetPath().ResetQuery(); - - // URL may come back from UrlBuilder with a final /, which doesn't work with httplib::Client, so we remove it - auto url = urlBuilder.GetUrl(); - if (url.at(url.size() - 1) == '/') - { - url.pop_back(); - } - const std::string pathAndQuery = path + (query.empty() ? "" : ("?" + query)); - httplib::Client cli(url); - httplib::Result result; - if (req.method == "GET") - { - result = cli.Get(pathAndQuery, req.headers); - } - else if (req.method == "POST") - { - const auto length = std::stoi(req.get_header_value("Content-Length")); - result = - cli.Post(pathAndQuery, req.headers, req.body.c_str(), length, req.get_header_value("Content-Type")); - } - - if (!result) - { - BUFFER_LOG("Client error: " + to_string(result.error())); - throw StatusCodeException(httplib::StatusCode::InternalServerError_500); - } - res = result.value(); - }; - - m_server.Get(".*", HandleRequest); - m_server.Post(".*", HandleRequest); -} - -std::string ProxyServerImpl::GetLogIdentifier() -{ - return "ProxyServer"; -} diff --git a/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.h b/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.h deleted file mode 100644 index eb833b2985..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/ProxyServer.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" - -#include - -namespace SFS::test -{ -namespace details -{ -class ProxyServerImpl; -} - -// Proxy Server implementation that redirects GET and POST requests directly -class ProxyServer -{ - public: - ProxyServer(); - ~ProxyServer(); - - ProxyServer(const ProxyServer&) = delete; - ProxyServer& operator=(const ProxyServer&) = delete; - - Result Stop(); - - std::string GetBaseUrl() const; - - private: - std::unique_ptr m_impl; -}; -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.cpp b/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.cpp deleted file mode 100644 index bdf3bb2d3d..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ServerCommon.h" - -#include "../util/TestHelper.h" -#include "Result.h" - -using SFS::test::BufferedLogData; -using SFS::test::StatusCodeException; -using SFS::test::details::BaseServerImpl; -using namespace SFS; -using namespace std::string_literals; - -static constexpr const char* c_listenHostName = "localhost"; - -static std::string ToString(httplib::StatusCode status) -{ - return std::to_string(status) + " " + std::string(httplib::status_message(status)); -} - -StatusCodeException::StatusCodeException(httplib::StatusCode status) : m_status(status), m_message(::ToString(m_status)) -{ -} - -const char* StatusCodeException::what() const noexcept -{ - return m_message.c_str(); -} - -httplib::StatusCode StatusCodeException::GetStatusCode() const -{ - return m_status; -} - -static SFS::LogData ToLogData(const BufferedLogData& data) -{ - return {LogSeverity::Info, data.message.c_str(), data.file.c_str(), data.line, data.function.c_str(), data.time}; -} - -void BaseServerImpl::Start() -{ - ConfigureServerSettings(); - ConfigureRequestHandlers(); - - m_port = m_server.bind_to_any_port(c_listenHostName); - m_listenerThread = std::thread([&]() { m_server.listen_after_bind(); }); -} - -void BaseServerImpl::ConfigureServerSettings() -{ - m_server.set_logger([&](const httplib::Request& req, const httplib::Response& res) { - BUFFER_LOG("Request: " + req.method + " " + req.path + " " + req.version); - BUFFER_LOG("Request Body: " + req.body); - - BUFFER_LOG("Response: " + res.version + " " + ::ToString(static_cast(res.status)) + " " + - res.reason); - BUFFER_LOG("Response body: " + res.body); - }); - - m_server.set_exception_handler([&](const httplib::Request&, httplib::Response& res, std::exception_ptr ep) { - try - { - std::rethrow_exception(ep); - } - catch (std::exception& e) - { - m_lastException = Result(Result::HttpUnexpected, e.what()); - } - catch (...) - { - m_lastException = Result(Result::HttpUnexpected, "Unknown Exception"); - } - - ProcessBufferedLogs(); - - res.status = httplib::StatusCode::InternalServerError_500; - }); - - // Keeping this interval to a minimum ensures tests run quicker - m_server.set_keep_alive_timeout(1); // 1 second -} - -void BaseServerImpl::BufferLog(const BufferedLogData& data) -{ - std::lock_guard guard(m_logMutex); - m_bufferedLog.push_back(data); -} - -BufferedLogData BaseServerImpl::BuildBufferedLogData(const std::string& message, - const char* file, - unsigned line, - const char* function) -{ - return BufferedLogData{GetLogIdentifier() + ": " + message, file, line, function, std::chrono::system_clock::now()}; -} - -void BaseServerImpl::ProcessBufferedLogs() -{ - for (const auto& data : m_bufferedLog) - { - LogCallbackToTest(ToLogData(data)); - } - m_bufferedLog.clear(); -} - -Result BaseServerImpl::Stop() -{ - if (m_listenerThread.joinable()) - { - m_server.stop(); - m_listenerThread.join(); - } - ProcessBufferedLogs(); - return m_lastException.value_or(Result::Success); -} - -std::string BaseServerImpl::GetUrl() const -{ - return "http://"s + c_listenHostName + ":"s + std::to_string(m_port); -} diff --git a/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.h b/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.h deleted file mode 100644 index bd3cca9fcd..0000000000 --- a/src/SfsClient/sfs-client/client/tests/mock/ServerCommon.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Logging.h" -#include "Result.h" - -#include - -#include -#include -#include - -namespace SFS::test -{ -class StatusCodeException : public std::exception -{ - public: - StatusCodeException(httplib::StatusCode status); - - const char* what() const noexcept override; - - httplib::StatusCode GetStatusCode() const; - - private: - httplib::StatusCode m_status; - std::string m_message; -}; - -#define BUILD_BUFFERED_LOG_DATA(message) BuildBufferedLogData(message, __FILE__, __LINE__, __FUNCTION__) - -#define BUFFER_LOG(message) BufferLog(BUILD_BUFFERED_LOG_DATA(message)) - -struct BufferedLogData -{ - std::string message; - std::string file; - unsigned line; - std::string function; - std::chrono::time_point time; -}; - -namespace details -{ -class BaseServerImpl -{ - public: - BaseServerImpl() = default; - ~BaseServerImpl() = default; - - BaseServerImpl(const BaseServerImpl&) = delete; - BaseServerImpl& operator=(const BaseServerImpl&) = delete; - - void Start(); - Result Stop(); - - std::string GetUrl() const; - - protected: - void ConfigureServerSettings(); - virtual void ConfigureRequestHandlers() = 0; - - virtual std::string GetLogIdentifier() = 0; - - void BufferLog(const BufferedLogData& data); - BufferedLogData BuildBufferedLogData(const std::string& message, - const char* file, - unsigned line, - const char* function); - void ProcessBufferedLogs(); - - httplib::Server m_server; - int m_port{-1}; - - std::optional m_lastException; - - std::thread m_listenerThread; - - std::vector m_bufferedLog; - std::mutex m_logMutex; -}; -} // namespace details -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/client/tests/unit/AppContentTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/AppContentTests.cpp deleted file mode 100644 index 6cf452dd4c..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/AppContentTests.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" -#include "sfsclient/AppContent.h" - -#include - -#define TEST(...) TEST_CASE("[AppContentTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[AppContentTests] Scenario: " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetContentId(const std::string& nameSpace, - const std::string& name, - const std::string& version) -{ - std::unique_ptr contentId; - REQUIRE(ContentId::Make(nameSpace, name, version, contentId) == Result::Success); - REQUIRE(contentId != nullptr); - return contentId; -} - -std::unique_ptr GetAppFile(const std::string& fileId, - const std::string& url, - uint64_t sizeInBytes, - const std::unordered_map& hashes, - std::vector architectures, - std::vector platformApplicabilityForPackage, - std::string fileMoniker) -{ - std::unique_ptr file; - REQUIRE(AppFile::Make(fileId, - url, - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker, - file) == Result::Success); - REQUIRE(file != nullptr); - return file; -} - -std::unique_ptr GetPrerequisiteContent(const std::string& contentNameSpace, - const std::string& contentName, - const std::string& contentVersion, - const std::vector& files) -{ - std::unique_ptr contentId = GetContentId(contentNameSpace, contentName, contentVersion); - std::vector clonedFiles; - for (auto& file : files) - { - clonedFiles.push_back(std::move(*GetAppFile(file.GetFileId(), - file.GetUrl(), - file.GetSizeInBytes(), - file.GetHashes(), - file.GetApplicabilityDetails().GetArchitectures(), - file.GetApplicabilityDetails().GetPlatformApplicabilityForPackage(), - file.GetFileMoniker()))); - } - - std::unique_ptr content; - REQUIRE(AppPrerequisiteContent::Make(std::move(contentId), std::move(clonedFiles), content) == Result::Success); - REQUIRE(content != nullptr); - return content; -} - -std::unique_ptr GetAppContent(const std::string& contentNameSpace, - const std::string& contentName, - const std::string& contentVersion, - const std::string& updateId, - const std::vector& prerequisites, - const std::vector& files) -{ - std::unique_ptr contentId = GetContentId(contentNameSpace, contentName, contentVersion); - std::vector clonedPreqs; - for (const auto& prereq : prerequisites) - { - clonedPreqs.push_back(std::move(*GetPrerequisiteContent(prereq.GetContentId().GetNameSpace(), - prereq.GetContentId().GetName(), - prereq.GetContentId().GetVersion(), - prereq.GetFiles()))); - } - - std::vector clonedFiles; - for (auto& file : files) - { - clonedFiles.push_back(std::move(*GetAppFile(file.GetFileId(), - file.GetUrl(), - file.GetSizeInBytes(), - file.GetHashes(), - file.GetApplicabilityDetails().GetArchitectures(), - file.GetApplicabilityDetails().GetPlatformApplicabilityForPackage(), - file.GetFileMoniker()))); - } - - std::unique_ptr appContent; - REQUIRE( - AppContent::Make(std::move(contentId), updateId, std::move(clonedPreqs), std::move(clonedFiles), appContent) == - Result::Success); - REQUIRE(appContent != nullptr); - return appContent; -} -} // namespace - -TEST("Testing AppContent::Make()") -{ - const std::string contentNameSpace{"myNameSpace"}; - const std::string contentName{"myName"}; - const std::string contentVersion{"myVersion"}; - - std::unique_ptr file1 = GetAppFile("fileId1", - "url1", - 1 /*sizeInBytes*/, - {{HashType::Sha1, "sha1"}}, - {Architecture::Amd64}, - {"myPlatformApplicabilityForPackage1"}, - "fileMoniker1"); - std::unique_ptr file2 = GetAppFile("fileId2", - "url2", - 1 /*sizeInBytes*/, - {{HashType::Sha256, "sha256"}}, - {Architecture::Amd64}, - {"myPlatformApplicabilityForPackage1"}, - "fileMoniker1"); - - std::vector files; - files.push_back(std::move(*file1)); - files.push_back(std::move(*file2)); - - std::vector prereqFiles; - prereqFiles.push_back(std::move(*GetAppFile("prereqFileId", - "url", - 1 /*sizeInBytes*/, - {{HashType::Sha1, "sha1"}}, - {Architecture::Amd64}, - {"myPlatformApplicabilityForPackage"}, - "fileMoniker"))); - - std::vector prerequisites; - prerequisites.push_back( - std::move(*GetPrerequisiteContent(contentNameSpace, "prereqName", "prereqVersion", prereqFiles))); - - std::unique_ptr appContent = - GetAppContent(contentNameSpace, contentName, contentVersion, "updateId", prerequisites, files); - REQUIRE(appContent != nullptr); -} - -TEST("Testing AppContent equality operators") -{ - const std::string contentNameSpace{"myNameSpace"}; - const std::string contentName{"myName"}; - const std::string contentVersion{"myVersion"}; - const std::string updateId{"myUpdateId"}; - - std::unique_ptr file = GetAppFile("fileId", - "url", - 1 /*sizeInBytes*/, - {{HashType::Sha1, "sha1"}}, - {Architecture::Amd64}, - {"myPlatformApplicabilityForPackage"}, - "fileMoniker"); - - std::vector files; - files.push_back(std::move(*file)); - - std::vector prereqFiles; - prereqFiles.push_back(std::move(*GetAppFile("prereqFileId", - "url", - 1 /*sizeInBytes*/, - {{HashType::Sha1, "sha1"}}, - {Architecture::Amd64}, - {"myPlatformApplicabilityForPackage"}, - "fileMoniker"))); - - std::vector prerequisites; - prerequisites.push_back( - std::move(*GetPrerequisiteContent(contentNameSpace, "prereqName", "prereqVersion", prereqFiles))); - - const std::unique_ptr content = - GetAppContent(contentNameSpace, contentName, contentVersion, updateId, prerequisites, files); - - SECTION("Equal") - { - auto CompareAppContentEqual = [&content](const std::unique_ptr& sameContent) { - REQUIRE((*content == *sameContent)); - REQUIRE_FALSE((*content != *sameContent)); - }; - - CompareAppContentEqual( - GetAppContent(contentNameSpace, contentName, contentVersion, updateId, prerequisites, files)); - } - - SECTION("Not equal") - { - auto CompareAppContentNotEqual = [&content](const std::unique_ptr& otherContent) { - REQUIRE((*content != *otherContent)); - REQUIRE_FALSE((*content == *otherContent)); - }; - - CompareAppContentNotEqual(GetAppContent("", contentName, contentVersion, updateId, prerequisites, files)); - CompareAppContentNotEqual(GetAppContent(contentNameSpace, "", contentVersion, updateId, prerequisites, files)); - CompareAppContentNotEqual(GetAppContent(contentNameSpace, contentName, "", updateId, prerequisites, files)); - CompareAppContentNotEqual( - GetAppContent(contentNameSpace, contentName, contentVersion, "", prerequisites, files)); - CompareAppContentNotEqual(GetAppContent(contentNameSpace, contentName, contentVersion, updateId, {}, files)); - CompareAppContentNotEqual( - GetAppContent(contentNameSpace, contentName, contentVersion, updateId, prerequisites, {})); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/AppFileTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/AppFileTests.cpp deleted file mode 100644 index 58ecb65dae..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/AppFileTests.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" -#include "sfsclient/AppFile.h" - -#include - -#define TEST(...) TEST_CASE("[AppFileTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[AppFileTests] Scenario: " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetAppFile(const std::string& fileId, - const std::string& url, - uint64_t sizeInBytes, - const std::unordered_map& hashes, - std::vector architectures, - std::vector platformApplicabilityForPackage, - std::string fileMoniker) -{ - std::unique_ptr file; - REQUIRE(AppFile::Make(fileId, - url, - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker, - file) == Result::Success); - REQUIRE(file != nullptr); - return file; -} -} // namespace - -TEST("Testing AppFile::Make()") -{ - const std::string fileId{"myFileId"}; - const std::string url{"myUrl"}; - const uint64_t sizeInBytes{1234}; - const std::unordered_map hashes{{HashType::Sha1, "mySha1"}, {HashType::Sha256, "mySha256"}}; - const std::vector architectures{Architecture::Amd64}; - const std::vector platformApplicabilityForPackage{"myPlatformApplicabilityForPackage"}; - const std::string fileMoniker{"myFileMoniker"}; - - const std::unique_ptr file = - GetAppFile(fileId, url, sizeInBytes, hashes, architectures, platformApplicabilityForPackage, fileMoniker); - - CHECK(fileId == file->GetFileId()); - CHECK(url == file->GetUrl()); - CHECK(sizeInBytes == file->GetSizeInBytes()); - CHECK(hashes == file->GetHashes()); - - SECTION("Testing File equality operators") - { - SECTION("Equal") - { - auto CompareFileEqual = [&file](const std::unique_ptr& sameFile) { - REQUIRE((*file == *sameFile)); - REQUIRE_FALSE((*file != *sameFile)); - }; - - CompareFileEqual(GetAppFile(fileId, - url, - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker)); - } - - SECTION("Not equal") - { - auto CompareFileNotEqual = [&file](const std::unique_ptr& otherFile) { - REQUIRE((*file != *otherFile)); - REQUIRE_FALSE((*file == *otherFile)); - }; - - CompareFileNotEqual( - GetAppFile("", url, sizeInBytes, hashes, architectures, platformApplicabilityForPackage, fileMoniker)); - CompareFileNotEqual(GetAppFile(fileId, - "", - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker)); - CompareFileNotEqual( - GetAppFile(fileId, url, 0, hashes, architectures, platformApplicabilityForPackage, fileMoniker)); - CompareFileNotEqual( - GetAppFile(fileId, url, sizeInBytes, {}, architectures, platformApplicabilityForPackage, fileMoniker)); - CompareFileNotEqual( - GetAppFile(fileId, url, sizeInBytes, hashes, {}, platformApplicabilityForPackage, fileMoniker)); - CompareFileNotEqual(GetAppFile(fileId, url, sizeInBytes, hashes, architectures, {}, fileMoniker)); - CompareFileNotEqual(GetAppFile(fileId, url, sizeInBytes, hashes, {}, platformApplicabilityForPackage, {})); - CompareFileNotEqual(GetAppFile("", "", 0, {}, architectures, platformApplicabilityForPackage, fileMoniker)); - CompareFileNotEqual(GetAppFile("MYFILEID", - url, - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker)); - CompareFileNotEqual(GetAppFile(fileId, - "MYURL", - sizeInBytes, - hashes, - architectures, - platformApplicabilityForPackage, - fileMoniker)); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/ApplicabilityDetailsTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/ApplicabilityDetailsTests.cpp deleted file mode 100644 index 30458d5010..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/ApplicabilityDetailsTests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ApplicabilityDetails.h" -#include "ContentUtil.h" - -#include - -#define TEST(...) TEST_CASE("[ApplicabilityDetailsTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetDetails(const std::vector& architectures, - const std::vector& platformApplicabilityForPackage) -{ - std::unique_ptr details; - REQUIRE(ApplicabilityDetails::Make(architectures, platformApplicabilityForPackage, details) == Result::Success); - REQUIRE(details != nullptr); - return details; -}; -} // namespace - -TEST("Testing ApplicabilityDetails::Make()") -{ - const std::vector architectures{Architecture::x86, Architecture::Amd64}; - const std::vector platformApplicabilityForPackage{"Windows.Desktop", "Windows.Server"}; - const std::string fileMoniker{"myApp"}; - - const std::unique_ptr details = GetDetails(architectures, platformApplicabilityForPackage); - - CHECK(architectures == details->GetArchitectures()); - CHECK(platformApplicabilityForPackage == details->GetPlatformApplicabilityForPackage()); - - SECTION("Testing ApplicabilityDetails equality operators") - { - SECTION("Equal") - { - auto CompareDetailsEqual = [&details](const std::unique_ptr& sameDetails) { - REQUIRE((*details == *sameDetails)); - REQUIRE_FALSE((*details != *sameDetails)); - }; - - CompareDetailsEqual(GetDetails(architectures, platformApplicabilityForPackage)); - } - - SECTION("Not equal") - { - auto CompareDetailsNotEqual = [&details](const std::unique_ptr& otherDetails) { - REQUIRE((*details != *otherDetails)); - REQUIRE_FALSE((*details == *otherDetails)); - }; - - CompareDetailsNotEqual(GetDetails({}, platformApplicabilityForPackage)); - CompareDetailsNotEqual(GetDetails(architectures, {})); - CompareDetailsNotEqual(GetDetails({}, {})); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/ContentIdTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/ContentIdTests.cpp deleted file mode 100644 index 3e76a9171b..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/ContentIdTests.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" -#include "sfsclient/ContentId.h" - -#include - -#define TEST(...) TEST_CASE("[ContentIdTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[ContentIdTests] Scenario: " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetContentId(const std::string& nameSpace, - const std::string& name, - const std::string& version) -{ - std::unique_ptr contentId; - REQUIRE(ContentId::Make(nameSpace, name, version, contentId) == Result::Success); - REQUIRE(contentId != nullptr); - return contentId; -}; -} // namespace - -TEST("Testing ContentId::Make()") -{ - const std::string nameSpace{"myNameSpace"}; - const std::string name{"myName"}; - const std::string version{"myVersion"}; - - const std::unique_ptr contentId = GetContentId(nameSpace, name, version); - - CHECK(nameSpace == contentId->GetNameSpace()); - CHECK(name == contentId->GetName()); - CHECK(version == contentId->GetVersion()); - - SECTION("Testing ContentId equality operators") - { - SECTION("Equal") - { - auto CompareContentIdEqual = [&contentId](const std::unique_ptr& sameContentId) { - REQUIRE((*contentId == *sameContentId)); - REQUIRE_FALSE((*contentId != *sameContentId)); - }; - - CompareContentIdEqual(GetContentId(nameSpace, name, version)); - } - - SECTION("Not equal") - { - auto CompareContentIdNotEqual = [&contentId](const std::unique_ptr& otherContentId) { - REQUIRE((*contentId != *otherContentId)); - REQUIRE_FALSE((*contentId == *otherContentId)); - }; - - CompareContentIdNotEqual(GetContentId("", name, version)); - CompareContentIdNotEqual(GetContentId(nameSpace, "", version)); - CompareContentIdNotEqual(GetContentId(nameSpace, name, "")); - CompareContentIdNotEqual(GetContentId("", "", "")); - CompareContentIdNotEqual(GetContentId("MYNAMESPACE", name, version)); - CompareContentIdNotEqual(GetContentId(nameSpace, "MYNAME", version)); - CompareContentIdNotEqual(GetContentId(nameSpace, name, "MYVERSION")); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/ContentTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/ContentTests.cpp deleted file mode 100644 index 6bfd60494e..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/ContentTests.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" -#include "sfsclient/Content.h" - -#include - -#define TEST(...) TEST_CASE("[ContentTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[ContentTests] Scenario: " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetFile(const std::string& fileId, - const std::string& url, - uint64_t sizeInBytes, - const std::unordered_map& hashes) -{ - std::unique_ptr file; - REQUIRE(File::Make(fileId, url, sizeInBytes, hashes, file) == Result::Success); - REQUIRE(file != nullptr); - return file; -}; - -std::unique_ptr GetContent(const std::string& contentNameSpace, - const std::string& contentName, - const std::string& contentVersion, - const std::vector& files) -{ - std::unique_ptr content; - REQUIRE(Content::Make(contentNameSpace, contentName, contentVersion, files, content) == Result::Success); - REQUIRE(content != nullptr); - return content; -}; -} // namespace - -TEST_SCENARIO("Testing Content::Make()") -{ - GIVEN("Elements that make up a content") - { - const std::string contentNameSpace{"myNameSpace"}; - const std::string contentName{"myName"}; - const std::string contentVersion{"myVersion"}; - - std::unique_ptr file1 = GetFile("fileId1", "url1", 1 /*sizeInBytes*/, {{HashType::Sha1, "sha1"}}); - std::unique_ptr file2 = GetFile("fileId2", "url2", 1 /*sizeInBytes*/, {{HashType::Sha256, "sha256"}}); - - std::vector files; - files.push_back(std::move(*file1)); - files.push_back(std::move(*file2)); - - // Getting raw pointers to check they don't match after copy but match after move - std::vector filePointers; - for (size_t i = 0; i < files.size(); ++i) - { - filePointers.push_back(&files[i]); - } - - WHEN("A Content is created by copying the parameters") - { - std::unique_ptr copiedContent; - REQUIRE(Content::Make(contentNameSpace, contentName, contentVersion, files, copiedContent) == - Result::Success); - REQUIRE(copiedContent != nullptr); - - THEN("The content elements are copies") - { - CHECK(contentNameSpace == copiedContent->GetContentId().GetNameSpace()); - CHECK(contentName == copiedContent->GetContentId().GetName()); - CHECK(contentVersion == copiedContent->GetContentId().GetVersion()); - - // Files were cloned, so the pointers are different, but the contents should be similar - REQUIRE(files.size() == copiedContent->GetFiles().size()); - REQUIRE(filePointers.size() == copiedContent->GetFiles().size()); - for (size_t i = 0; i < files.size(); ++i) - { - // Checking ptrs - REQUIRE(filePointers[i] != &copiedContent->GetFiles()[i]); - REQUIRE(&files[i] != &copiedContent->GetFiles()[i]); - - // Checking contents - CHECK((files[i] == copiedContent->GetFiles()[i])); - } - } - - AND_THEN("Using the Make() that moves the file parameter really moves the parameter") - { - std::unique_ptr movedContent; - REQUIRE(Content::Make(contentNameSpace, contentName, contentVersion, std::move(files), movedContent) == - Result::Success); - REQUIRE(movedContent != nullptr); - - // Checking contents - CHECK((*copiedContent == *movedContent)); - - // Checking underlying pointers are the same since they were moved - REQUIRE(filePointers.size() == movedContent->GetFiles().size()); - REQUIRE(copiedContent->GetFiles().size() == movedContent->GetFiles().size()); - for (size_t i = 0; i < filePointers.size(); ++i) - { - REQUIRE(&copiedContent->GetFiles()[i] != &movedContent->GetFiles()[i]); - REQUIRE(filePointers[i] == &movedContent->GetFiles()[i]); - } - } - } - } -} - -TEST("Testing Content equality operators") -{ - const std::string contentNameSpace{"myNameSpace"}; - const std::string contentName{"myName"}; - const std::string contentVersion{"myVersion"}; - - std::unique_ptr file = GetFile("fileId", "url", 1 /*sizeInBytes*/, {{HashType::Sha1, "sha1"}}); - - std::unique_ptr clonedFile; - REQUIRE(File::Make(file->GetFileId(), file->GetUrl(), file->GetSizeInBytes(), file->GetHashes(), clonedFile) == - Result::Success); - - std::vector files; - files.push_back(std::move(*file)); - - std::vector clonedFiles; - clonedFiles.push_back(std::move(*clonedFile)); - - const std::unique_ptr content = GetContent(contentNameSpace, contentName, contentVersion, files); - - SECTION("Equal") - { - auto CompareContentEqual = [&content](const std::unique_ptr& sameContent) { - REQUIRE((*content == *sameContent)); - REQUIRE_FALSE((*content != *sameContent)); - }; - - CompareContentEqual(GetContent(contentNameSpace, contentName, contentVersion, files)); - CompareContentEqual(GetContent(contentNameSpace, contentName, contentVersion, clonedFiles)); - } - - SECTION("Not equal") - { - auto CompareContentNotEqual = [&content](const std::unique_ptr& otherContent) { - REQUIRE((*content != *otherContent)); - REQUIRE_FALSE((*content == *otherContent)); - }; - - CompareContentNotEqual(GetContent("", contentName, contentVersion, files)); - CompareContentNotEqual(GetContent(contentNameSpace, "", contentVersion, files)); - CompareContentNotEqual(GetContent(contentNameSpace, contentName, "", files)); - CompareContentNotEqual(GetContent(contentNameSpace, contentName, contentVersion, {})); - CompareContentNotEqual(GetContent("", "", "", {})); - CompareContentNotEqual(GetContent("MYNAMESPACE", contentName, contentVersion, files)); - CompareContentNotEqual(GetContent(contentNameSpace, "MYNAME", contentVersion, files)); - CompareContentNotEqual(GetContent(contentNameSpace, contentName, "MYVERSION", files)); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/FileTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/FileTests.cpp deleted file mode 100644 index 73263b7722..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/FileTests.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ContentUtil.h" -#include "sfsclient/File.h" - -#include - -#define TEST(...) TEST_CASE("[FileTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[FileTests] Scenario: " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::details::contentutil; - -namespace -{ -std::unique_ptr GetFile(const std::string& fileId, - const std::string& url, - uint64_t sizeInBytes, - const std::unordered_map& hashes) -{ - std::unique_ptr file; - REQUIRE(File::Make(fileId, url, sizeInBytes, hashes, file) == Result::Success); - REQUIRE(file != nullptr); - return file; -}; -} // namespace - -TEST("Testing File::Make()") -{ - const std::string fileId{"myFileId"}; - const std::string url{"myUrl"}; - const uint64_t sizeInBytes{1234}; - const std::unordered_map hashes{{HashType::Sha1, "mySha1"}, {HashType::Sha256, "mySha256"}}; - - const std::unique_ptr file = GetFile(fileId, url, sizeInBytes, hashes); - - CHECK(fileId == file->GetFileId()); - CHECK(url == file->GetUrl()); - CHECK(sizeInBytes == file->GetSizeInBytes()); - CHECK(hashes == file->GetHashes()); - - SECTION("Testing File equality operators") - { - SECTION("Equal") - { - auto CompareFileEqual = [&file](const std::unique_ptr& sameFile) { - REQUIRE((*file == *sameFile)); - REQUIRE_FALSE((*file != *sameFile)); - }; - - CompareFileEqual(GetFile(fileId, url, sizeInBytes, hashes)); - } - - SECTION("Not equal") - { - auto CompareFileNotEqual = [&file](const std::unique_ptr& otherFile) { - REQUIRE((*file != *otherFile)); - REQUIRE_FALSE((*file == *otherFile)); - }; - - CompareFileNotEqual(GetFile("", url, sizeInBytes, hashes)); - CompareFileNotEqual(GetFile(fileId, "", sizeInBytes, hashes)); - CompareFileNotEqual(GetFile(fileId, url, 0, hashes)); - CompareFileNotEqual(GetFile(fileId, url, sizeInBytes, {})); - CompareFileNotEqual(GetFile("", "", 0, {})); - CompareFileNotEqual(GetFile("MYFILEID", url, sizeInBytes, hashes)); - CompareFileNotEqual(GetFile(fileId, "MYURL", sizeInBytes, hashes)); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/ResultTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/ResultTests.cpp deleted file mode 100644 index aece41bda7..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/ResultTests.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "sfsclient/Result.h" - -#include - -#define TEST(...) TEST_CASE("[ResultTests] " __VA_ARGS__) - -using namespace SFS; - -TEST("Testing Result() class methods") -{ - SECTION("Default constructor") - { - Result resultSuccess(Result::Code::Success); - - REQUIRE(resultSuccess.GetCode() == Result::Code::Success); - REQUIRE(resultSuccess.GetMsg().empty()); - REQUIRE(resultSuccess.IsSuccess()); - REQUIRE_FALSE(resultSuccess.IsFailure()); - - INFO("Comparison operators"); - REQUIRE(resultSuccess == Result::Code::Success); - REQUIRE(resultSuccess == Result::Success); - REQUIRE(resultSuccess != Result::Code::NotSet); - REQUIRE(resultSuccess != Result::NotSet); - REQUIRE_FALSE(resultSuccess == Result::NotSet); - - INFO("bool operator"); - REQUIRE(resultSuccess); - } - - SECTION("Constructor with message") - { - Result resultUnexpected(Result::Code::Unexpected, "message"); - REQUIRE(resultUnexpected.GetCode() == Result::Code::Unexpected); - REQUIRE(resultUnexpected.GetMsg() == "message"); - REQUIRE_FALSE(resultUnexpected.IsSuccess()); - REQUIRE(resultUnexpected.IsFailure()); - - INFO("Comparison operators on constructor with message"); - REQUIRE(resultUnexpected == Result::Code::Unexpected); - REQUIRE(resultUnexpected != Result::Code::NotSet); - - INFO("bool operator"); - REQUIRE_FALSE(resultUnexpected); - } -} - -TEST("Testing ToString(Result)") -{ - REQUIRE(SFS::ToString(Result::Code::Success) == "Success"); - REQUIRE(SFS::ToString(Result::Code::NotImpl) == "NotImpl"); - REQUIRE(SFS::ToString(Result::Code::NotSet) == "NotSet"); - REQUIRE(SFS::ToString(Result::Code::OutOfMemory) == "OutOfMemory"); - REQUIRE(SFS::ToString(Result::Code::Unexpected) == "Unexpected"); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/SFSClientTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/SFSClientTests.cpp deleted file mode 100644 index 33cc0d5cc3..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/SFSClientTests.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "sfsclient/ApplicabilityDetails.h" -#include "sfsclient/SFSClient.h" - -#include - -#include - -#define TEST(...) TEST_CASE("[SFSClientTests] " __VA_ARGS__) -#define TEST_SCENARIO(...) TEST_CASE("[SFSClientTests] Scenario: " __VA_ARGS__) - -using namespace SFS; - -namespace -{ -std::unique_ptr GetSFSClient(std::optional instanceId = std::nullopt) -{ - std::unique_ptr sfsClient; - ClientConfig options; - options.accountId = "testAccountId"; - if (instanceId) - { - options.instanceId = *instanceId; - } - REQUIRE(SFSClient::Make(options, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - return sfsClient; -} - -void TestLoggingCallback(const LogData&) -{ -} - -struct TestLoggingCallbackStruct -{ - static void TestLoggingCallback(const LogData&) - { - } -}; -} // namespace - -static void StaticTestLoggingCallback(const LogData&) -{ -} - -TEST("Testing SFSClient::Make()") -{ -#ifdef __GNUG__ -// For GCC, explicitly turning off "missing-field-initializers" warning as this block is testing the scenario -// in which a user calls explicitly onto the API with field initializers for ClientConfig -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - - const std::string accountId{"testAccountId"}; - const std::string instanceId{"testInstanceId"}; - const std::string nameSpace{"testNameSpace"}; - - std::unique_ptr sfsClient; - - SECTION("Make({accountId}, out)") - { - REQUIRE(SFSClient::Make({accountId}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - - ClientConfig config{accountId}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Make(accountId, instanceId, out)") - { - REQUIRE(SFSClient::Make({accountId, instanceId}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - - ClientConfig config{accountId, instanceId}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Make(accountId, instanceId, namespace, out)") - { - REQUIRE(SFSClient::Make({accountId, instanceId, nameSpace}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - - SECTION("Call make when the pointer is reset") - { - sfsClient.reset(); - REQUIRE(SFSClient::Make({accountId, instanceId, nameSpace}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Call make if the pointer is not reset, as Make() resets it") - { - REQUIRE(SFSClient::Make({accountId, instanceId, nameSpace}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("We can also use a separate ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("We can also move a separate ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace}; - REQUIRE(SFSClient::Make(std::move(config), sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - } - - SECTION("Make(accountId, std::nullopt, nameSpace, out)") - { - REQUIRE(SFSClient::Make({accountId, std::nullopt, nameSpace}, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - - ClientConfig config; - config.accountId = accountId; - config.nameSpace = nameSpace; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Make(accountId, instanceId, namespace, logCallbackFn, out) works") - { - SECTION("Using a lambda with {} initialization") - { - REQUIRE(SFSClient::Make({accountId, instanceId, nameSpace, [](const LogData&) {}}, sfsClient) == - Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Using a lambda with a ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace, [](const LogData&) {}}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Using a nullptr with a ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace, nullptr}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Using a valid empty-namespace function within a ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace, TestLoggingCallback}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Using a valid static function within a ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace, StaticTestLoggingCallback}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Using a valid static member method within a ClientConfig object") - { - ClientConfig config{accountId, instanceId, nameSpace, &TestLoggingCallbackStruct::TestLoggingCallback}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - - SECTION("Can also move a lambda") - { - ClientConfig config{accountId, instanceId, nameSpace, [](const LogData&) {}}; - REQUIRE(SFSClient::Make(std::move(config), sfsClient) == Result::Success); - REQUIRE(sfsClient != nullptr); - } - } - - SECTION("AccountId cannot be empty") - { - REQUIRE(SFSClient::Make({}, sfsClient) == Result::InvalidArg); - REQUIRE(sfsClient == nullptr); - - ClientConfig config; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::InvalidArg); - REQUIRE(sfsClient == nullptr); - - config = {}; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::InvalidArg); - REQUIRE(sfsClient == nullptr); - - config.accountId = std::string(); - REQUIRE(SFSClient::Make(config, sfsClient) == Result::InvalidArg); - REQUIRE(sfsClient == nullptr); - - config.instanceId = instanceId; - REQUIRE(SFSClient::Make(config, sfsClient) == Result::InvalidArg); - REQUIRE(sfsClient == nullptr); - } - -#ifdef __GNUG__ -// For "-Wmissing-field-initializers" -#pragma GCC diagnostic pop -#endif -} - -namespace -{ -void TestProductInRequestParams(const std::function& apiCall, - const std::function& checkContents) -{ - RequestParams params; - SECTION("Product must not be empty") - { - const std::string expectedErrorMsg = "product must not be empty"; - - params.productRequests = {{"", {}}}; - auto result = apiCall(params); - REQUIRE(result.GetCode() == Result::InvalidArg); - REQUIRE(result.GetMsg() == expectedErrorMsg); - checkContents(); - - const TargetingAttributes attributes{{"attr1", "value"}}; - params.productRequests = {{"", attributes}}; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::InvalidArg); - REQUIRE(result.GetMsg() == expectedErrorMsg); - checkContents(); - } - - SECTION("Does not allow an empty request") - { - auto result = apiCall(params); - REQUIRE(result.GetCode() == Result::InvalidArg); - REQUIRE(result.GetMsg() == "productRequests cannot be empty"); - checkContents(); - } - - SECTION("Accepting multiple products is not implemented yet") - { - params.productRequests = {{"p1", {}}, {"p2", {}}}; - auto result = apiCall(params); - REQUIRE(result.GetCode() == Result::NotImpl); - REQUIRE(result.GetMsg() == "There cannot be more than 1 productRequest at the moment"); - checkContents(); - } - - SECTION("Fails if base cv is not correct") - { - params.productRequests = {{"p1", {}}}; - params.baseCV = ""; - auto result = apiCall(params); - REQUIRE(result.GetCode() == Result::InvalidArg); - REQUIRE(result.GetMsg() == "cv must not be empty"); - checkContents(); - - params.baseCV = "cv"; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::InvalidArg); - REQUIRE(result.GetMsg().find("baseCV is not a valid correlation vector:") == 0); - checkContents(); - } - - SECTION("Fails if proxy is setup incorrectly") - { - params.productRequests = {{"p1", {}}}; - params.proxy = "bad://"; - auto result = apiCall(params); - REQUIRE(result.GetCode() == Result::ConnectionUnexpectedError); - REQUIRE(result.GetMsg() == "Unsupported proxy syntax in 'bad://': No host part in the URL"); - checkContents(); - - params.proxy = "bad://bad.com"; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::ConnectionUnexpectedError); - REQUIRE(result.GetMsg() == "Unsupported proxy scheme for 'bad://bad.com'"); - checkContents(); - - params.proxy = ":"; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::ConnectionUnexpectedError); - REQUIRE(result.GetMsg() == - "Unsupported proxy syntax in ':': Port number was not a decimal number between 0 and 65535"); - checkContents(); - - params.proxy = "http://bad:bad"; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::ConnectionUnexpectedError); - REQUIRE( - result.GetMsg() == - "Unsupported proxy syntax in 'http://bad:bad': Port number was not a decimal number between 0 and 65535"); - checkContents(); - - params.proxy = "bad:bad"; - result = apiCall(params); - REQUIRE(result.GetCode() == Result::ConnectionUnexpectedError); - REQUIRE(result.GetMsg() == - "Unsupported proxy syntax in 'bad:bad': Port number was not a decimal number between 0 and 65535"); - checkContents(); - } -} -} // namespace - -TEST("Testing SFSClient::GetLatestDownloadInfo()") -{ - auto sfsClient = GetSFSClient(); - std::vector contents; - - TestProductInRequestParams( - [&](const RequestParams& params) { return sfsClient->GetLatestDownloadInfo(params, contents); }, - [&contents] { REQUIRE(contents.empty()); }); -} - -TEST("Testing SFSClient::GetAppLatestDownloadInfo()") -{ - SECTION("With storeapps instance") - { - auto sfsClient = GetSFSClient("storeapps"); - std::vector contents; - - TestProductInRequestParams( - [&](const RequestParams& params) { return sfsClient->GetLatestAppDownloadInfo(params, contents); }, - [&contents] { REQUIRE(contents.empty()); }); - } - - SECTION("Fails if not storeapps instanceId") - { - auto sfsClient = GetSFSClient("testInstanceId"); - std::vector contents; - RequestParams params; - params.productRequests = {{"a", {}}}; - auto result = sfsClient->GetLatestAppDownloadInfo(params, contents); - REQUIRE(result.GetCode() == Result::Unexpected); - REQUIRE(result.GetMsg() == "At this moment only the \"storeapps\" instanceId can send app requests"); - REQUIRE(contents.empty()); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionManagerTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionManagerTests.cpp deleted file mode 100644 index ed739e1d57..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionManagerTests.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ReportingHandler.h" -#include "connection/Connection.h" -#include "connection/CurlConnection.h" -#include "connection/CurlConnectionManager.h" - -#include -#include - -using namespace SFS; -using namespace SFS::details; - -#define TEST(...) TEST_CASE("[CurlConnectionManagerTests] " __VA_ARGS__) - -TEST("Testing expected values in curl_version_info_data") -{ - curl_version_info_data* ver = curl_version_info(CURLVERSION_NOW); - REQUIRE(ver != nullptr); - CHECK(ver->features & CURL_VERSION_SSL); - - // Checking thread safety is on - CHECK(ver->features & CURL_VERSION_THREADSAFE); - - // For thread safety we need the DNS resolutions to be asynchronous (which happens because of c-ares) - CHECK(ver->features & CURL_VERSION_ASYNCHDNS); -} - -TEST("Testing CurlConnectionManager()") -{ - ReportingHandler handler; - CurlConnectionManager curlConnectionManager(handler); - - // Check that the CurlConnectionManager generates a CurlConnection object - std::unique_ptr Connection = curlConnectionManager.MakeConnection({}); - REQUIRE(Connection != nullptr); - REQUIRE(dynamic_cast(Connection.get()) != nullptr); - - // Having many CurlConnectionManager objects should not cause any issues, curl is smart enough to - // handle multiple initialization and cleanup calls - CurlConnectionManager curlConnectionManager2(handler); - auto Connection2 = curlConnectionManager2.MakeConnection({}); - - CurlConnectionManager curlConnectionManager3(handler); - auto Connection3 = curlConnectionManager3.MakeConnection({}); - auto Connection4 = curlConnectionManager3.MakeConnection({}); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionTests.cpp deleted file mode 100644 index 2915371ff1..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/CurlConnectionTests.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "Result.h" -#include "connection/CurlConnection.h" -#include "connection/mock/MockConnection.h" - -#include -#include -#include - -#define TEST(...) TEST_CASE("[CurlConnectionTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using json = nlohmann::json; - -namespace -{ -class MockCurlConnection : public CurlConnection -{ - public: - MockCurlConnection(const ReportingHandler& handler, Result::Code& responseCode, std::string& response) - : CurlConnection({}, handler) - , m_responseCode(responseCode) - , m_response(response) - { - } - - protected: - std::string CurlPerform(const std::string&, CurlHeaderList&) override - { - if (m_responseCode == Result::Success) - { - return m_response; - } - throw SFSException(m_responseCode); - } - - private: - Result::Code& m_responseCode; - std::string& m_response; -}; -} // namespace - -TEST("Testing CurlConnection()") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - Result::Code responseCode = Result::HttpNotFound; - std::string response = "expected"; - std::unique_ptr connection = std::make_unique(handler, responseCode, response); - - SECTION("Testing CurlConnection::Get()") - { - std::string out; - REQUIRE_THROWS_CODE(out = connection->Get("url"), HttpNotFound); - REQUIRE(out.empty()); - - responseCode = Result::Success; - REQUIRE_NOTHROW(out = connection->Get("url")); - REQUIRE(out == response); - } - - SECTION("Testing CurlConnection::Post()") - { - std::string out; - REQUIRE_THROWS_CODE(out = connection->Post("url"), HttpNotFound); - REQUIRE(out.empty()); - - const json body = {{{"dummy", {}}}}; - REQUIRE_THROWS_CODE(out = connection->Post("url", body.dump()), HttpNotFound); - REQUIRE(out.empty()); - - responseCode = Result::Success; - REQUIRE_NOTHROW(out = connection->Get("url")); - REQUIRE(out == response); - - response.clear(); - REQUIRE_NOTHROW(out = connection->Post("url", body.dump())); - REQUIRE(out == response); - } -} - -TEST("Testing CurlConnection constructor passing a cv") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - ConnectionConfig config; - config.baseCV = ""; - - REQUIRE_THROWS_CODE_MSG(std::make_unique(config, handler), InvalidArg, "cv must not be empty"); - - config.baseCV = "cv"; - REQUIRE_THROWS_CODE_MSG_MATCHES(std::make_unique(config, handler), - InvalidArg, - Catch::Matchers::ContainsSubstring("baseCV is not a valid correlation vector:")); - - config.baseCV = "aaaaaaaaaaaaaaaa.1"; - REQUIRE_NOTHROW(std::make_unique(config, handler)); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/EnvTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/EnvTests.cpp deleted file mode 100644 index 1d136adffe..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/EnvTests.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Env.h" - -#include - -#define TEST(...) TEST_CASE("[EnvTests] " __VA_ARGS__) - -using namespace SFS::details::env; - -TEST("Testing GetEnv()") -{ - SECTION("Testing GetEnv() on an existing environment variable") - { - // Get the value of an existing environment variable per platform -#ifdef _WIN32 - const std::string varName = "COMPUTERNAME"; -#else - const std::string varName = "LANG"; -#endif - auto env = GetEnv(varName); - REQUIRE(env.has_value()); - REQUIRE(env.value().size() > 0); - } - - SECTION("Testing GetEnv() on a non-existing variable") - { - auto env = GetEnv("DUMMYVARIABLESHOULDNOTEXIST"); - REQUIRE(!env.has_value()); - } -} - -TEST("Testing SetEnv()") -{ - SECTION("Testing SetEnv() on a non-existing variable") - { - auto env = GetEnv("DUMMYVARIABLE"); - REQUIRE(!env.has_value()); - - REQUIRE(SetEnv("DUMMYVARIABLE", "dummyValue")); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - - SECTION("Testing SetEnv() on an existing variable overwrites it") - { - REQUIRE(SetEnv("DUMMYVARIABLE", "dummyValue2")); - - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue2"); - } - - INFO("Unsetting the environment variable"); - REQUIRE(UnsetEnv("DUMMYVARIABLE")); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(!env.has_value()); - } - - SECTION("Testing SetEnv() with empty strings fails") - { - REQUIRE_FALSE(SetEnv("DUMMYVARIABLE", "")); - REQUIRE_FALSE(SetEnv("DUMMYVARIABLE", std::string())); - REQUIRE_FALSE(SetEnv("", "dummy")); - REQUIRE_FALSE(SetEnv(std::string(), "dummy")); - REQUIRE_FALSE(SetEnv(std::string(), std::string())); - } -} - -TEST("Testing UnsetEnv()") -{ - SECTION("Testing UnsetEnv() on a non-existing variable still succeeds") - { - auto env = GetEnv("DUMMYVARIABLE"); - REQUIRE(!env.has_value()); - - REQUIRE(UnsetEnv("DUMMYVARIABLE")); - } -} - -TEST("Testing ScopedEnv") -{ - SECTION("Testing ScopedEnv on a non-existing variable") - { - auto env = GetEnv("DUMMYVARIABLE"); - REQUIRE(!env.has_value()); - - { - ScopedEnv scopedEnv("DUMMYVARIABLE", "dummyValue"); - - INFO("Variable exists within scope"); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - - SECTION("Testing ScopedEnv on an existing variable overwrites it") - { - { - ScopedEnv scopedEnv2("DUMMYVARIABLE", "dummyValue2"); - - INFO("Variable has value overwritten within scope"); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue2"); - } - - INFO("Variable goes back to previous value"); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - } - } - - INFO("Variable should be unset after the scope ends"); - env = GetEnv("DUMMYVARIABLE"); - REQUIRE(!env.has_value()); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/ErrorHandlingTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/ErrorHandlingTests.cpp deleted file mode 100644 index 6b8b9ef88f..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/ErrorHandlingTests.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ErrorHandling.h" -#include "ReportingHandler.h" -#include "Result.h" -#include "SFSException.h" - -#include - -#define TEST(...) TEST_CASE("[ErrorHandlingTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; - -namespace -{ -Result TestSFS_Catch_Return_bad_alloc() -try -{ - throw std::bad_alloc(); -} -SFS_CATCH_RETURN(); - -class MyException : public std::exception -{ -}; - -Result TestSFS_Catch_Return_std_exception() -try -{ - throw MyException(); -} -SFS_CATCH_RETURN(); - -Result TestSFS_Catch_Return_unknown() -try -{ - throw std::error_code(); -} -SFS_CATCH_RETURN(); -} // namespace - -TEST("Testing ErrorHandling's SFS_CATCH_RETURN()") -{ - REQUIRE(TestSFS_Catch_Return_bad_alloc() == Result::OutOfMemory); - REQUIRE(TestSFS_Catch_Return_std_exception() == Result::Unexpected); - REQUIRE(TestSFS_Catch_Return_unknown() == Result::Unexpected); -} - -namespace -{ -void TestSFS_Catch_Log_Rethrow_bad_alloc(const ReportingHandler& handler) -try -{ - throw std::bad_alloc(); -} -SFS_CATCH_LOG_RETHROW(handler); - -void TestSFS_Catch_Log_Rethrow_SFSException(const ReportingHandler& handler) -try -{ - throw SFS::details::SFSException(Result::Unexpected); -} -SFS_CATCH_LOG_RETHROW(handler); -} // namespace - -TEST("Testing ErrorHandling's SFS_CATCH_LOG_RETHROW()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that SFS_CATCH_LOG_RETHROW does not rethrow if not SFSException") - { - REQUIRE_THROWS_AS(TestSFS_Catch_Log_Rethrow_bad_alloc(handler), std::bad_alloc); - REQUIRE(!called); - } - - SECTION("Test that SFS_CATCH_LOG_RETHROW rethrows and logs if SFSException") - { - REQUIRE_THROWS_AS(TestSFS_Catch_Log_Rethrow_SFSException(handler), SFSException); - REQUIRE(called); - } -} - -namespace -{ -Result TestSFS_ReturnIfFailed(const Result& result, const Result& ifNotFailed = Result::Success) -{ - RETURN_IF_FAILED(result); - return ifNotFailed; -} -} // namespace - -TEST("Testing ErrorHandling's RETURN_IF_FAILED()") -{ - SECTION("Test that RETURN_IF_FAILED returns the result if it is a failure") - { - REQUIRE(TestSFS_ReturnIfFailed(Result::Code::Unexpected) == Result::Code::Unexpected); - } - - SECTION("Test that RETURN_IF_FAILED does not return if the result is a success") - { - REQUIRE(TestSFS_ReturnIfFailed(Result::Code::Success) == Result::Code::Success); - REQUIRE(TestSFS_ReturnIfFailed(Result::Code::Success, Result::Code::Unexpected) == Result::Code::Unexpected); - } -} - -namespace -{ -Result TestSFS_ReturnIfFailedLog(const ReportingHandler& handler, - const Result& result, - const Result& ifNotFailed = Result::Success) -{ - RETURN_IF_FAILED_LOG(result, handler); - return ifNotFailed; -} -} // namespace - -TEST("Testing ErrorHandling's RETURN_IF_FAILED_LOG()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that RETURN_IF_FAILED_LOG returns and logs if the result is a failure") - { - REQUIRE(TestSFS_ReturnIfFailedLog(handler, Result::Code::Unexpected) == Result::Code::Unexpected); - REQUIRE(called); - } - - SECTION("Test that RETURN_IF_FAILED_LOG does not return and log if the result is a success") - { - REQUIRE(TestSFS_ReturnIfFailedLog(handler, Result::Code::Success) == Result::Code::Success); - REQUIRE(!called); - } -} - -TEST("Testing ErrorHandling's LOG_IF_FAILED()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that LOG_IF_FAILED logs if the result is a failure") - { - LOG_IF_FAILED(Result(Result::Unexpected), handler); - REQUIRE(called); - } - - SECTION("Test that LOG_IF_FAILED does not log if the result is a success") - { - LOG_IF_FAILED(Result(Result::Success), handler); - REQUIRE(!called); - } -} - -namespace -{ -void TestSFS_ThrowLog(const ReportingHandler& handler, const Result& result) -{ - THROW_LOG(result, handler); -} -} // namespace - -TEST("Testing ErrorHandling's THROW_LOG()") -{ - INFO("Test that ErrorHandling's THROW_LOG throws and logs the result"); - - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - REQUIRE_THROWS_AS(TestSFS_ThrowLog(handler, Result::Code::Unexpected), SFSException); - REQUIRE(called); -} - -namespace -{ -void TestSFS_ThrowIfFailedLog(const ReportingHandler& handler, const Result& result) -{ - THROW_IF_FAILED_LOG(result, handler); -} -} // namespace - -TEST("Testing ErrorHandling's THROW_IF_FAILED_LOG()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that ErrorHandling's THROW_IF_FAILED_LOG throws and logs the result if it is a failure") - { - REQUIRE_THROWS_AS(TestSFS_ThrowIfFailedLog(handler, Result::Code::Unexpected), SFSException); - REQUIRE(called); - } - - SECTION("Test that ErrorHandling's THROW_IF_FAILED_LOG does not throw and log the result if it is not a failure") - { - REQUIRE_NOTHROW(TestSFS_ThrowIfFailedLog(handler, Result::Code::Success)); - REQUIRE(!called); - } -} - -TEST("Testing ErrorHandling's THROW_CODE_IF()") -{ - SECTION("Test that THROW_CODE_IF throws if the condition is true") - { - REQUIRE_THROWS_AS([]() { THROW_CODE_IF(Unexpected, true); }(), SFSException); - } - - SECTION("Test that THROW_CODE_IF does not throw if the condition is false") - { - REQUIRE_NOTHROW([]() { THROW_CODE_IF(Unexpected, false); }()); - } -} - -TEST("Testing ErrorHandling's THROW_CODE_IF_LOG()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that THROW_CODE_IF_LOG throws and logs the result if the condition is true") - { - REQUIRE_THROWS_AS([&handler]() { THROW_CODE_IF_LOG(Unexpected, true, handler); }(), SFSException); - REQUIRE(called); - } - - SECTION("Test that THROW_CODE_IF_LOG does not throw and log if the condition is false") - { - REQUIRE_NOTHROW([&handler]() { THROW_CODE_IF_LOG(Unexpected, false, handler); }()); - REQUIRE(!called); - } -} - -TEST("Testing ErrorHandling's THROW_CODE_IF_NOT_LOG()") -{ - ReportingHandler handler; - bool called = false; - handler.SetLoggingCallback([&](const auto&) { called = true; }); - - SECTION("Test that THROW_CODE_IF_NOT_LOG throws and logs the result if the condition is false") - { - REQUIRE_THROWS_AS([&handler]() { THROW_CODE_IF_NOT_LOG(Unexpected, false, handler); }(), SFSException); - REQUIRE(called); - } - - SECTION("Test that THROW_CODE_IF_NOT_LOG does not throw and log if the condition is true") - { - REQUIRE_NOTHROW([&handler]() { THROW_CODE_IF_NOT_LOG(Unexpected, true, handler); }()); - REQUIRE(!called); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/ReportingHandlerTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/ReportingHandlerTests.cpp deleted file mode 100644 index 72c7b17893..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/ReportingHandlerTests.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "ReportingHandler.h" - -#include - -#include -#include -#include -#include - -#define TEST(...) TEST_CASE("[ReportingHandlerTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; - -TEST("Testing SetLoggingCallback()") -{ - ReportingHandler handler; - - bool called = false; - auto handling = [&](const LogData&) { called = true; }; - - handler.SetLoggingCallback(handling); - REQUIRE_FALSE(called); - - LOG_INFO(handler, "Test"); - - called = false; - handler.SetLoggingCallback(nullptr); - - LOG_INFO(handler, "Test"); - REQUIRE_FALSE(called); -} - -TEST("Testing Severities") -{ - ReportingHandler handler; - - std::optional severity; - auto handling = [&](const LogData& data) { severity = data.severity; }; - - handler.SetLoggingCallback(handling); - - REQUIRE(!severity.has_value()); - - LOG_INFO(handler, "Test"); - REQUIRE(severity.has_value()); - REQUIRE(*severity == LogSeverity::Info); - severity.reset(); - - LOG_WARNING(handler, "Test"); - REQUIRE(severity.has_value()); - REQUIRE(*severity == LogSeverity::Warning); - severity.reset(); - - LOG_ERROR(handler, "Test"); - REQUIRE(severity.has_value()); - REQUIRE(*severity == LogSeverity::Error); - severity.reset(); - - LOG_VERBOSE(handler, "Test"); - REQUIRE(severity.has_value()); - REQUIRE(*severity == LogSeverity::Verbose); - - handler.SetLoggingCallback(nullptr); -} - -TEST("Testing file/line/function") -{ - ReportingHandler handler; - - std::string file; - int line = 0; - std::string function; - auto handling = [&](const LogData& data) { - file = std::string(data.file); - line = data.line; - function = std::string(data.function); - }; - - handler.SetLoggingCallback(handling); - - LOG_INFO(handler, "Test"); - CHECK(file.find("ReportingHandlerTests.cpp") != std::string::npos); - CHECK(line == (__LINE__ - 2)); - CHECK(function == "CATCH2_INTERNAL_TEST_4"); - - LOG_WARNING(handler, "Test"); - CHECK(file.find("ReportingHandlerTests.cpp") != std::string::npos); - CHECK(line == (__LINE__ - 2)); - CHECK(function == "CATCH2_INTERNAL_TEST_4"); -} - -TEST("Testing LogFormatting") -{ - ReportingHandler handler; - - std::string message; - auto handling = [&](const LogData& data) { message = data.message; }; - - handler.SetLoggingCallback(handling); - - REQUIRE(message.empty()); - - LOG_INFO(handler, "Test %s", "Test"); - REQUIRE(message == "Test Test"); - - LOG_WARNING(handler, "Test %s %s", "Test1", "Test2"); - REQUIRE(message == "Test Test1 Test2"); - - LOG_ERROR(handler, "Test %s %s %s", "Test1", "Test2", "Test3"); - REQUIRE(message == "Test Test1 Test2 Test3"); - - LOG_INFO(handler, "Test %d %d", 1, true); - REQUIRE(message == "Test 1 1"); - - LOG_INFO(handler, "Test %d %s", 2, false ? "true" : "false"); - REQUIRE(message == "Test 2 false"); - - handler.SetLoggingCallback(nullptr); -} - -TEST("Testing setting another logging callback waits for an existing call to finish") -{ - ReportingHandler handler; - - // Set a callback that will be blocked by a mutex - std::mutex mutex; - bool called = false; - std::chrono::time_point time1; - bool startedCall = false; - auto handling = [&](const LogData& logData) { - startedCall = true; - std::lock_guard guard(mutex); - called = true; - time1 = logData.time; - }; - - handler.SetLoggingCallback(handling); - - // Make sure the callback is blocked - std::unique_lock lock(mutex); - - // Spawn a thread that will be blocked by the callback - std::thread t([&]() { LOG_INFO(handler, "Test"); }); - - LoggingCallbackFn anotherHandling; - SECTION("Setting another callback") - { - anotherHandling = [&](const LogData&) {}; - } - SECTION("Setting a nullptr callback") - { - anotherHandling = nullptr; - } - - // Spawn a second thread that tries to set another callback - std::chrono::time_point time2; - std::thread t2([&]() { - // Make sure the callback has started and is now blocked - while (!startedCall) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - // Now setting another callback should be blocked until we unlock the mutex - handler.SetLoggingCallback(std::move(anotherHandling)); - INFO("The first callback should have been called at this point"); - REQUIRE(called); - time2 = std::chrono::system_clock::now(); - }); - - // Unlocking the mutex will allow the threads to continue - lock.unlock(); - t.join(); - t2.join(); - - INFO("The first callback should have been called before the second callback was set"); - REQUIRE(time1 < time2); -} - -TEST("Testing ToString(LogSeverity)") -{ - REQUIRE(SFS::ToString(LogSeverity::Info) == "Info"); - REQUIRE(SFS::ToString(LogSeverity::Warning) == "Warning"); - REQUIRE(SFS::ToString(LogSeverity::Error) == "Error"); - REQUIRE(SFS::ToString(LogSeverity::Verbose) == "Verbose"); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/SFSClientImplTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/SFSClientImplTests.cpp deleted file mode 100644 index 8e2ed24f62..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/SFSClientImplTests.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "SFSClientImpl.h" -#include "TestOverride.h" -#include "connection/Connection.h" -#include "connection/ConnectionManager.h" -#include "connection/CurlConnection.h" -#include "connection/CurlConnectionManager.h" -#include "connection/mock/MockConnectionManager.h" - -#include -#include - -#define TEST(...) TEST_CASE("[SFSClientImplTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using json = nlohmann::json; - -namespace -{ -class MockCurlConnection : public CurlConnection -{ - public: - MockCurlConnection(const ReportingHandler& handler, - Result::Code& responseCode, - std::string& getResponse, - std::string& postResponse, - bool& expectEmptyPostBody) - : CurlConnection({}, handler) - , m_responseCode(responseCode) - , m_getResponse(getResponse) - , m_postResponse(postResponse) - , m_expectEmptyPostBody(expectEmptyPostBody) - { - } - - std::string Get(const std::string&) override - { - if (m_responseCode == Result::Success) - { - INFO("MockCurlConnection::Get() called, response: " << m_getResponse); - return m_getResponse; - } - throw SFSException(m_responseCode); - } - - std::string Post(const std::string&, const std::string& data) override - { - if (m_responseCode == Result::Success) - { - INFO("MockCurlConnection::Post() called, response: " << m_postResponse); - if (m_expectEmptyPostBody) - { - REQUIRE(data.empty()); - } - else - { - REQUIRE(!data.empty()); - } - return m_postResponse; - } - throw SFSException(m_responseCode); - } - - private: - Result::Code& m_responseCode; - std::string& m_getResponse; - std::string& m_postResponse; - bool& m_expectEmptyPostBody; -}; - -void CheckProduct(const VersionEntity& entity, std::string_view ns, std::string_view name, std::string_view version) -{ - REQUIRE(entity.GetContentType() == ContentType::Generic); - REQUIRE(entity.contentId.nameSpace == ns); - REQUIRE(entity.contentId.name == name); - REQUIRE(entity.contentId.version == version); -} - -void CheckDownloadInfo(const FileEntities& files, const std::string& name) -{ - REQUIRE(files.size() == 2); - REQUIRE(files[0]->fileId == (name + ".json")); - REQUIRE(files[0]->url == ("http://localhost/1.json")); - REQUIRE(files[1]->fileId == (name + ".bin")); - REQUIRE(files[1]->url == ("http://localhost/2.bin")); -} -} // namespace - -TEST("Testing class SFSClientImpl()") -{ - const std::string ns = "testNameSpace"; - SFSClientImpl sfsClient({"testAccountId", "testInstanceId", ns, LogCallbackToTest}); - - Result::Code responseCode = Result::Success; - std::string getResponse; - std::string postResponse; - bool expectEmptyPostBody = true; - std::unique_ptr connection = std::make_unique(sfsClient.GetReportingHandler(), - responseCode, - getResponse, - postResponse, - expectEmptyPostBody); - - const std::string productName = "productName"; - const std::string expectedVersion = "0.0.0.2"; - - SECTION("Testing SFSClientImpl::GetLatestVersion()") - { - expectEmptyPostBody = false; - std::unique_ptr entity; - - SECTION("Expected response") - { - const json latestVersionResponse = { - {"ContentId", {{"Namespace", ns}, {"Name", productName}, {"Version", expectedVersion}}}}; - postResponse = latestVersionResponse.dump(); - SECTION("No attributes") - { - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({productName, {}}, *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, productName, expectedVersion); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_NOTHROW(entity = sfsClient.GetLatestVersion({productName, attributes}, *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, productName, expectedVersion); - } - - SECTION("Failing") - { - responseCode = Result::HttpNotFound; - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", {}}, *connection), HttpNotFound); - REQUIRE(!entity); - - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_THROWS_CODE(entity = sfsClient.GetLatestVersion({"badName", attributes}, *connection), - HttpNotFound); - REQUIRE(!entity); - } - } - - SECTION("Unexpected response") - { - SECTION("Wrong ns") - { - const json latestVersionResponse = { - {"ContentId", {{"Namespace", "wrong"}, {"Name", productName}, {"Version", expectedVersion}}}}; - postResponse = latestVersionResponse.dump(); - } - - SECTION("Wrong name") - { - const json latestVersionResponse = { - {"ContentId", {{"Namespace", ns}, {"Name", "wrong"}, {"Version", expectedVersion}}}}; - postResponse = latestVersionResponse.dump(); - } - - REQUIRE_THROWS_CODE_MSG(entity = sfsClient.GetLatestVersion({productName, {}}, *connection), - ServiceInvalidResponse, - "Response does not match the requested product"); - REQUIRE(!entity); - } - } - - SECTION("Testing SFSClientImpl::GetLatestVersionBatch()") - { - expectEmptyPostBody = false; - json latestVersionResponse = json::array(); - latestVersionResponse.push_back( - {{"ContentId", {{"Namespace", ns}, {"Name", productName}, {"Version", expectedVersion}}}}); - postResponse = latestVersionResponse.dump(); - VersionEntities entities; - SECTION("No attributes") - { - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{productName, {}}}, *connection)); - REQUIRE(!entities.empty()); - CheckProduct(*entities[0], ns, productName, expectedVersion); - } - - SECTION("With attributes") - { - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_NOTHROW(entities = sfsClient.GetLatestVersionBatch({{productName, attributes}}, *connection)); - REQUIRE(!entities.empty()); - CheckProduct(*entities[0], ns, productName, expectedVersion); - } - - SECTION("Failing") - { - responseCode = Result::HttpNotFound; - REQUIRE_THROWS_CODE(entities = sfsClient.GetLatestVersionBatch({{"badName", {}}}, *connection), - HttpNotFound); - - const TargetingAttributes attributes{{"attr1", "value"}}; - REQUIRE_THROWS_CODE(entities = sfsClient.GetLatestVersionBatch({{"badName", attributes}}, *connection), - HttpNotFound); - } - } - - SECTION("Testing SFSClientImpl::GetSpecificVersion()") - { - json specificVersionResponse; - specificVersionResponse["ContentId"] = {{"Namespace", ns}, {"Name", productName}, {"Version", expectedVersion}}; - specificVersionResponse["Files"] = json::array({productName + ".json", productName + ".bin"}); - getResponse = specificVersionResponse.dump(); - std::unique_ptr entity; - SECTION("Getting version") - { - REQUIRE_NOTHROW(entity = sfsClient.GetSpecificVersion(productName, expectedVersion, *connection)); - REQUIRE(entity); - CheckProduct(*entity, ns, productName, expectedVersion); - } - - SECTION("Failing") - { - responseCode = Result::HttpNotFound; - REQUIRE_THROWS_CODE(entity = sfsClient.GetSpecificVersion(productName, expectedVersion, *connection), - HttpNotFound); - } - } - - SECTION("Testing SFSClientImpl::GetDownloadInfo()") - { - expectEmptyPostBody = true; - json downloadInfoResponse; - downloadInfoResponse = json::array(); - downloadInfoResponse.push_back({{"Url", "http://localhost/1.json"}, - {"FileId", productName + ".json"}, - {"SizeInBytes", 100}, - {"Hashes", {{"Sha1", "123"}, {"Sha256", "456"}}}}); - downloadInfoResponse[0]["DeliveryOptimization"] = {{"CatalogId", "789"}}; - downloadInfoResponse[0]["DeliveryOptimization"]["Properties"] = { - {"IntegrityCheckInfo", {{"PiecesHashFileUrl", "http://localhost/1.json"}, {"HashOfHashes", "abc"}}}}; - - downloadInfoResponse.push_back({{"Url", "http://localhost/2.bin"}, - {"FileId", productName + ".bin"}, - {"SizeInBytes", 200}, - {"Hashes", {{"Sha1", "421"}, {"Sha256", "132"}}}}); - downloadInfoResponse[1]["DeliveryOptimization"] = downloadInfoResponse[0]["DeliveryOptimization"]; - postResponse = downloadInfoResponse.dump(); - - FileEntities files; - SECTION("Getting version") - { - REQUIRE_NOTHROW(files = sfsClient.GetDownloadInfo(productName, expectedVersion, *connection)); - REQUIRE(!files.empty()); - CheckDownloadInfo(files, productName); - } - - SECTION("Failing") - { - responseCode = Result::HttpNotFound; - REQUIRE_THROWS_CODE(files = sfsClient.GetDownloadInfo(productName, expectedVersion, *connection), - HttpNotFound); - REQUIRE(files.empty()); - } - } -} - -TEST("Testing ClientConfig validation") -{ - SECTION("accountId must not be empty") - { - const std::string expectedErrorMsg = "ClientConfig::accountId must not be empty"; - for (size_t i = 0; i <= 10; ++i) - { - ClientConfig config; - config.accountId = std::string(i, 'a'); - if (i >= 1) - { - REQUIRE_NOTHROW(SFSClientImpl(std::move(config))); - } - else - { - REQUIRE_THROWS_CODE_MSG(SFSClientImpl(std::move(config)), - InvalidArg, - expectedErrorMsg); - } - } - } - - SECTION("instanceId must not be empty") - { - const std::string expectedErrorMsg = "ClientConfig::instanceId must not be empty"; - for (size_t i = 0; i <= 10; ++i) - { - ClientConfig config; - config.accountId = "testAccountId"; - config.instanceId = std::string(i, 'a'); - if (i >= 1) - { - REQUIRE_NOTHROW(SFSClientImpl(std::move(config))); - } - else - { - REQUIRE_THROWS_CODE_MSG(SFSClientImpl(std::move(config)), - InvalidArg, - expectedErrorMsg); - } - } - } - - SECTION("NameSpace must not be empty") - { - const std::string expectedErrorMsg = "ClientConfig::nameSpace must not be empty"; - for (size_t i = 0; i <= 10; ++i) - { - ClientConfig config; - config.accountId = "testAccountId"; - config.nameSpace = std::string(i, 'a'); - if (i >= 1) - { - REQUIRE_NOTHROW(SFSClientImpl(std::move(config))); - } - else - { - REQUIRE_THROWS_CODE_MSG(SFSClientImpl(std::move(config)), - InvalidArg, - expectedErrorMsg); - } - } - } -} - -TEST("Testing SFSClientImpl::SetCustomBaseUrl()") -{ - ClientConfig config; - config.accountId = "testAccountId"; - SFSClientImpl sfsClient(std::move(config)); - - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "https://testAccountId.api.cdp.microsoft.com/"); - - sfsClient.SetCustomBaseUrl("customUrl"); - REQUIRE_THROWS_CODE_MSG(sfsClient.MakeUrlBuilder().GetUrl(), - ConnectionUrlSetupFailed, - "Curl URL error: Bad scheme"); - - sfsClient.SetCustomBaseUrl("http://customUrl.com/"); - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://customUrl.com/"); -} - -TEST("Testing test override SFS_TEST_OVERRIDE_BASE_URL") -{ - ClientConfig config; - config.accountId = "testAccountId"; - SFSClientImpl sfsClient(std::move(config)); - - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "https://testAccountId.api.cdp.microsoft.com/"); - - { - INFO("Can override the base url with the test key"); - ScopedTestOverride override(TestOverride::BaseUrl, "http://override.com"); - if (AreTestOverridesAllowed()) - { - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://override.com/"); - } - else - { - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "https://testAccountId.api.cdp.microsoft.com/"); - } - } - - if (AreTestOverridesAllowed()) - { - INFO("Fails if the override is not a valid URL"); - ScopedTestOverride override(TestOverride::BaseUrl, "override"); - { - REQUIRE_THROWS_CODE_MSG(sfsClient.MakeUrlBuilder().GetUrl(), - ConnectionUrlSetupFailed, - "Curl URL error: Bad scheme"); - } - } - - INFO("Override is unset after ScopedEnv goes out of scope"); - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "https://testAccountId.api.cdp.microsoft.com/"); - - sfsClient.SetCustomBaseUrl("http://customUrl.com"); - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://customUrl.com/"); - - { - INFO("Can also override a custom base base url with the test key"); - ScopedTestOverride override(TestOverride::BaseUrl, "http://override.com"); - if (AreTestOverridesAllowed()) - { - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://override.com/"); - } - else - { - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://customUrl.com/"); - } - } - - REQUIRE(sfsClient.MakeUrlBuilder().GetUrl() == "http://customUrl.com/"); -} - -TEST("Testing passing a logging callback to constructor of SFSClientImpl") -{ - SFSClientImpl sfsClient( - {"testAccountId", "testInstanceId", "testNameSpace", [](const LogData&) {}}); - SFSClientImpl sfsClient2({"testAccountId", "testInstanceId", "testNameSpace", nullptr}); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/SFSUrlBuilderTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/SFSUrlBuilderTests.cpp deleted file mode 100644 index 7e4810545c..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/SFSUrlBuilderTests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "SFSUrlBuilder.h" - -#include - -#define TEST(...) TEST_CASE("[SFSUrlBuilderTests] " __VA_ARGS__) - -using namespace SFS::details; -using namespace SFS::test; - -const std::string c_accountId = "accountId"; -const std::string c_instanceId = "instanceId"; -const std::string c_nameSpace = "nameSpace"; - -TEST("SFSUrlBuilder") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - SECTION("CreateFromAccountId()") - { - SECTION("ASCII strings") - { - SFSUrlBuilder builder(c_accountId, c_instanceId, c_nameSpace, handler); - REQUIRE(builder.GetUrl() == "https://accountId.api.cdp.microsoft.com/"); - - REQUIRE( - builder.GetLatestVersionUrl("product") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId/namespaces/nameSpace/names/product/versions/latest?action=select"); - - REQUIRE( - builder.GetLatestVersionBatchUrl() == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId/namespaces/nameSpace/names?action=BatchUpdates"); - - REQUIRE( - builder.GetSpecificVersionUrl("product", "version") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId/namespaces/nameSpace/names/product/versions/version"); - - REQUIRE( - builder.GetDownloadInfoUrl("product", "version") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId/namespaces/nameSpace/names/product/versions/version/files?action=GenerateDownloadInfo"); - } - - SECTION("Non-ASCII strings") - { - REQUIRE_THROWS_CODE_MSG(SFSUrlBuilder("a&b", c_instanceId, c_nameSpace, handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - - REQUIRE_THROWS_CODE_MSG(SFSUrlBuilder("a\nb", c_instanceId, c_nameSpace, handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - - REQUIRE_THROWS_CODE_MSG(SFSUrlBuilder("a\tb", c_instanceId, c_nameSpace, handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - - SFSUrlBuilder builder(c_accountId, "instanceId@", "namespace+", handler); - REQUIRE(builder.GetUrl() == "https://accountId.api.cdp.microsoft.com/"); - - REQUIRE( - builder.GetLatestVersionUrl("pr$duct") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId%40/namespaces/namespace%2b/names/pr%24duct/versions/latest?action=select"); - - REQUIRE( - builder.GetLatestVersionBatchUrl() == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId%40/namespaces/namespace%2b/names?action=BatchUpdates"); - - REQUIRE( - builder.GetSpecificVersionUrl("pr$duct", "versi/n") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId%40/namespaces/namespace%2b/names/pr%24duct/versions/versi%2fn"); - - REQUIRE( - builder.GetDownloadInfoUrl("pr$duct", "versi/n") == - "https://accountId.api.cdp.microsoft.com/api/v2/contents/instanceId%40/namespaces/namespace%2b/names/pr%24duct/versions/versi%2fn/files?action=GenerateDownloadInfo"); - } - } - - SECTION("CreateFromCustomUrl()") - { - SFSUrlBuilder builder(SFSCustomUrl("http://www.example.com"), c_instanceId, c_nameSpace, handler); - REQUIRE(builder.GetUrl() == "http://www.example.com/"); - - SFSUrlBuilder builder2(SFSCustomUrl("http://www.example2.com"), c_instanceId, c_nameSpace, handler); - REQUIRE(builder2.GetUrl() == "http://www.example2.com/"); - - REQUIRE_THROWS_CODE_MSG(SFSUrlBuilder(SFSCustomUrl("http://www.+.com"), c_instanceId, c_nameSpace, handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - - REQUIRE_THROWS_CODE_MSG(SFSUrlBuilder(SFSCustomUrl("example"), c_instanceId, c_nameSpace, handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad scheme"); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/TestOverrideTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/TestOverrideTests.cpp deleted file mode 100644 index a2b710e63c..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/TestOverrideTests.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Env.h" -#include "TestOverride.h" - -#include - -#define TEST(...) TEST_CASE("[TestOverrideTests] " __VA_ARGS__) - -using namespace SFS::test; -using namespace SFS::details::env; - -TEST("Testing AreTestOverridesAllowed()") -{ - bool areTestOverridesAllowed = AreTestOverridesAllowed(); -#ifdef SFS_ENABLE_TEST_OVERRIDES - REQUIRE(areTestOverridesAllowed); -#else - REQUIRE_FALSE(areTestOverridesAllowed); -#endif -} - -TEST("Testing GetEnvVarNameFromOverride") -{ - REQUIRE(GetEnvVarNameFromOverride(TestOverride::BaseUrl) == "SFS_TEST_OVERRIDE_BASE_URL"); -} - -TEST("Testing GetTestOverride()") -{ - if (AreTestOverridesAllowed()) - { - SECTION("Testing GetTestOverride() on a non-existing environment variable") - { - auto env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(!env.has_value()); - - SECTION("Testing GetTestOverride() on an existing environment variable") - { - const std::string varName = GetEnvVarNameFromOverride(TestOverride::BaseUrl); - REQUIRE(SetEnv(varName, "override")); - - env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(env.has_value()); - REQUIRE(*env == "override"); - - INFO("Unsetting the environment variable"); - REQUIRE(UnsetEnv(varName)); - } - } - } - else - { - SECTION("GetTestOverride() returns std::nullopt when test overrides are not allowed") - { - auto env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(!env.has_value()); - } - } -} - -TEST("Testing ScopedTestOverride") -{ - SECTION("Testing ScopedTestOverride on a non-existing override") - { - const std::string varName = GetEnvVarNameFromOverride(TestOverride::BaseUrl); - auto env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(!env.has_value()); - - { - ScopedTestOverride scopedOverride(TestOverride::BaseUrl, "dummyValue"); - - INFO("Variable exists within scope"); - env = GetEnv(varName); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - - if (AreTestOverridesAllowed()) - { - INFO("Checking test override within scope"); - env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - } - - SECTION("Testing ScopedEnv on an existing variable overwrites it") - { - { - ScopedTestOverride scopedOverride2(TestOverride::BaseUrl, "dummyValue2"); - - INFO("Variable has value overwritten within scope"); - env = GetEnv(varName); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue2"); - - if (AreTestOverridesAllowed()) - { - INFO("Checking test override has been overwritten within scope"); - env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue2"); - } - } - - INFO("Variable goes back to previous value"); - env = GetEnv(varName); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - - if (AreTestOverridesAllowed()) - { - INFO("Checking test override has gone back to previous value"); - env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(env.has_value()); - REQUIRE(*env == "dummyValue"); - } - } - } - - INFO("Variable should be unset after the scope ends"); - env = GetEnv(varName); - REQUIRE(!env.has_value()); - - if (AreTestOverridesAllowed()) - { - INFO("Test override should be unset after the scope ends"); - env = GetTestOverride(TestOverride::BaseUrl); - REQUIRE(!env.has_value()); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/UrlBuilderTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/UrlBuilderTests.cpp deleted file mode 100644 index d61336bec5..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/UrlBuilderTests.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../util/SFSExceptionMatcher.h" -#include "../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "UrlBuilder.h" - -#include - -#define TEST(...) TEST_CASE("[UrlBuilderTests] " __VA_ARGS__) - -using namespace SFS::details; -using namespace SFS::test; - -TEST("UrlBuilder") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - UrlBuilder builder(handler); - REQUIRE_THROWS_CODE_MSG(builder.GetUrl(), ConnectionUrlSetupFailed, "Curl URL error: No host part in the URL"); - - builder.SetHost("www.example.com"); - REQUIRE_THROWS_CODE_MSG(builder.GetUrl(), ConnectionUrlSetupFailed, "Curl URL error: No scheme part in the URL"); - - builder.SetScheme(Scheme::Https); - REQUIRE(builder.GetUrl() == "https://www.example.com/"); - - SECTION("SetHost") - { - builder.SetHost("www.example2.com"); - REQUIRE(builder.GetUrl() == "https://www.example2.com/"); - - REQUIRE_THROWS_CODE_MSG(builder.SetHost("+"), ConnectionUrlSetupFailed, "Curl URL error: Bad hostname"); - REQUIRE_THROWS_CODE_MSG(builder.SetHost("a&b"), ConnectionUrlSetupFailed, "Curl URL error: Bad hostname"); - REQUIRE_THROWS_CODE_MSG(builder.SetHost("a\nb"), ConnectionUrlSetupFailed, "Curl URL error: Bad hostname"); - REQUIRE_THROWS_CODE_MSG(builder.SetHost("a\tb"), ConnectionUrlSetupFailed, "Curl URL error: Bad hostname"); - - REQUIRE_THROWS_CODE_MSG(builder.SetHost(""), InvalidArg, "Host must not empty"); - } - - SECTION("SetPath") - { - builder.SetPath("index.html"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html"); - - REQUIRE_THROWS_CODE_MSG(builder.SetPath(""), InvalidArg, "Path must not empty"); - - builder.ResetPath(); - REQUIRE(builder.GetUrl() == "https://www.example.com/"); - } - - SECTION("AppendPath") - { - builder.SetPath("index.html"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html"); - - builder.AppendPathEncoded("index.html"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html/index.html"); - - builder.AppendPath("a/"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html/index.html/a/"); - - builder.AppendPath("b/"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html/index.html/a/b/"); - - INFO("Encoding for append includes the / character"); - builder.AppendPathEncoded("c/"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html/index.html/a/b/c%2f"); - - INFO("Calling SetPath() resets the path"); - builder.SetPath("index"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index"); - - REQUIRE_THROWS_CODE_MSG(builder.AppendPath(""), InvalidArg, "Path must not empty"); - } - - SECTION("SetQuery, AppendQuery") - { - builder.SetQuery("key", "value"); - REQUIRE(builder.GetUrl() == "https://www.example.com/?key=value"); - - builder.AppendQuery("key2", "value2"); - REQUIRE(builder.GetUrl() == "https://www.example.com/?key=value&key2=value2"); - - builder.SetQuery("key2", "value2"); - REQUIRE(builder.GetUrl() == "https://www.example.com/?key2=value2"); - - builder.AppendQuery("key3", "valu/e@2"); - REQUIRE(builder.GetUrl() == "https://www.example.com/?key2=value2&key3=valu%2fe%402"); - - builder.SetQuery("ke$y4", "valu/e@3"); - REQUIRE(builder.GetUrl() == "https://www.example.com/?ke%24y4=valu%2fe%403"); - - REQUIRE_THROWS_CODE_MSG(builder.SetQuery("", "value"), InvalidArg, "Query key and value must not empty"); - REQUIRE_THROWS_CODE_MSG(builder.SetQuery("key", ""), InvalidArg, "Query key and value must not empty"); - REQUIRE_THROWS_CODE_MSG(builder.SetQuery("", ""), InvalidArg, "Query key and value must not empty"); - - REQUIRE_THROWS_CODE_MSG(builder.AppendQuery("", "value"), InvalidArg, "Query key and value must not empty"); - REQUIRE_THROWS_CODE_MSG(builder.AppendQuery("key", ""), InvalidArg, "Query key and value must not empty"); - REQUIRE_THROWS_CODE_MSG(builder.AppendQuery("", ""), InvalidArg, "Query key and value must not empty"); - - builder.ResetQuery(); - REQUIRE(builder.GetUrl() == "https://www.example.com/"); - } - - SECTION("SetUrl") - { - builder.SetUrl("https://www.example.com/index.html?key=value"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html?key=value"); - REQUIRE_THROWS_CODE_MSG(builder.SetUrl("https://www.+.com"), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - - REQUIRE_THROWS_CODE_MSG(builder.SetUrl(""), InvalidArg, "Url must not empty"); - } - - SECTION("SetScheme, SetHost, SetPath, SetQuery") - { - builder.SetScheme(Scheme::Https).SetHost("www.example.com").SetPath("index.html").SetQuery("key", "value"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index.html?key=value"); - } - - SECTION("SetScheme, SetHost, AppendPathEncoded, SetQuery") - { - builder.SetScheme(Scheme::Https) - .SetHost("www.example.com") - .AppendPathEncoded("index@.html") - .SetQuery("key", "value"); - REQUIRE(builder.GetUrl() == "https://www.example.com/index%40.html?key=value"); - } - - SECTION("Constructor with URL") - { - UrlBuilder builder2("https://www.example.com/index.html?key=value", handler); - REQUIRE(builder2.GetUrl() == "https://www.example.com/index.html?key=value"); - - REQUIRE_THROWS_CODE_MSG(UrlBuilder("https://www.+.com", handler), - ConnectionUrlSetupFailed, - "Curl URL error: Bad hostname"); - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/UtilTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/UtilTests.cpp deleted file mode 100644 index a91e0a1e7e..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/UtilTests.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "Util.h" - -#include - -#define TEST(...) TEST_CASE("[UtilTests] " __VA_ARGS__) - -using namespace SFS::details::util; - -TEST("Testing AreEqualI") -{ - SECTION("ASCII strings") - { - REQUIRE(AreEqualI("abc", "abc")); - REQUIRE(AreEqualI("abc", "ABC")); - REQUIRE(AreEqualI("abc", "Abc")); - REQUIRE(AreEqualI("abc", "aBc")); - REQUIRE(AreEqualI("abc", "abC")); - REQUIRE(AreEqualI("abc", "ABC")); - REQUIRE(AreEqualI("abc", "AbC")); - REQUIRE(AreEqualI("abc", "aBC")); - } -} - -TEST("Testing AreNotEqualI") -{ - REQUIRE(AreNotEqualI("abc", "ab")); - REQUIRE(AreNotEqualI("ab", "abc")); - REQUIRE(AreNotEqualI("abc", "abd")); -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/entity/FileEntityTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/entity/FileEntityTests.cpp deleted file mode 100644 index 84ea724cf2..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/entity/FileEntityTests.cpp +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../../util/SFSExceptionMatcher.h" -#include "../../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "entity/FileEntity.h" -#include "sfsclient/AppFile.h" -#include "sfsclient/File.h" - -#include -#include - -#define TEST(...) TEST_CASE("[FileEntityTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using json = nlohmann::json; - -// GenericFileEntity constants -const std::string c_fileId = "fileId"; -const std::string c_url = "url"; -const uint64_t c_size = 123; -const std::string c_sha1 = "sha1"; -const std::string c_sha256 = "sha256"; - -// AppFileEntity constants -const std::string c_fileMoniker = "fileMoniker"; -const std::string c_arch = "amd64"; -const std::string c_applicability = "app"; -ApplicabilityDetailsEntity c_appDetailsEntity{{c_arch}, {c_applicability}}; - -TEST("Testing FileEntity::FromJson()") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - SECTION("Generic File Entity") - { - std::unique_ptr entity; - SECTION("Correct") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - - REQUIRE_NOTHROW(entity = FileEntity::FromJson(fileEntity, handler)); - REQUIRE(entity != nullptr); - REQUIRE(entity->GetContentType() == ContentType::Generic); - REQUIRE(entity->fileId == c_fileId); - REQUIRE(entity->url == c_url); - REQUIRE(entity->sizeInBytes == c_size); - REQUIRE(entity->hashes.size() == 2); - REQUIRE(entity->hashes.at("Sha1") == c_sha1); - REQUIRE(entity->hashes.at("Sha256") == c_sha256); - } - - SECTION("Missing fields") - { - SECTION("Missing FileId") - { - const json fileEntity = {{"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.FileId in response"); - } - - SECTION("Missing Url") - { - const json fileEntity = {{"FileId", c_fileId}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.Url in response"); - } - - SECTION("Missing SizeInBytes") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.SizeInBytes in response"); - } - - SECTION("Missing Hashes") - { - const json fileEntity = {{"FileId", c_fileId}, {"Url", c_url}, {"SizeInBytes", c_size}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.Hashes in response"); - } - } - - SECTION("Wrong types") - { - SECTION("FileId") - { - const json fileEntity = {{"FileId", 1}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.FileId is not a string"); - } - - SECTION("Url") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", 1}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.Url is not a string"); - } - - SECTION("SizeInBytes") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", "size"}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.SizeInBytes is not an unsigned number"); - } - - SECTION("Hashes not an object") - { - const json fileEntity = {{"FileId", c_fileId}, {"Url", c_url}, {"SizeInBytes", c_size}, {"Hashes", 1}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.Hashes is not an object"); - } - - SECTION("Hashes.Sha1") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", 1}}}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.Hashes object value is not a string"); - } - } - } - - SECTION("App File Entity") - { - std::unique_ptr entity; - const json applicabilityDetails = {{"Architectures", json::array({c_arch})}, - {"PlatformApplicabilityForPackage", json::array({c_applicability})}}; - - SECTION("Correct") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", applicabilityDetails}}; - - REQUIRE_NOTHROW(entity = FileEntity::FromJson(fileEntity, handler)); - REQUIRE(entity != nullptr); - REQUIRE(entity->GetContentType() == ContentType::App); - REQUIRE(entity->fileId == c_fileId); - REQUIRE(entity->url == c_url); - REQUIRE(entity->sizeInBytes == c_size); - REQUIRE(entity->hashes.size() == 2); - REQUIRE(entity->hashes.at("Sha1") == c_sha1); - REQUIRE(entity->hashes.at("Sha256") == c_sha256); - - AppFileEntity* appEntity = dynamic_cast(entity.get()); - REQUIRE(appEntity->fileMoniker == c_fileMoniker); - REQUIRE(appEntity->applicabilityDetails.architectures.size() == 1); - REQUIRE(appEntity->applicabilityDetails.architectures[0] == c_arch); - REQUIRE(appEntity->applicabilityDetails.platformApplicabilityForPackage.size() == 1); - REQUIRE(appEntity->applicabilityDetails.platformApplicabilityForPackage[0] == c_applicability); - } - - SECTION("Missing fields") - { - SECTION("Missing ApplicabilityDetails") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.ApplicabilityDetails in response"); - } - - SECTION("Missing ApplicabilityDetails.Architectures") - { - const json wrongApplicabilityDetails = { - {"PlatformApplicabilityForPackage", json::array({c_applicability})}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.ApplicabilityDetails.Architectures in response"); - } - - SECTION("Missing ApplicabilityDetails.PlatformApplicabilityForPackage") - { - const json wrongApplicabilityDetails = {{"Architectures", json::array({c_arch})}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG( - FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "Missing File.ApplicabilityDetails.PlatformApplicabilityForPackage in response"); - } - } - - SECTION("Wrong types") - { - SECTION("FileMoniker") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", 1}, - {"ApplicabilityDetails", applicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.FileMoniker is not a string"); - } - - SECTION("ApplicabilityDetails") - { - SECTION("Not an object") - { - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", c_fileId}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.ApplicabilityDetails is not an object"); - } - - SECTION("Architectures is not an array") - { - const json wrongApplicabilityDetails = { - {"Architectures", c_fileId}, - {"PlatformApplicabilityForPackage", json::array({c_applicability})}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.ApplicabilityDetails.Architectures is not an array"); - } - - SECTION("Architectures array value is not a string") - { - const json wrongApplicabilityDetails = { - {"Architectures", json::array({1})}, - {"PlatformApplicabilityForPackage", json::array({c_applicability})}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG(FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.ApplicabilityDetails.Architectures array value is not a string"); - } - - SECTION("PlatformApplicabilityForPackage is not an array") - { - const json wrongApplicabilityDetails = {{"Architectures", json::array({c_arch})}, - {"PlatformApplicabilityForPackage", c_fileId}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG( - FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.ApplicabilityDetails.PlatformApplicabilityForPackage is not an array"); - } - - SECTION("PlatformApplicabilityForPackage array value is not a string") - { - const json wrongApplicabilityDetails = {{"Architectures", json::array({c_arch})}, - {"PlatformApplicabilityForPackage", json::array({1})}}; - const json fileEntity = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", wrongApplicabilityDetails}}; - REQUIRE_THROWS_CODE_MSG( - FileEntity::FromJson(fileEntity, handler), - ServiceInvalidResponse, - "File.ApplicabilityDetails.PlatformApplicabilityForPackage array value is not a string"); - } - } - } - } -} - -TEST("Testing GenericFileEntity conversions") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - auto CheckFile = [&](const File& file) { - REQUIRE(file.GetFileId() == c_fileId); - REQUIRE(file.GetUrl() == c_url); - REQUIRE(file.GetSizeInBytes() == c_size); - REQUIRE(file.GetHashes().size() == 2); - REQUIRE(file.GetHashes().at(HashType::Sha1) == c_sha1); - REQUIRE(file.GetHashes().at(HashType::Sha256) == c_sha256); - }; - - SECTION("GenericFileEntity::ToFile()") - { - SECTION("Success") - { - std::unique_ptr entity = std::make_unique(); - entity->fileId = c_fileId; - entity->url = c_url; - entity->sizeInBytes = c_size; - entity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - auto file = GenericFileEntity::ToFile(std::move(*entity), handler); - CheckFile(*file); - } - - SECTION("Wrong type") - { - std::unique_ptr wrongEntity = std::make_unique(); - wrongEntity->fileId = c_fileId; - wrongEntity->url = c_url; - wrongEntity->sizeInBytes = c_size; - wrongEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - REQUIRE_THROWS_CODE_MSG( - GenericFileEntity::ToFile(std::move(*wrongEntity), handler), - ServiceUnexpectedContentType, - R"(The service returned file "fileId" with content type [App] while the expected type was [Generic])"); - } - } - - SECTION("GenericFileEntity::FileEntitiesToFileVector()") - { - SECTION("Success") - { - std::unique_ptr entity = std::make_unique(); - entity->fileId = c_fileId; - entity->url = c_url; - entity->sizeInBytes = c_size; - entity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - FileEntities entities; - entities.push_back(std::move(entity)); - - std::vector files; - SECTION("1 file") - { - REQUIRE_NOTHROW(files = GenericFileEntity::FileEntitiesToFileVector(std::move(entities), handler)); - REQUIRE(files.size() == 1); - CheckFile(files[0]); - } - - SECTION("2 files") - { - std::unique_ptr entity2 = std::make_unique(); - entity2->fileId = c_fileId; - entity2->url = c_url; - entity2->sizeInBytes = c_size; - entity2->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - entities.push_back(std::move(entity2)); - REQUIRE_NOTHROW(files = GenericFileEntity::FileEntitiesToFileVector(std::move(entities), handler)); - REQUIRE(files.size() == 2); - CheckFile(files[0]); - CheckFile(files[1]); - } - } - - SECTION("Wrong type, fails") - { - std::unique_ptr wrongEntity = std::make_unique(); - wrongEntity->fileId = c_fileId; - wrongEntity->url = c_url; - wrongEntity->sizeInBytes = c_size; - wrongEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - FileEntities wrongEntities; - wrongEntities.push_back(std::move(wrongEntity)); - REQUIRE_THROWS_CODE_MSG( - GenericFileEntity::FileEntitiesToFileVector(std::move(wrongEntities), handler), - ServiceUnexpectedContentType, - R"(The service returned file "fileId" with content type [App] while the expected type was [Generic])"); - } - } -} - -TEST("Testing AppFileEntity conversions") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - auto CheckAppFile = [&](const AppFile& file) { - REQUIRE(file.GetFileId() == c_fileId); - REQUIRE(file.GetUrl() == c_url); - REQUIRE(file.GetSizeInBytes() == c_size); - REQUIRE(file.GetHashes().size() == 2); - REQUIRE(file.GetHashes().at(HashType::Sha1) == c_sha1); - REQUIRE(file.GetHashes().at(HashType::Sha256) == c_sha256); - REQUIRE(file.GetFileMoniker() == c_fileMoniker); - REQUIRE(file.GetApplicabilityDetails().GetArchitectures().size() == 1); - REQUIRE(file.GetApplicabilityDetails().GetArchitectures()[0] == Architecture::Amd64); - REQUIRE(file.GetApplicabilityDetails().GetPlatformApplicabilityForPackage().size() == 1); - REQUIRE(file.GetApplicabilityDetails().GetPlatformApplicabilityForPackage()[0] == c_applicability); - }; - - SECTION("AppFileEntity::ToAppFile()") - { - SECTION("Success") - { - std::unique_ptr entity = std::make_unique(); - auto appEntity = dynamic_cast(entity.get()); - appEntity->fileId = c_fileId; - appEntity->url = c_url; - appEntity->sizeInBytes = c_size; - appEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - appEntity->fileMoniker = c_fileMoniker; - appEntity->applicabilityDetails = c_appDetailsEntity; - - auto file = AppFileEntity::ToAppFile(std::move(*entity), handler); - CheckAppFile(*file); - } - - SECTION("Wrong type") - { - std::unique_ptr wrongEntity = std::make_unique(); - wrongEntity->fileId = c_fileId; - wrongEntity->url = c_url; - wrongEntity->sizeInBytes = c_size; - wrongEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - REQUIRE_THROWS_CODE_MSG( - AppFileEntity::ToAppFile(std::move(*wrongEntity), handler), - ServiceUnexpectedContentType, - R"(The service returned file "fileId" with content type [Generic] while the expected type was [App])"); - } - } - - SECTION("Testing AppFileEntity::FileEntitiesToAppFileVector()") - { - SECTION("Success") - { - std::unique_ptr entity = std::make_unique(); - auto appEntity = dynamic_cast(entity.get()); - appEntity->fileId = c_fileId; - appEntity->url = c_url; - appEntity->sizeInBytes = c_size; - appEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - appEntity->fileMoniker = c_fileMoniker; - appEntity->applicabilityDetails = c_appDetailsEntity; - - FileEntities entities; - entities.push_back(std::move(entity)); - - std::vector files; - SECTION("1 file") - { - REQUIRE_NOTHROW(files = AppFileEntity::FileEntitiesToAppFileVector(std::move(entities), handler)); - REQUIRE(files.size() == 1); - CheckAppFile(files[0]); - } - - SECTION("2 files") - { - std::unique_ptr entity2 = std::make_unique(); - auto appEntity2 = dynamic_cast(entity2.get()); - appEntity2->fileId = c_fileId; - appEntity2->url = c_url; - appEntity2->sizeInBytes = c_size; - appEntity2->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - appEntity2->fileMoniker = c_fileMoniker; - appEntity2->applicabilityDetails = c_appDetailsEntity; - - entities.push_back(std::move(entity2)); - REQUIRE_NOTHROW(files = AppFileEntity::FileEntitiesToAppFileVector(std::move(entities), handler)); - REQUIRE(files.size() == 2); - CheckAppFile(files[0]); - CheckAppFile(files[1]); - } - } - - SECTION("Wrong type, fails") - { - std::unique_ptr wrongEntity = std::make_unique(); - wrongEntity->fileId = c_fileId; - wrongEntity->url = c_url; - wrongEntity->sizeInBytes = c_size; - wrongEntity->hashes = {{"Sha1", c_sha1}, {"Sha256", c_sha256}}; - - FileEntities wrongEntities; - wrongEntities.push_back(std::move(wrongEntity)); - REQUIRE_THROWS_CODE_MSG( - AppFileEntity::FileEntitiesToAppFileVector(std::move(wrongEntities), handler), - ServiceUnexpectedContentType, - R"(The service returned file "fileId" with content type [Generic] while the expected type was [App])"); - } - } -} - -TEST("Testing FileEntity::DownloadInfoResponseToFileEntities()") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - SECTION("GenericFileEntity") - { - auto CheckEntity = [&](const FileEntity& entity) { - REQUIRE(entity.GetContentType() == ContentType::Generic); - REQUIRE(entity.fileId == c_fileId); - REQUIRE(entity.url == c_url); - REQUIRE(entity.sizeInBytes == c_size); - REQUIRE(entity.hashes.size() == 2); - REQUIRE(entity.hashes.at("Sha1") == c_sha1); - REQUIRE(entity.hashes.at("Sha256") == c_sha256); - }; - - SECTION("Correct") - { - const json fileJson = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}}; - - SECTION("1 file") - { - const json downloadInfoResponse = json::array({fileJson}); - auto entities = FileEntity::DownloadInfoResponseToFileEntities(downloadInfoResponse, handler); - REQUIRE(entities.size() == 1); - REQUIRE(entities[0] != nullptr); - CheckEntity(*entities[0]); - } - - SECTION("2 files") - { - const json downloadInfoResponse = json::array({fileJson, fileJson}); - auto entities = FileEntity::DownloadInfoResponseToFileEntities(downloadInfoResponse, handler); - REQUIRE(entities.size() == 2); - REQUIRE(entities[0] != nullptr); - CheckEntity(*entities[0]); - REQUIRE(entities[1] != nullptr); - CheckEntity(*entities[1]); - } - } - } - - SECTION("AppFileEntity") - { - const json applicabilityDetails = {{"Architectures", json::array({c_arch})}, - {"PlatformApplicabilityForPackage", json::array({c_applicability})}}; - - auto CheckEntity = [&](const AppFileEntity& entity) { - REQUIRE(entity.GetContentType() == ContentType::App); - REQUIRE(entity.fileId == c_fileId); - REQUIRE(entity.url == c_url); - REQUIRE(entity.sizeInBytes == c_size); - REQUIRE(entity.hashes.size() == 2); - REQUIRE(entity.hashes.at("Sha1") == c_sha1); - REQUIRE(entity.hashes.at("Sha256") == c_sha256); - REQUIRE(entity.fileMoniker == c_fileMoniker); - REQUIRE(entity.applicabilityDetails.architectures.size() == 1); - REQUIRE(entity.applicabilityDetails.architectures[0] == c_arch); - REQUIRE(entity.applicabilityDetails.platformApplicabilityForPackage.size() == 1); - REQUIRE(entity.applicabilityDetails.platformApplicabilityForPackage[0] == c_applicability); - }; - - SECTION("Correct") - { - const json fileJson = {{"FileId", c_fileId}, - {"Url", c_url}, - {"SizeInBytes", c_size}, - {"Hashes", {{"Sha1", c_sha1}, {"Sha256", c_sha256}}}, - {"FileMoniker", c_fileMoniker}, - {"ApplicabilityDetails", applicabilityDetails}}; - - SECTION("1 file") - { - const json downloadInfoResponse = json::array({fileJson}); - auto entities = FileEntity::DownloadInfoResponseToFileEntities(downloadInfoResponse, handler); - REQUIRE(entities.size() == 1); - REQUIRE(entities[0] != nullptr); - CheckEntity(dynamic_cast(*entities[0])); - } - - SECTION("2 files") - { - const json downloadInfoResponse = json::array({fileJson, fileJson}); - auto entities = FileEntity::DownloadInfoResponseToFileEntities(downloadInfoResponse, handler); - REQUIRE(entities.size() == 2); - REQUIRE(entities[0] != nullptr); - CheckEntity(dynamic_cast(*entities[0])); - REQUIRE(entities[1] != nullptr); - CheckEntity(dynamic_cast(*entities[1])); - } - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/unit/details/entity/VersionEntityTests.cpp b/src/SfsClient/sfs-client/client/tests/unit/details/entity/VersionEntityTests.cpp deleted file mode 100644 index 23b1b4f4d3..0000000000 --- a/src/SfsClient/sfs-client/client/tests/unit/details/entity/VersionEntityTests.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "../../../util/SFSExceptionMatcher.h" -#include "../../../util/TestHelper.h" -#include "ReportingHandler.h" -#include "entity/VersionEntity.h" -#include "sfsclient/ContentId.h" - -#include -#include - -#define TEST(...) TEST_CASE("[VersionEntityTests] " __VA_ARGS__) - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; -using json = nlohmann::json; - -// GenericVersionEntity constants -const std::string c_ns = "namespace"; -const std::string c_name = "name"; -const std::string c_version = "version"; - -// AppVersionEntity constants -const std::string c_updateId = "updateId"; - -TEST("Testing VersionEntity::FromJson()") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - SECTION("Generic Version Entity") - { - std::unique_ptr entity; - SECTION("Correct") - { - const json versionEntity = {{"ContentId", {{"Namespace", c_ns}, {"Name", c_name}, {"Version", c_version}}}}; - - REQUIRE_NOTHROW(entity = VersionEntity::FromJson(versionEntity, handler)); - REQUIRE(entity != nullptr); - REQUIRE(entity->GetContentType() == ContentType::Generic); - REQUIRE(entity->contentId.nameSpace == c_ns); - REQUIRE(entity->contentId.name == c_name); - REQUIRE(entity->contentId.version == c_version); - } - - SECTION("Missing fields") - { - SECTION("Missing ContentId") - { - const json versionEntity = {{"Namespace", c_ns}, {"Name", c_name}, {"Version", c_version}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing ContentId in response"); - } - - SECTION("Missing ContentId.Namespace") - { - const json versionEntity = {{"ContentId", {{"Name", c_name}, {"Version", c_version}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing ContentId.Namespace in response"); - } - - SECTION("Missing ContentId.Name") - { - const json versionEntity = {{"ContentId", {{"Namespace", c_ns}, {"Version", c_version}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing ContentId.Name in response"); - } - - SECTION("Missing ContentId.Version") - { - const json versionEntity = {{"ContentId", {{"Namespace", c_ns}, {"Name", c_name}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing ContentId.Version in response"); - } - } - - SECTION("Wrong types") - { - SECTION("ContentId not an object") - { - const json versionEntity = json::array({{"Namespace", 1}, {"Name", c_name}, {"Version", c_version}}); - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Response is not a JSON object"); - } - - SECTION("ContentId.Namespace") - { - const json versionEntity = { - {"ContentId", {{"Namespace", 1}, {"Name", c_name}, {"Version", c_version}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "ContentId.Namespace is not a string"); - } - - SECTION("ContentId.Name") - { - const json versionEntity = {{"ContentId", {{"Namespace", c_ns}, {"Name", 1}, {"Version", c_version}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "ContentId.Name is not a string"); - } - - SECTION("ContentId.Version") - { - const json versionEntity = {{"ContentId", {{"Namespace", c_ns}, {"Name", c_name}, {"Version", 1}}}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "ContentId.Version is not a string"); - } - } - } - - SECTION("App Version Entity") - { - std::unique_ptr entity; - const json contentId = {{"Namespace", c_ns}, {"Name", c_name}, {"Version", c_version}}; - - SECTION("Correct") - { - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({contentId})}}; - - REQUIRE_NOTHROW(entity = VersionEntity::FromJson(versionEntity, handler)); - REQUIRE(entity != nullptr); - REQUIRE(entity->GetContentType() == ContentType::App); - REQUIRE(entity->contentId.nameSpace == c_ns); - REQUIRE(entity->contentId.name == c_name); - REQUIRE(entity->contentId.version == c_version); - - AppVersionEntity* appEntity = dynamic_cast(entity.get()); - REQUIRE(appEntity->updateId == c_updateId); - REQUIRE(appEntity->prerequisites.size() == 1); - REQUIRE(appEntity->prerequisites[0].contentId.nameSpace == c_ns); - REQUIRE(appEntity->prerequisites[0].contentId.name == c_name); - REQUIRE(appEntity->prerequisites[0].contentId.version == c_version); - } - - SECTION("Missing fields") - { - SECTION("Missing Prerequisites") - { - const json versionEntity = {{"ContentId", contentId}, {"UpdateId", c_updateId}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing Prerequisites in response"); - } - - SECTION("Missing Prerequisite.Namespace") - { - const json wrongPrerequisite = {{"Name", c_name}, {"Version", c_version}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing Prerequisite.Namespace in response"); - } - - SECTION("Missing Prerequisite.Name") - { - const json wrongPrerequisite = {{"Namespace", c_ns}, {"Version", c_version}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing Prerequisite.Name in response"); - } - - SECTION("Missing Prerequisite.Version") - { - const json wrongPrerequisite = {{"Namespace", c_ns}, {"Name", c_name}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Missing Prerequisite.Version in response"); - } - } - - SECTION("Wrong types") - { - SECTION("UpdateId") - { - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", 1}, - {"Prerequisites", json::array({contentId})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "UpdateId is not a string"); - } - - SECTION("Prerequisites") - { - SECTION("Not an array") - { - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", contentId}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Prerequisites is not an array"); - } - - SECTION("Element is not an object") - { - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({1})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Prerequisite element is not a JSON object"); - } - - SECTION("Prerequisite.Namespace") - { - const json wrongPrerequisite = {{"Namespace", 1}, {"Name", c_name}, {"Version", c_version}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Prerequisite.Namespace is not a string"); - } - - SECTION("Prerequisite.Name") - { - const json wrongPrerequisite = {{"Namespace", c_ns}, {"Name", 1}, {"Version", c_version}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Prerequisite.Name is not a string"); - } - - SECTION("Prerequisite.Version") - { - const json wrongPrerequisite = {{"Namespace", c_ns}, {"Name", c_name}, {"Version", 1}}; - const json versionEntity = {{"ContentId", contentId}, - {"UpdateId", c_updateId}, - {"Prerequisites", json::array({wrongPrerequisite})}}; - REQUIRE_THROWS_CODE_MSG(VersionEntity::FromJson(versionEntity, handler), - ServiceInvalidResponse, - "Prerequisite.Version is not a string"); - } - } - } - } -} - -TEST("Testing VersionEntity conversions") -{ - ReportingHandler handler; - handler.SetLoggingCallback(LogCallbackToTest); - - auto CheckContentId = [&](const ContentId& contentId) { - REQUIRE(contentId.GetNameSpace() == c_ns); - REQUIRE(contentId.GetName() == c_name); - REQUIRE(contentId.GetVersion() == c_version); - }; - - SECTION("VersionEntity::ToContentId()") - { - SECTION("Success with GenericVersionEntity") - { - std::unique_ptr entity = std::make_unique(); - entity->contentId.nameSpace = c_ns; - entity->contentId.name = c_name; - entity->contentId.version = c_version; - - auto contentId = VersionEntity::ToContentId(std::move(*entity), handler); - CheckContentId(*contentId); - } - - SECTION("Success with AppVersionEntity") - { - std::unique_ptr entity = std::make_unique(); - auto appEntity = AppVersionEntity::GetAppVersionEntityPtr(entity, handler); - appEntity->contentId.nameSpace = c_ns; - appEntity->contentId.name = c_name; - appEntity->contentId.version = c_version; - appEntity->updateId = c_updateId; - - std::unique_ptr prereqEntity = std::make_unique(); - prereqEntity->contentId.nameSpace = c_ns; - prereqEntity->contentId.name = c_name; - prereqEntity->contentId.version = c_version; - - appEntity->prerequisites.push_back(std::move(*prereqEntity)); - - auto contentId = VersionEntity::ToContentId(std::move(*entity), handler); - CheckContentId(*contentId); - } - } -} diff --git a/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.cpp b/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.cpp deleted file mode 100644 index d1b5de29b1..0000000000 --- a/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "SFSExceptionMatcher.h" - -#include - -using namespace SFS; -using namespace SFS::details; -using namespace SFS::test; - -SFSExceptionMatcher::SFSExceptionMatcher(const Result::Code& code) : m_code(code) -{ -} - -SFSExceptionMatcher::SFSExceptionMatcher(const Result::Code& code, std::string message) - : m_code(code) - , m_message(std::move(message)) -{ -} - -bool SFSExceptionMatcher::match(const SFSException& other) const -{ - if (m_message) - { - return other.GetResult() == m_code && other.what() == m_message; - } - return other.GetResult() == m_code; -} - -std::string SFSExceptionMatcher::describe() const -{ - return "Equals: " + std::string(ToString(m_code)) + (m_message ? " with message: " + *m_message : ""); -} - -template -SFSExceptionGenericMatcher::SFSExceptionGenericMatcher(const Result::Code& code, Matcher messageMatcher) - : m_code(code) - , m_messageMatcher(std::move(messageMatcher)) -{ -} - -template -bool SFSExceptionGenericMatcher::match(const SFSException& other) const -{ - return other.GetResult() == m_code && m_messageMatcher.match(other.what()); -} - -template -std::string SFSExceptionGenericMatcher::describe() const -{ - return "Equals: " + std::string(ToString(m_code)) + m_messageMatcher.describe(); -} - -template struct SFS::test::SFSExceptionGenericMatcher; -template struct SFS::test::SFSExceptionGenericMatcher; -template struct SFS::test::SFSExceptionGenericMatcher; -template struct SFS::test::SFSExceptionGenericMatcher; -template struct SFS::test::SFSExceptionGenericMatcher; diff --git a/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.h b/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.h deleted file mode 100644 index 89e17713dc..0000000000 --- a/src/SfsClient/sfs-client/client/tests/util/SFSExceptionMatcher.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "Result.h" -#include "SFSException.h" - -#include -#include - -#include -#include - -#define REQUIRE_THROWS_CODE(call, code) \ - REQUIRE_THROWS_MATCHES(call, SFS::details::SFSException, SFS::test::SFSExceptionMatcher(SFS::Result::code)) -#define REQUIRE_THROWS_CODE_MSG(call, code, message) \ - REQUIRE_THROWS_MATCHES(call, SFS::details::SFSException, SFS::test::SFSExceptionMatcher(SFS::Result::code, message)) -#define REQUIRE_THROWS_CODE_MSG_MATCHES(call, code, matcher) \ - REQUIRE_THROWS_MATCHES(call, \ - SFS::details::SFSException, \ - SFS::test::SFSExceptionGenericMatcher(SFS::Result::code, matcher)) - -namespace SFS::test -{ -struct SFSExceptionMatcher : Catch::Matchers::MatcherGenericBase -{ - SFSExceptionMatcher(const Result::Code& code); - SFSExceptionMatcher(const Result::Code& code, std::string message); - - bool match(const SFS::details::SFSException& other) const; - std::string describe() const override; - - private: - Result::Code m_code{Result::Success}; - std::optional m_message; -}; - -template -struct SFSExceptionGenericMatcher : Catch::Matchers::MatcherGenericBase -{ - SFSExceptionGenericMatcher(const Result::Code& code, Matcher messageMatcher); - - bool match(const SFS::details::SFSException& other) const; - std::string describe() const override; - - private: - Result::Code m_code{Result::Success}; - Matcher m_messageMatcher; -}; -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp b/src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp deleted file mode 100644 index d569796323..0000000000 --- a/src/SfsClient/sfs-client/client/tests/util/TestHelper.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "TestHelper.h" - -#include -#include -#include -#include -#include -#include - -#include - -static std::mutex s_logMutex; - -namespace -{ -std::string TimestampToString(std::chrono::time_point time) -{ - using namespace std::chrono; - - // get number of milliseconds for the current second - // (remainder after division into seconds) - auto ms = duration_cast(time.time_since_epoch()) % 1000; - - auto timer = system_clock::to_time_t(time); - - std::stringstream timeStream; - struct tm gmTime; -#ifdef _WIN32 - gmtime_s(&gmTime, &timer); // gmtime_s is the safe version of gmtime, not available on Linux -#else - gmTime = (*std::gmtime(&timer)); -#endif - timeStream << std::put_time(&gmTime, "%F %X"); // yyyy-mm-dd HH:MM:SS - timeStream << '.' << std::setfill('0') << std::setw(3) << ms.count(); - - return timeStream.str(); -} -} // namespace - -void SFS::test::LogCallbackToTest(const SFS::LogData& logData) -{ - std::lock_guard guard(s_logMutex); - UNSCOPED_INFO("Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity) << "]" - << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line - << " " << logData.message); -} diff --git a/src/SfsClient/sfs-client/client/tests/util/TestHelper.h b/src/SfsClient/sfs-client/client/tests/util/TestHelper.h deleted file mode 100644 index 7fbaff0c16..0000000000 --- a/src/SfsClient/sfs-client/client/tests/util/TestHelper.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "sfsclient/Logging.h" - -#include - -#define TEST_UNSCOPED_INFO(message) \ - do \ - { \ - const std::string __message = (message); \ - SFS::test::LogCallbackToTest({SFS::LogSeverity::Info, \ - __message.c_str(), \ - __FILE__, \ - __LINE__, \ - __FUNCTION__, \ - std::chrono::system_clock::now()}); \ - } while ((void)0, 0) - -namespace SFS::test -{ -// Use this method to redirect the library logging to the Catch2 logging system -void LogCallbackToTest(const SFS::LogData& logData); -} // namespace SFS::test diff --git a/src/SfsClient/sfs-client/cmake/SFSOptions.cmake b/src/SfsClient/sfs-client/cmake/SFSOptions.cmake deleted file mode 100644 index dde611961c..0000000000 --- a/src/SfsClient/sfs-client/cmake/SFSOptions.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Define custom options for CMake. -# Options are passed to cmake through command line, -Doption=value -option(SFS_BUILD_TESTS "Indicates if tests should be built." OFF) - -option(SFS_BUILD_SAMPLES "Indicates if samples should be built." OFF) - -option( - SFS_ENABLE_TEST_OVERRIDES - "Set SFS_ENABLE_OVERRIDES to ON to enable certain test overrides through environment variables." - OFF) - -option( - SFS_WINDOWS_STATIC_ONLY - "Indicates if only static libraries and dependencies should be built on Windows." - OFF) diff --git a/src/SfsClient/sfs-client/cmake/sfsclient-config.cmake.in b/src/SfsClient/sfs-client/cmake/sfsclient-config.cmake.in deleted file mode 100644 index 8e4d754c70..0000000000 --- a/src/SfsClient/sfs-client/cmake/sfsclient-config.cmake.in +++ /dev/null @@ -1,16 +0,0 @@ -# Generates a macro to auto-configure everything -@PACKAGE_INIT@ - -set(SFS_BUILD_TESTS @SFS_BUILD_TESTS@) - -include(CMakeFindDependencyMacro) -find_dependency(CURL) -find_dependency(nlohmann_json) -find_dependency(correlation_vector) - -if(SFS_BUILD_TESTS) - find_dependency(Catch2) - find_dependency(cpp-httplib) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/sfsclient-targets.cmake") \ No newline at end of file diff --git a/src/SfsClient/sfs-client/pre-commit-wrapper.sh b/src/SfsClient/sfs-client/pre-commit-wrapper.sh deleted file mode 100644 index 53a0241413..0000000000 --- a/src/SfsClient/sfs-client/pre-commit-wrapper.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# -# A wrapper script to call the pre-commit.sh script -# -# Since git hooks are not committed to the server, existing only -# in the .git folder, this will be copied there and call the actual -# hook script only in case it exists. This should prevent issues -# with the user having to work on an earlier commit that does not -# have the hook or its dependencies installed. - -root_dir=$(git rev-parse --show-toplevel) -hook_script="$root_dir/pre-commit.sh" -if [[ -f $hook_script ]]; then - . "$hook_script" -fi diff --git a/src/SfsClient/sfs-client/pre-commit.sh b/src/SfsClient/sfs-client/pre-commit.sh deleted file mode 100644 index 481450eeb6..0000000000 --- a/src/SfsClient/sfs-client/pre-commit.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/sh -# -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# -# A hook script to verify what is about to be committed. -# Called by "git commit" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message if -# it wants to stop the commit. -# To avoid having this script called, use "git commit -n" - -# Get files that need to be checked against clang-format into an array - -# To understand the regex format in bash, take a look at https://tldp.org/LDP/abs/html/x17129.html -# For example, this is how the regex (\.cpp|\.h)$ matches the example file client/include/SFSClient.h: -# (\.cpp|\.h): Match either .cpp or .h. The . character is escaped because it has a special meaning in regex. -# $: Match end of string -pattern_to_include_in_clang_format='(\.cpp|\.h)$' -pattern_to_include_in_cmake_format='CMakeLists.txt' - -# Get the names of staged files (--cached) that are not deleted (--diff-filter=d) -all_staged_files=$(git diff --cached --name-only --diff-filter=d) -mapfile -t filtered_files <<<"$all_staged_files" - -declare -A files_with_unstaged_changes - -clang_format_files=() -cmake_format_files=() - -# Only those that match a pattern will be analyzed -max_index=${#filtered_files[@]} -for ((i = 0; i < max_index; i++)); do - if [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_clang_format || "${filtered_files[$i]}" =~ $pattern_to_include_in_cmake_format ]]; then - if [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_clang_format ]]; then - clang_format_files+=("${filtered_files[$i]}") - elif [[ "${filtered_files[$i]}" =~ $pattern_to_include_in_cmake_format ]]; then - cmake_format_files+=("${filtered_files[$i]}") - fi - - # Collect files that will be analyzed but have unstaged changes, as we don't want to overwrite these changes - # `git diff` is empty if a file has no unstaged changes - if [[ -n "$(git diff "${filtered_files[$i]}")" ]]; then - files_with_unstaged_changes["${filtered_files[$i]}"]=1 - fi - fi -done - -# Early stop if no files to check -if [[ ${clang_format_files[*]} == "" && ${cmake_format_files[*]} == "" ]]; then - exit 0 -fi - -RED='\033[0;31m' -YELLOW='\033[0;33m' -NC='\033[0m' # No color - -# -# clang-format -# - -# Check that clang-format is installed -python_dir="$(python -c 'import os,sysconfig;print(sysconfig.get_path("scripts",f"{os.name}_user"))')" -clang_format="$python_dir/clang-format.exe" - -if [[ ! -x "$(command -v "$clang_format")" ]]; then - echo -e "${RED}There are staged changes that need to be checked by the formatter." - echo -e "${RED}clang-format toolset was not found. Re-run scripts\Setup.ps1 to install it.${NC}" - exit 1 -fi - -formatted_files=() -unformatted_files=() -for file in "${clang_format_files[@]}"; do - # Check if file will be modified so we can properly inform the user of modified files - # Use `clang-format -n` which does a dry run and outputs a non-empty response if there's something to format - # Pass contents through stdin to get only the staged changes. --assume-filename is used to find the closest .clang-format - # `git show :` outputs the staged version of a file - if [[ -n $(git show ":$file" | "$clang_format" -n --assume-filename "$file" 2>&1) ]]; then - - # If the file has unstaged changes, we can't format it - if [[ -v "files_with_unstaged_changes["$file"]" ]] ; then - unformatted_files+=("$file") - else - # Collect file that will be formatted and then actually format in-place - formatted_files+=("$file") - "$clang_format" -i "$file" - fi - fi -done - - -# Report modified files at end so we can run both formatters and report all changes at once - -# -# cmake-format -# -cmake_format="$python_dir/cmake-format.exe" - -if [[ ! -x "$(command -v "$cmake_format")" ]]; then - echo -e "${RED}There are staged changes that need to be checked by the formatter." - echo -e "${RED}cmake-format toolset was not found. Re-run scripts\Setup.ps1 to install it.${NC}" - exit 1 -fi - -tmp_formatted_name="tmp_formatted" - -for file in "${cmake_format_files[@]}"; do - # Check if file will be modified so we can properly inform the user of modified files - # First create a temporary file with only staged changes since cmake-format does not support stdin - git show ":$file" > $tmp_formatted_name - # Use `cmake-format --check` which does a dry run and returns 1 if there's something to format - if ! "$cmake_format" --check "$tmp_formatted_name" > /dev/null 2>&1; then - - # If the file has unstaged changes, we can't format it - if [[ -v "files_with_unstaged_changes["$file"]" ]] ; then - unformatted_files+=("$file") - else - # Collect file that will be formatted and then actually format in-place - formatted_files+=("$file") - "$cmake_format" -i "$file" - fi - fi - rm $tmp_formatted_name -done - -IFS=$'\n' sorted_formatted_files=($(sort <<<"${formatted_files[*]}")) -IFS=$'\n' sorted_unformatted_files=($(sort <<<"${unformatted_files[*]}")) -unset IFS - -exit_code=0 - -# If there were formatted files, we report the changes -if [[ ${sorted_formatted_files[*]} != "" ]]; then - echo -e "${YELLOW}There are files that have been automatically formatted. Review and stage the changes before committing:" - for file in "${sorted_formatted_files[@]}"; do - echo -e "M\t$file" - done - echo -e "${NC}" - exit_code=1 -fi - -# If there were files that could not be formatted, we report them -if [[ ${unformatted_files[*]} != "" ]]; then - echo -e "${RED}There are files that need to be formatted but have unstaged changes. Either stage the files or stash/discard the unstaged changes:" - for file in "${unformatted_files[@]}"; do - echo -e "M\t$file" - done - echo -e "${NC}" - exit_code=1 -fi - -exit $exit_code diff --git a/src/SfsClient/sfs-client/samples/CMakeLists.txt b/src/SfsClient/sfs-client/samples/CMakeLists.txt deleted file mode 100644 index 7527cd456e..0000000000 --- a/src/SfsClient/sfs-client/samples/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -add_subdirectory(integration-do-client) -add_subdirectory(tool) diff --git a/src/SfsClient/sfs-client/samples/README.md b/src/SfsClient/sfs-client/samples/README.md deleted file mode 100644 index 0fd444de13..0000000000 --- a/src/SfsClient/sfs-client/samples/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Samples - -This folder contains a few samples that show how to use the SFS Client library. - -## integration-do-client - -This sample shows how to use the SFSClient to retrieva metadata and URLs from the SFS Service and the [Delivery Optimization SDK](https://github.com/microsoft/do-client) to perform the download of the retrieved URLs. - -In Windows, the DO SDK uses COM APIs. In Linux, it will require the DO Agent, which is built from the same repository. diff --git a/src/SfsClient/sfs-client/samples/integration-do-client/CMakeLists.txt b/src/SfsClient/sfs-client/samples/integration-do-client/CMakeLists.txt deleted file mode 100644 index 1b8c5a4470..0000000000 --- a/src/SfsClient/sfs-client/samples/integration-do-client/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -project(IntegrationDOClient LANGUAGES CXX) - -# Settings for do-client library -set(DO_INCLUDE_SDK - ON - CACHE BOOL "" FORCE) -set(DO_BUILD_TESTS - OFF - CACHE BOOL "" FORCE) -if(NOT DEFINED CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE debug) -endif() - -if(MSVC) - set(DO_PATCH_COMMAND "") -else() - # Populate LSB_RELEASE_ID_SHORT with the linux distro if available - find_program(LSB_RELEASE_EXEC lsb_release) - execute_process( - COMMAND ${LSB_RELEASE_EXEC} -is - OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(LSB_RELEASE_ID_SHORT STREQUAL "Ubuntu") - # 1. Run bootstrap script to install Linux dependencies - # 2. Build & install DO Agent, which acts as a service for the REST API - set(DO_PATCH_COMMAND - cd && sudo ./build/scripts/bootstrap.sh --install - build && python3 ./build/build.py --project agent --package-for deb - && cd /tmp/build-deliveryoptimization-agent/linux-debug && sudo - dpkg -i deliveryoptimization-agent_1.1.0_amd64.deb) - else() - message(FATAL_ERROR "Unsupported platform ${LSB_RELEASE_ID_SHORT}") - endif() -endif() - -# Include the capability of fetching external content -include(FetchContent) - -FetchContent_Declare( - do-client - GIT_REPOSITORY https://github.com/microsoft/do-client.git - GIT_TAG d71ade6f692dd8bc319ec3228c956517e9b29292 # v1.1.0 - PATCH_COMMAND "${DO_PATCH_COMMAND}") -FetchContent_MakeAvailable(do-client) - -add_executable(${PROJECT_NAME}) - -target_sources(${PROJECT_NAME} PRIVATE IntegrationDOClient.cpp) - -set(SFS_CLIENT_LIB_NAME "Microsoft::${CMAKE_PROJECT_NAME}") -add_dependencies(${PROJECT_NAME} ${SFS_CLIENT_LIB_NAME}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${SFS_CLIENT_LIB_NAME}) - -set(DO_SDK_LIB_NAME "Microsoft::deliveryoptimization") -add_dependencies(${PROJECT_NAME} ${DO_SDK_LIB_NAME}) -target_link_libraries(${PROJECT_NAME} PRIVATE ${DO_SDK_LIB_NAME}) - -target_include_directories(${PROJECT_NAME} PUBLIC ../../client/include) diff --git a/src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp b/src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp deleted file mode 100644 index 7fb6ce66ac..0000000000 --- a/src/SfsClient/sfs-client/samples/integration-do-client/IntegrationDOClient.cpp +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -namespace -{ -void DisplayUsage() -{ - std::cout << "Usage: ExampleIntegrationDOClient --product [options]" << std::endl - << std::endl - << "Required:" << std::endl - << " --product \t\tName or GUID of the product to be retrieved" << std::endl - << std::endl - << "Options:" << std::endl - << " -h, --help\t\t\tDisplay this help message" << std::endl - << " --accountId \t\tAccount ID of the SFS service, used to identify the caller" << std::endl - << " --outDir \t\tLocation where download will be placed. Defaults to a tmp dir" << std::endl - << std::endl - << std::endl - << "Example:" << std::endl - << " ExampleIntegrationDOClient --product \"msedge-stable-win-x64\" --accountId msedge" << std::endl; -} - -void DisplayHelp() -{ - std::cout - << "ExampleIntegrationDOClient" << std::endl - << "Copyright (c) Microsoft Corporation. All rights reserved." << std::endl - << std::endl - << "Use to interact with the SFS service through the SFS Client library and download using the Delivery Optimization SDK." - << std::endl - << std::endl; - DisplayUsage(); -} - -const std::string c_boldRedStart = "\033[1;31m"; -const std::string c_cyanStart = "\033[0;36m"; -const std::string c_darkGreyStart = "\033[0;90m"; -const std::string c_colorEnd = "\033[0m"; - -void PrintError(std::string_view message) -{ - std::cout << c_boldRedStart << message << c_colorEnd << std::endl; -} - -void PrintLog(std::string_view message) -{ - std::cout << c_cyanStart << message << c_colorEnd << std::endl; -} - -struct Settings -{ - bool displayHelp{true}; - std::string product; - std::string accountId; - std::string outDir; -}; - -int ParseArguments(const std::vector& args, Settings& settings) -{ - settings = {}; - settings.displayHelp = args.size() == 1; - - const size_t argsSize = args.size(); - auto validateArg = - [&argsSize](const size_t index, const std::string& switchName, const std::string_view& argValue) -> bool { - if (argsSize <= index + 1) - { - PrintError("Missing argument of --" + switchName + "."); - return false; - } - if (!argValue.empty()) - { - PrintError("--" + switchName + " can only be specified once."); - return false; - } - return true; - }; - - for (size_t i = 1; i < args.size(); ++i) - { - if (args[i].compare("-h") == 0 || args[i].compare("--help") == 0) - { - settings.displayHelp = true; - } - else if (args[i].compare("--product") == 0) - { - if (!validateArg(i, "product", settings.product)) - { - return 1; - } - settings.product = args[++i]; - } - else if (args[i].compare("--accountId") == 0) - { - if (!validateArg(i, "accountId", settings.accountId)) - { - return 1; - } - settings.accountId = args[++i]; - } - else if (args[i].compare("--outDir") == 0) - { - if (!validateArg(i, "outDir", settings.outDir)) - { - return 1; - } - settings.outDir = args[++i]; - } - else - { - PrintError("Unknown option " + std::string(args[i]) + ".\n"); - return 1; - } - } - - if (settings.product.empty()) - { - return 1; - } - - return 0; -} - -void LogResult(const SFS::Result& result) -{ - std::cout << " Result code: " << ToString(result.GetCode()); - if (!result.GetMsg().empty()) - { - std::cout << ". Message: " << result.GetMsg(); - } - std::cout << std::endl; -} - -std::string TimestampToString(std::chrono::time_point time) -{ - using namespace std::chrono; - - // get number of milliseconds for the current second - // (remainder after division into seconds) - auto ms = duration_cast(time.time_since_epoch()) % 1000; - - auto timer = system_clock::to_time_t(time); - - std::stringstream timeStream; - struct tm gmTime; -#ifdef _WIN32 - gmtime_s(&gmTime, &timer); // gmtime_s is the safe version of gmtime, not available on Linux -#else - gmTime = (*std::gmtime(&timer)); -#endif - timeStream << std::put_time(&gmTime, "%F %X"); // yyyy-mm-dd HH:MM:SS - timeStream << '.' << std::setfill('0') << std::setw(3) << ms.count(); - - return timeStream.str(); -} - -void LoggingCallback(const SFS::LogData& logData) -{ - std::cout << c_darkGreyStart << "Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity) - << "]" - << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " " - << logData.message << c_colorEnd << std::endl; -} - -std::filesystem::path GetOutDir(const std::string& outDir) -{ - if (outDir.empty()) - { - auto tmpDir = std::filesystem::temp_directory_path(); - auto outDir = tmpDir / "SFSDownload"; - std::filesystem::create_directories(outDir); - - // Set write permissions to the download dir since the download happens from a different process, but only in - // non-Windows -#ifndef _WIN32 - std::filesystem::permissions(outDir, - std::filesystem::perms::owner_all | std::filesystem::perms::group_all | - std::filesystem::perms::others_all, - std::filesystem::perm_options::add); -#endif - return outDir.string(); - } - return outDir; -} - -std::vector GetLatestDownloadInfo(const Settings& settings) -{ - // Initialize SFSClient - PrintLog("Initializing SFSClient with accountId: " + settings.accountId); - - SFS::ClientConfig config; - config.accountId = settings.accountId; - config.logCallbackFn = LoggingCallback; - - std::unique_ptr sfsClient; - auto result = SFS::SFSClient::Make(config, sfsClient); - if (!result) - { - PrintError("Failed to initialize SFSClient."); - LogResult(result); - return {}; - } - - // Perform operations using SFSClient - PrintLog("Getting latest download info for product: " + settings.product); - std::vector contents; - SFS::RequestParams params; - params.productRequests = {{settings.product, {}}}; - result = sfsClient->GetLatestDownloadInfo(params, contents); - if (!result) - { - PrintError("Failed to get latest download info."); - LogResult(result); - return {}; - } - - return contents; -} - -#ifdef _WIN32 -class CoInitializeWrapper -{ - public: - CoInitializeWrapper() - { - CoInitializeEx(nullptr, COINIT_MULTITHREADED); - } - - ~CoInitializeWrapper() - { - CoUninitialize(); - } -}; -#endif - -std::string ErrorCodeToHexString(std::error_code error) -{ - std::stringstream stream; - stream << "0x" << std::hex << error.value(); - return stream.str(); -} - -int Download(const SFS::Content& content, const std::string& baseOutDir) -{ - PrintLog("Found content: " + content.GetContentId().GetNameSpace() + "/" + content.GetContentId().GetName() + "/" + - content.GetContentId().GetVersion()); - - const auto outDir = GetOutDir(baseOutDir); - PrintLog("Downloading files to: " + outDir.string()); - - for (const auto& file : content.GetFiles()) - { - PrintLog("Downloading file " + file.GetFileId() + " from " + file.GetUrl()); - auto outFilePath = outDir / file.GetFileId(); - - if (std::filesystem::exists(outFilePath)) - { - // In a real implementation, one would check the file sha1 or sha256 hash to see if the file is the same as - // the current one - PrintLog("File already exists, cannot download again. Skipping."); - continue; - } - - // Download the file using Delivery Optimization SDK - std::unique_ptr download; - auto error = microsoft::deliveryoptimization::download::make(file.GetUrl(), outFilePath.string(), download); - if (error) - { - PrintError("Failed to create download object with error " + ErrorCodeToHexString(error)); - return 1; - } - - error = download->start_and_wait_until_completion(); - if (error) - { - PrintError("Failed to download file with error " + ErrorCodeToHexString(error)); - return 1; - } - - PrintLog("File successfully downloaded"); - } - - return 0; -} -} // namespace - -int main(int argc, char* argv[]) -{ - Settings settings; - if (ParseArguments(std::vector(argv, argv + argc), settings) != 0) - { - DisplayUsage(); - return 1; - } - - if (settings.displayHelp) - { - DisplayHelp(); - return 0; - } - - auto contents = GetLatestDownloadInfo(settings); - if (contents.size() != 1) - { - return 1; - } - - // In Windows the DO SDK uses COM, so we must call CoInitializeEx before using it -#ifdef _WIN32 - CoInitializeWrapper coInitWrapper; -#endif - - if (Download(contents[0], settings.outDir) != 0) - { - return 1; - } - - return 0; -} diff --git a/src/SfsClient/sfs-client/samples/tool/CMakeLists.txt b/src/SfsClient/sfs-client/samples/tool/CMakeLists.txt deleted file mode 100644 index 0b378cd89f..0000000000 --- a/src/SfsClient/sfs-client/samples/tool/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# JSON library -find_package(nlohmann_json CONFIG REQUIRED) - -project(SFSClientTool LANGUAGES CXX) - -add_executable(${PROJECT_NAME}) - -target_sources(${PROJECT_NAME} PRIVATE SFSClientTool.cpp) - -set(SFS_CLIENT_LIB_NAME "Microsoft::${CMAKE_PROJECT_NAME}") -add_dependencies(${PROJECT_NAME} ${SFS_CLIENT_LIB_NAME}) - -target_link_libraries(${PROJECT_NAME} PRIVATE ${SFS_CLIENT_LIB_NAME}) -target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) - -target_include_directories(${PROJECT_NAME} PUBLIC ../../client/include) - -set_compile_options_for_target(${PROJECT_NAME}) diff --git a/src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp b/src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp deleted file mode 100644 index b784c94611..0000000000 --- a/src/SfsClient/sfs-client/samples/tool/SFSClientTool.cpp +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace SFS; -using json = nlohmann::json; - -namespace -{ -void DisplayUsage() -{ - std::cout - << "Usage: SFSClientTool --product --accountId [options]" << std::endl - << std::endl - << "Required:" << std::endl - << " --product \tName or GUID of the product to be retrieved" << std::endl - << " --accountId \t\tAccount ID of the SFS service, used to identify the caller" << std::endl - << std::endl - << "Options:" << std::endl - << " -h, --help\t\t\tDisplay this help message" << std::endl - << " -v, --version\t\t\tDisplay the library version" << std::endl - << " -o, --outputFile \t\tWhen specified, the JSON output is saved to this file" << std::endl - << " --isApp\t\t\tIndicates the specific product is an App" << std::endl - << " --instanceId \t\tA custom SFS instance ID" << std::endl - << " --namespace \t\tA custom SFS namespace" << std::endl - << " --customUrl \t\tA custom URL for the SFS service. Library must have been built with SFS_ENABLE_OVERRIDES" - << std::endl - << std::endl - << "Example:" << std::endl - << " SFSClientTool --product msedge-stable-win-x64 --accountId msedge" << std::endl; -} - -void DisplayHelp() -{ - std::cout << "SFSClient Tool" << std::endl - << "Copyright (c) Microsoft Corporation. All rights reserved." << std::endl - << std::endl - << "Use to interact with the SFS service through the SFS Client library." << std::endl - << std::endl; - DisplayUsage(); -} - -void DisplayVersion() -{ - std::cout << "SFSClient Tool " << SFSClient::GetVersion() << std::endl; -} - -const std::string c_boldRedStart = "\033[1;31m"; -const std::string c_cyanStart = "\033[0;36m"; -const std::string c_darkGreyStart = "\033[0;90m"; -const std::string c_colorEnd = "\033[0m"; - -void PrintError(std::string_view message) -{ - std::cout << c_boldRedStart << message << c_colorEnd << std::endl; -} - -void PrintLog(std::string_view message) -{ - std::cout << c_cyanStart << message << c_colorEnd << std::endl; -} - -bool AreEqualI(std::string_view a, std::string_view b) -{ - return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { - return std::tolower(static_cast(a)) == std::tolower(static_cast(b)); - }); -} - -struct Settings -{ - bool displayHelp{true}; - bool displayVersion{false}; - bool isApp{false}; - std::string product; - std::string accountId; - std::string instanceId; - std::string nameSpace; - std::string customUrl; - std::string outputFile; - - bool ShouldOutputToFile() const - { - return !outputFile.empty(); - } -}; - -void ParseArguments(const std::vector& args, Settings& settings) -{ - settings = {}; - settings.displayHelp = args.size() == 1; - - const size_t argsSize = args.size(); - auto validateArg = - [&argsSize](const size_t index, const std::string& switchName, const std::string_view& argValue) -> void { - if (argsSize <= index + 1) - { - throw std::runtime_error("Missing argument of --" + switchName); - } - if (!argValue.empty()) - { - throw std::runtime_error("--" + switchName + " can only be specified once"); - } - }; - - auto matchArg = - [](std::string_view arg, const std::string& shortSwitchName, const std::string& longSwitchName) -> bool { - return (!shortSwitchName.empty() && AreEqualI(arg, shortSwitchName)) || AreEqualI(arg, longSwitchName); - }; - - auto matchLongArg = [](std::string_view arg, const std::string& longSwitchName) -> bool { - return AreEqualI(arg, longSwitchName); - }; - - for (size_t i = 1; i < args.size(); ++i) - { - if (matchArg(args[i], "-h", "--help")) - { - settings.displayHelp = true; - } - else if (matchArg(args[i], "-v", "--version")) - { - settings.displayVersion = true; - } - else if (matchArg(args[i], "-o", "--outputFile")) - { - validateArg(i, "outputFile", settings.outputFile); - settings.outputFile = args[++i]; - } - else if (matchLongArg(args[i], "--isApp")) - { - settings.isApp = true; - } - else if (matchLongArg(args[i], "--product")) - { - validateArg(i, "product", settings.product); - settings.product = args[++i]; - } - else if (matchLongArg(args[i], "--accountId")) - { - validateArg(i, "accountId", settings.accountId); - settings.accountId = args[++i]; - } - else if (matchLongArg(args[i], "--instanceId")) - { - validateArg(i, "instanceId", settings.instanceId); - settings.instanceId = args[++i]; - } - else if (matchLongArg(args[i], "--namespace")) - { - validateArg(i, "namespace", settings.nameSpace); - settings.nameSpace = args[++i]; - } - else if (matchLongArg(args[i], "--customUrl")) - { - validateArg(i, "customUrl", settings.customUrl); - settings.customUrl = args[++i]; - } - else - { - throw std::runtime_error("Unknown option " + std::string(args[i])); - } - } - - if (!settings.displayHelp && (settings.product.empty() || settings.accountId.empty())) - { - throw std::runtime_error("--product and --accountId are required and cannot be empty"); - } -} - -constexpr std::string_view ToString(HashType type) -{ - switch (type) - { - case HashType::Sha1: - return "Sha1"; - case HashType::Sha256: - return "Sha256"; - } - return ""; -} - -constexpr std::string_view ToString(Architecture type) -{ - switch (type) - { - case Architecture::None: - return "None"; - case Architecture::Amd64: - return "amd64"; - case Architecture::Arm: - return "arm"; - case Architecture::Arm64: - return "arm64"; - case Architecture::x86: - return "x86"; - } - return ""; -} - -json ContentsToJson(const std::vector& contents) -{ - json out = json::array(); - for (const auto& content : contents) - { - json j = json::object(); - j["ContentId"]["Namespace"] = content.GetContentId().GetNameSpace(); - j["ContentId"]["Name"] = content.GetContentId().GetName(); - j["ContentId"]["Version"] = content.GetContentId().GetVersion(); - - j["Files"] = json::array(); - for (const auto& file : content.GetFiles()) - { - json fileJson = json::object(); - fileJson["FileId"] = file.GetFileId(); - fileJson["Url"] = file.GetUrl(); - fileJson["SizeInBytes"] = file.GetSizeInBytes(); - json hashes = json::object(); - for (const auto& hash : file.GetHashes()) - { - hashes[ToString(hash.first)] = hash.second; - } - fileJson["Hashes"] = hashes; - j["Files"].push_back(fileJson); - } - out.push_back(j); - } - - return out; -} - -json AppFileToJson(const AppFile& file) -{ - json fileJson = json::object(); - fileJson["FileId"] = file.GetFileId(); - fileJson["Url"] = file.GetUrl(); - fileJson["SizeInBytes"] = file.GetSizeInBytes(); - fileJson["FileMoniker"] = file.GetFileMoniker(); - json hashes = json::object(); - for (const auto& hash : file.GetHashes()) - { - hashes[ToString(hash.first)] = hash.second; - } - fileJson["Hashes"] = hashes; - - fileJson["ApplicabilityDetails"] = json::object(); - fileJson["ApplicabilityDetails"]["Architectures"] = json::array(); - for (const auto& arch : file.GetApplicabilityDetails().GetArchitectures()) - { - fileJson["ApplicabilityDetails"]["Architectures"].push_back(ToString(arch)); - } - fileJson["ApplicabilityDetails"]["PlatformApplicabilityForPackage"] = json::array(); - for (const auto& app : file.GetApplicabilityDetails().GetPlatformApplicabilityForPackage()) - { - fileJson["ApplicabilityDetails"]["PlatformApplicabilityForPackage"].push_back(app); - } - - return fileJson; -} - -json AppContentsToJson(const std::vector& contents) -{ - json out = json::array(); - for (const auto& content : contents) - { - json j = json::object(); - j["ContentId"]["Namespace"] = content.GetContentId().GetNameSpace(); - j["ContentId"]["Name"] = content.GetContentId().GetName(); - j["ContentId"]["Version"] = content.GetContentId().GetVersion(); - j["UpdateId"] = content.GetUpdateId(); - - j["Files"] = json::array(); - for (const auto& file : content.GetFiles()) - { - j["Files"].push_back(AppFileToJson(file)); - } - - j["Prerequisites"] = json::array(); - for (const auto& prereq : content.GetPrerequisites()) - { - json prereqJson = json::object(); - prereqJson["ContentId"]["Namespace"] = prereq.GetContentId().GetNameSpace(); - prereqJson["ContentId"]["Name"] = prereq.GetContentId().GetName(); - prereqJson["ContentId"]["Version"] = prereq.GetContentId().GetVersion(); - - prereqJson["Files"] = json::array(); - for (const auto& file : prereq.GetFiles()) - { - prereqJson["Files"].push_back(AppFileToJson(file)); - } - - j["Prerequisites"].push_back(prereqJson); - } - out.push_back(j); - } - - return out; -} - -void DisplayResults(const json& results) -{ - if (results.empty()) - { - return; - } - - PrintLog("Content found:"); - PrintLog(results.dump(2 /*indent*/)); -} - -Result JSONToFile(const json& results, const std::string& filename) -{ - if (results.empty()) - { - return Result::Unexpected; - } - - const std::filesystem::path filepath = std::filesystem::absolute(filename); - try - { - std::filesystem::create_directories(filepath.parent_path()); - } - catch (const std::exception& e) - { - PrintError("Failed to create parent directories for filepath: " + filepath.string() + ". Error: " + e.what()); - return Result::Unexpected; - } - - std::ofstream file(filepath, std::ios::out | std::ios::trunc); - if (!file.is_open()) - { - PrintError("Failed to open file for writing: " + filename); - return Result::Unexpected; - } - - file << results.dump(2 /*indent*/); - - PrintLog("Content found. Saved to file " + filepath.string()); - - return Result::Success; -} - -void LogResult(const SFS::Result& result) -{ - std::cout << " Result code: " << ToString(result.GetCode()); - if (!result.GetMsg().empty()) - { - std::cout << ". Message: " << result.GetMsg(); - } - std::cout << std::endl; -} - -std::string TimestampToString(std::chrono::time_point time) -{ - using namespace std::chrono; - - // get number of milliseconds for the current second - // (remainder after division into seconds) - auto ms = duration_cast(time.time_since_epoch()) % 1000; - - auto timer = system_clock::to_time_t(time); - - std::stringstream timeStream; - struct tm gmTime; -#ifdef _WIN32 - gmtime_s(&gmTime, &timer); // gmtime_s is the safe version of gmtime, not available on Linux -#else - gmTime = (*std::gmtime(&timer)); -#endif - timeStream << std::put_time(&gmTime, "%F %X"); // yyyy-mm-dd HH:MM:SS - timeStream << '.' << std::setfill('0') << std::setw(3) << ms.count(); - - return timeStream.str(); -} - -void LoggingCallback(const SFS::LogData& logData) -{ - std::cout << c_darkGreyStart << "Log: " << TimestampToString(logData.time) << " [" << ToString(logData.severity) - << "]" - << " " << std::filesystem::path(logData.file).filename().string() << ":" << logData.line << " " - << logData.message << c_colorEnd << std::endl; -} - -bool SetEnv(const std::string& varName, const std::string& value) -{ - if (varName.empty() || value.empty()) - { - return false; - } -#ifdef _WIN32 - return _putenv_s(varName.c_str(), value.c_str()) == 0; -#else - return setenv(varName.c_str(), value.c_str(), 1 /*overwrite*/) == 0; -#endif -} - -Result GetLatestDownloadInfo(const SFSClient& sfsClient, const Settings& settings, json& out) -{ - PrintLog("Getting latest download info for product: " + settings.product); - RequestParams params; - params.productRequests = {{settings.product, {}}}; - - if (settings.isApp) - { - std::vector appContents; - auto result = sfsClient.GetLatestAppDownloadInfo(params, appContents); - if (!result) - { - PrintError("Failed to get latest download info for app."); - LogResult(result); - return result.GetCode(); - } - - out = AppContentsToJson(appContents); - } - else - { - std::vector contents; - auto result = sfsClient.GetLatestDownloadInfo(params, contents); - if (!result) - { - PrintError("Failed to get latest download info."); - LogResult(result); - return result.GetCode(); - } - - out = ContentsToJson(contents); - } - - if (out.empty()) - { - PrintError("No results found."); - return Result::Unexpected; - } - - return Result::Success; -} - -Result HandleJSONContents(const Settings& settings, const json& contents) -{ - if (settings.ShouldOutputToFile()) - { - auto result = JSONToFile(contents, settings.outputFile); - if (!result) - { - PrintError("Failed to save to file."); - return result.GetCode(); - } - } - else - { - DisplayResults(contents); - return Result::Success; - } - - return Result::Success; -} -} // namespace - -int main(int argc, char* argv[]) -{ - Settings settings; - try - { - ParseArguments(std::vector(argv, argv + argc), settings); - } - catch (const std::runtime_error& e) - { - PrintError(std::string(e.what()) + "\n"); - DisplayUsage(); - return 1; - } - - if (settings.displayVersion) - { - DisplayVersion(); - return 0; - } - - if (settings.displayHelp) - { - DisplayHelp(); - return 0; - } - - if (!settings.customUrl.empty()) - { - SetEnv("SFS_TEST_OVERRIDE_BASE_URL", settings.customUrl); - PrintLog("Using custom URL: " + settings.customUrl); - PrintLog("Note that the library must have been built with SFS_ENABLE_OVERRIDES to use a custom URL."); - } - - // Initialize SFSClient - PrintLog("Initializing SFSClient with accountId: " + settings.accountId + - (settings.instanceId.empty() ? "" : ", instanceId: " + settings.instanceId) + - (settings.nameSpace.empty() ? "" : ", namespace: " + settings.nameSpace)); - - ClientConfig config; - config.accountId = settings.accountId; - if (!settings.instanceId.empty()) - { - config.instanceId = settings.instanceId; - } - if (!settings.nameSpace.empty()) - { - config.nameSpace = settings.nameSpace; - } - config.logCallbackFn = LoggingCallback; - - std::unique_ptr sfsClient; - auto result = SFSClient::Make(config, sfsClient); - if (!result) - { - PrintError("Failed to initialize SFSClient."); - LogResult(result); - return result.GetCode(); - } - - // Perform operations using SFSClient - json contents; - result = GetLatestDownloadInfo(*sfsClient, settings, contents); - if (!result) - { - LogResult(result); - return result.GetCode(); - } - - result = HandleJSONContents(settings, contents); - if (!result) - { - LogResult(result); - return result.GetCode(); - } - - return 0; -} diff --git a/src/SfsClient/sfs-client/scripts/Build.ps1 b/src/SfsClient/sfs-client/scripts/Build.ps1 deleted file mode 100644 index c32dda1640..0000000000 --- a/src/SfsClient/sfs-client/scripts/Build.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -<# -.SYNOPSIS -Simplifies build commands for the SFS Client. - -.PARAMETER Clean -Use this to clean the build folder before building. - -.PARAMETER BuildType -Use this to define the build type between "Debug" and "Release". The default is "Debug". - -.PARAMETER EnableTestOverrides -Use this to enable test overrides. - -.PARAMETER BuildTests -Use this to enable building tests. On by default. - -.PARAMETER BuildSamples -Use this to enable building samples. On by default. - -.DESCRIPTION -This script will contain the build commands for the SFS Client. The default build folder will be "/build". -Use this on Windows platforms in a PowerShell session. - -.EXAMPLE -PS> ./scripts/Build.ps1 -#> -param ( - [switch] $Clean = $false, - # BuildType is a build time parameter for Visual Studio generator in CMake - [ValidateSet("Debug", "Release")] - [string] $BuildType = "Debug", - # Make sure when adding a new switch below to check if it requires CMake regeneration - [switch] $EnableTestOverrides = $false, - [bool] $BuildTests = $true, - [bool] $BuildSamples = $true -) - -$GitRoot = (Resolve-Path (&git -C $PSScriptRoot rev-parse --show-toplevel)).Path -if (!(Test-Path "$GitRoot\vcpkg")) -{ - throw "vcpkg not found at $GitRoot\vcpkg. Run the Setup.ps1 script first." -} - -$BuildFolder = "$GitRoot/build" -if ($Clean -and (Test-Path $BuildFolder)) -{ - Write-Host -ForegroundColor Yellow "Cleaning build folder before build..." - Remove-Item -Recurse -Force $BuildFolder -} - -$Regenerate = $false -$CMakeCacheFile = "$BuildFolder\CMakeCache.txt" -$EnableTestOverridesStr = if ($EnableTestOverrides) {"ON"} else {"OFF"} -$BuildTestsOverridesStr = if ($BuildTests) {"ON"} else {"OFF"} -$BuildSamplesOverridesStr = if ($BuildSamples) {"ON"} else {"OFF"} - -function Test-CMakeCacheValueNoMatch($CMakeCacheFile, $Pattern, $ExpectedValue) -{ - $Match = Select-String -Path $CMakeCacheFile -Pattern $Pattern - if ($null -ne $Match -and $null -ne $Match.Matches.Groups[1]) - { - return $ExpectedValue -ne $Match.Matches.Groups[1].Value - } - return $true -} - -if (Test-Path $CMakeCacheFile) -{ - # Regenerate if one of the build options is set to a different value than the one passed in - $Regenerate = Test-CMakeCacheValueNoMatch $CMakeCacheFile "^SFS_ENABLE_TEST_OVERRIDES:BOOL=(.*)$" $EnableTestOverridesStr - $Regenerate = Test-CMakeCacheValueNoMatch $CMakeCacheFile "^SFS_BUILD_TESTS:BOOL=(.*)$" $BuildTestsOverridesStr - $Regenerate = Test-CMakeCacheValueNoMatch $CMakeCacheFile "^SFS_BUILD_SAMPLES:BOOL=(.*)$" $BuildSamplesOverridesStr -} - -# Configure cmake if build folder doesn't exist or if the build must be regenerated. -# This creates build targets that will be used by the build command -if (!(Test-Path $BuildFolder) -or $Regenerate) -{ - $Options = "-DSFS_ENABLE_TEST_OVERRIDES=$EnableTestOverridesStr"; - $Options += " -DSFS_BUILD_TESTS=$BuildTestsOverridesStr"; - $Options += " -DSFS_BUILD_SAMPLES=$BuildSamplesOverridesStr"; - $Options += " -DSFS_WINDOWS_STATIC_ONLY=ON"; - Invoke-Expression "cmake -S $GitRoot -B $BuildFolder $Options" -} - -# This is the build command. If any CMakeLists.txt files change, this will also reconfigure before building -cmake --build $BuildFolder --config $BuildType diff --git a/src/SfsClient/sfs-client/scripts/Setup.ps1 b/src/SfsClient/sfs-client/scripts/Setup.ps1 deleted file mode 100644 index 3a5b0623aa..0000000000 --- a/src/SfsClient/sfs-client/scripts/Setup.ps1 +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -<# -.SYNOPSIS -Sets up dependencies required to build and work with the SFS Client. - -.DESCRIPTION -This script will install all of the dependencies required to build and work with the SFS Client. -Use this on Windows platforms in a PowerShell session. - -.EXAMPLE -PS> .\scripts\Setup.ps1 -#> - -$ErrorActionPreference = "Stop" - -$GitRoot = (Resolve-Path (&git -C $PSScriptRoot rev-parse --show-toplevel)).Path - -function Update-Env { - $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") -} - -function Install-Python { - Write-Host -ForegroundColor Cyan "Installing latest Python if it's not installed" - - # We use the "python --version" command to check if it exists because by default Windows now comes - # with a python.exe from the Store that simply opens the Store when someone tries to use python.exe - python --version 2>&1 | Out-Null - if (!$?) { - winget install python - if (!$?) { - Write-Host -ForegroundColor Red "Failed to install Python" - exit 1 - } - } -} - -function Install-PipDependencies { - Write-Host -ForegroundColor Cyan "`nInstalling dependencies using pip" - - # Upgrade pip and install requirements. Filter out output for dependencies that are already installed - $PipInstalledPackageString = "Requirement already satisfied" - python -m pip install --upgrade pip | Select-String -Pattern $PipInstalledPackageString -NotMatch - - # Installing to user site packages - $PipReqs = Join-Path $PSScriptRoot "pip.requirements.txt" -Resolve - pip install --user -r $PipReqs | Select-String -Pattern $PipInstalledPackageString -NotMatch -} - -function Install-CMake { - Write-Host -ForegroundColor Cyan "`nInstalling cmake if it's not installed" - - # Installing cmake from winget because it adds to PATH - try { - cmake --version - } - catch { - winget install cmake - if (!$?) { - Write-Host -ForegroundColor Red "Failed to install cmake" - exit 1 - } - - # Update env with newly installed cmake - Update-Env - } -} - -function Install-CppBuildTools { - Write-Host -ForegroundColor Cyan "`nInstalling C++ Builds tools if they are not installed" - - # Instaling vswhere, which will be used to query for the required build tools - try { - vswhere -? 2>&1 | Out-Null - } - catch { - winget install vswhere - if (!$?) { - Write-Host -ForegroundColor Red "Failed to install vswhere" - exit 1 - } - } - - # - Microsoft.VisualStudio.Workload.VCTools is the C++ workload in the Visual Studio Build Tools - # - Microsoft.VisualStudio.Workload.NativeDesktop is the C++ workload that comes pre-installed in the github runner image - $ExistingBuildTools = vswhere -products * -requires Microsoft.VisualStudio.Workload.VCTools Microsoft.VisualStudio.Workload.NativeDesktop -requiresAny -format json | ConvertFrom-Json - if ($null -eq $ExistingBuildTools) - { - Write-Host "`nTools not found, installing..." - - # --wait makes the install synchronous - winget install Microsoft.VisualStudio.2022.BuildTools --silent --override "--wait --quiet --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --remove Microsoft.VisualStudio.Component.VC.CMake.Project" - if (!$?) { - Write-Host -ForegroundColor Red "Failed to install build tools" - exit 1 - } - } -} - -function Install-Vcpkg { - Write-Host -ForegroundColor Cyan "`nSetting up vcpkg" - - if (!(Test-Path vcpkg)) { - Write-Host "Cloning vcpkg repo" - git clone https://github.com/microsoft/vcpkg $GitRoot\vcpkg - } - else { - Write-Host "Checking if vcpkg repo has new commits" - $NoUpdatesString = "Already up to date." - git -C "$GitRoot\vcpkg" pull --show-forced-updates | Select-String -Pattern $NoUpdatesString -NotMatch - } - - # Bootstrapping on every setup updates the vcpkg.exe and solves potential issues with VS Build Tools not being found - & "$GitRoot\vcpkg\bootstrap-vcpkg.bat" -} - -function Set-GitHooks { - Write-Host -ForegroundColor Cyan "`nSetting Git hooks" - - $HookDestDir = Join-Path $GitRoot "\.git\hooks" -Resolve - $GitHooks = @{"pre-commit-wrapper.sh" = "pre-commit" } - foreach ($i in $GitHooks.GetEnumerator()) { - $HookSrc = Join-Path $GitRoot $i.Name -Resolve - $HookDest = Join-Path $HookDestDir $i.Value - - # If the destination doesn't exist or is different than the one in the source, we'll copy it over. - if (-not (Test-Path $HookDest) -or (Get-FileHash $HookDest).Hash -ne (Get-FileHash $HookSrc).Hash) { - Copy-Item -Path $HookSrc -Destination $HookDest -Force | Out-Null - Write-Host -ForegroundColor Cyan "Setup git $($i.Value) hook with $HookSrc" - } - } -} - -function Set-Aliases { - Write-Host -ForegroundColor Cyan "`nSetting aliases" - - $PythonScriptsDir = python -c "import os,sysconfig;print(sysconfig.get_path('scripts',f'{os.name}_user'))" - - $Aliases = [ordered]@{ - "build" = "$GitRoot\scripts\Build.ps1" - "clang-format" = "$PythonScriptsDir\clang-format.exe" - "cmake-format" = "$PythonScriptsDir\cmake-format.exe" - "test" = "$GitRoot\scripts\Test.ps1" - "vcpkg" = "$GitRoot\vcpkg\vcpkg.exe" - } - foreach ($i in $Aliases.GetEnumerator()) { - $Alias = $i.Name - $Target = $i.Value - - # If the alias doesn't exist or is set to something different than the one we want to target, we'll add it. - $AliasDoesNotExist = !(Get-Alias $Alias -ErrorAction SilentlyContinue) - if ($AliasDoesNotExist -or (Get-Alias $Alias).Definition -ne $Target) { - if (!$AliasDoesNotExist) { - Remove-Alias $Alias -Scope Global - } - New-Alias -Name $Alias -Value $Target -Scope Global - Write-Host "Setup alias $Alias" - } - } -} - -Install-Python -Install-PipDependencies -Install-CMake -Install-CppBuildTools -Install-Vcpkg -Set-GitHooks -Set-Aliases diff --git a/src/SfsClient/sfs-client/scripts/Test.ps1 b/src/SfsClient/sfs-client/scripts/Test.ps1 deleted file mode 100644 index 3e659bf5ef..0000000000 --- a/src/SfsClient/sfs-client/scripts/Test.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -<# -.SYNOPSIS -Simplifies test commands for the SFS Client. - -.DESCRIPTION -This script will contain the test commands for the SFS Client. -Use this on Windows platforms in a PowerShell session. - -.EXAMPLE -PS> ./scripts/Test.ps1 -#> -param ( - [switch] $OutputOnFailure = $false -) - -$GitRoot = (Resolve-Path (&git -C $PSScriptRoot rev-parse --show-toplevel)).Path -$BuildFolder = "$GitRoot/build" - -$cmd = "ctest --test-dir ""$BuildFolder/client""" -if ($OutputOnFailure) -{ - $cmd += " --output-on-failure" -} - -Invoke-Expression $cmd diff --git a/src/SfsClient/sfs-client/scripts/build.sh b/src/SfsClient/sfs-client/scripts/build.sh deleted file mode 100755 index 2a72fc5fd4..0000000000 --- a/src/SfsClient/sfs-client/scripts/build.sh +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/bash - -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# Synopsis: Simplifies build commands for the SFS Client. -# -# Description: This script will contain the build commands for the SFS Client. The default build folder will be "/build". -# Use this on non-Windows platforms in a bash session. -# -# Example: -# $ ./scripts/build.sh -# - -# Ensures script stops on errors -set -e - -if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then - error "Script is being sourced, it should be executed instead." - return 1 -fi - -COLOR_RED="\033[1;31m" -COLOR_YELLOW="\033[1;33m" -BOLD_DEFAULT_COLOR="\033[1m" -NO_COLOR="\033[0m" - -error() { echo -e "${COLOR_RED}$*${NO_COLOR}" >&2; exit 1; } -warn() { echo -e "${COLOR_YELLOW}$*${NO_COLOR}"; } - -clean=false -enable_test_overrides="OFF" -build_tests="ON" -build_samples="ON" -build_type="Debug" - -usage() { echo -e "Usage: $0 [-c|--clean] [-b|--build-type {Debug,Release}] [-t|--enable-test-overrides] - [--build-tests {${BOLD_DEFAULT_COLOR}ON${NO_COLOR}, OFF}] [--build-samples {${BOLD_DEFAULT_COLOR}ON${NO_COLOR}, OFF}]" 1>&2; exit 1; } - -# Make sure when adding a new option to check if it requires CMake regeneration - -if ! opts=$(getopt \ - --longoptions "clean,build-type:,enable-test-overrides,build-tests:,build-samples:" \ - --name "$(basename "$0")" \ - --options "cb:t" \ - -- "$@" -); then - usage -fi - -eval set "--$opts" - -while [ $# -gt 0 ]; do - case "$1" in - -c|--clean) - clean=true - shift 1 - ;; - -b|--build-type) - shift 1 - build_type=$1 - case "$build_type" in - Debug|Release) - ;; - *) - usage - ;; - esac - shift 1 - ;; - -t|--enable-test-overrides) - enable_test_overrides="ON" - shift 1 - ;; - --build-tests) - shift 1 - build_tests=$1 - case "$build_tests" in - ON|OFF) - ;; - *) - usage - ;; - esac - shift 1 - ;; - --build-samples) - shift 1 - build_samples=$1 - case "$build_samples" in - ON|OFF) - ;; - *) - usage - ;; - esac - shift 1 - ;; - *) - break - ;; - esac -done - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" -git_root=$(git -C "$script_dir" rev-parse --show-toplevel) - -vcpkg_dir="$git_root/vcpkg" -if [ ! -d "$vcpkg_dir" ]; then - error "vcpkg not found at $git_root/vcpkg. Source the setup.sh script first." -fi - -build_folder="$git_root/build" -if $clean && [ -d "$build_folder" ]; then - warn "Cleaning build folder before build..." - rm -r -f "$build_folder" -fi - -regenerate=false -cmake_cache_file="$build_folder/CMakeCache.txt" - -test_cmake_cache_value_no_match() { - local cmake_cache_file=$1 - local pattern=$2 - local expected_value=$3 - - value=$(sed -nr "s/$pattern/\1/p" "$cmake_cache_file") - if [ -n "$value" ] && [ "$value" == "$expected_value" ]; then - return 1 - fi - return 0 -} - -if [ -f "$cmake_cache_file" ]; then - # Regenerate if one of the build options is set to a different value than the one passed in - if test_cmake_cache_value_no_match "$cmake_cache_file" "^CMAKE_BUILD_TYPE:STRING=(.*)$" "$build_type"; then - regenerate=true - fi - if test_cmake_cache_value_no_match "$cmake_cache_file" "^SFS_ENABLE_TEST_OVERRIDES:BOOL=(.*)$" "$enable_test_overrides"; then - regenerate=true - fi - if test_cmake_cache_value_no_match "$cmake_cache_file" "^SFS_BUILD_TESTS:BOOL=(.*)$" "$build_tests"; then - regenerate=true - fi - if test_cmake_cache_value_no_match "$cmake_cache_file" "^SFS_BUILD_SAMPLES:BOOL=(.*)$" "$build_samples"; then - regenerate=true - fi -fi - -# Configure cmake if build folder doesn't exist or if the build must be regenerated. -# This creates build targets that will be used by the build command -if [ ! -d "$build_folder" ] || $regenerate ; then - cmake \ - -S "$git_root" \ - -B "$build_folder" \ - -DCMAKE_BUILD_TYPE="$build_type" \ - -DSFS_ENABLE_TEST_OVERRIDES="$enable_test_overrides" \ - -DSFS_BUILD_TESTS="$build_tests" \ - -DSFS_BUILD_SAMPLES="$build_samples" -fi - -# This is the build command. If any CMakeLists.txt files change, this will also reconfigure before building -cmake --build "$build_folder" diff --git a/src/SfsClient/sfs-client/scripts/check-format.py b/src/SfsClient/sfs-client/scripts/check-format.py deleted file mode 100644 index 416b852055..0000000000 --- a/src/SfsClient/sfs-client/scripts/check-format.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -"""Checks the formatting of the codebase and fails if it is not correct.""" - -import os -import subprocess -import sys -import sysconfig - -script_dir = os.path.dirname(os.path.realpath(__file__)) -root_dir = os.path.join(script_dir, "..") -os.chdir(root_dir) - -user_dir = sysconfig.get_path("scripts",f"{os.name}_user") -if os.name == 'nt': - clang_format = "{}\clang-format.exe".format(user_dir) - cmake_format = "{}\cmake-format.exe".format(user_dir) -else: - clang_format = "{}/clang-format".format(user_dir) - cmake_format = "{}/cmake-format".format(user_dir) - -if not os.path.exists(clang_format): - print("clang-format not found at: {}".format(clang_format)) - sys.exit(1) - -if not os.path.exists(cmake_format): - print("cmake-format not found at: {}".format(cmake_format)) - sys.exit(1) - -cmake_filename='CMakeLists.txt' - -# Find all the interesting files in the repository -clang_files = [] -cmake_files = [] - -all_files = subprocess.check_output('git ls-files', shell=True, text=True).strip('\n').split('\n') - -for file in all_files: - if file.endswith('.cpp') or file.endswith('.h'): - clang_files.append(file) - if file.endswith(cmake_filename): - cmake_files.append(file) - -# Run clang and cmake-format on all the interesting files -unformatted_files = [] -for file in clang_files: - # When the file is unformatted, clang-format returns a zero exit code, but a non-empty stderr - result = subprocess.check_output("{} {} -n".format(clang_format, file), shell=True, text=True, stderr=subprocess.STDOUT) - if len(result) > 0: - unformatted_files.append(file) -for file in cmake_files: - # When the file is unformatted, cmake-format returns a non-zero exit code - result = subprocess.run("{} {} --check".format(cmake_format, file), stderr=subprocess.DEVNULL, shell=True) - if result.returncode != 0: - unformatted_files.append(file) - -unformatted_files.sort() - -if unformatted_files: - print("The following files have incorrect formatting:\n") - for file in unformatted_files: - print(file) - sys.exit(1) - -print("All files have correct formatting.") diff --git a/src/SfsClient/sfs-client/scripts/pip.requirements.txt b/src/SfsClient/sfs-client/scripts/pip.requirements.txt deleted file mode 100644 index bae1499f62..0000000000 --- a/src/SfsClient/sfs-client/scripts/pip.requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -clang-format==19.1.1 -cmake-format==0.6.13 \ No newline at end of file diff --git a/src/SfsClient/sfs-client/scripts/setup.sh b/src/SfsClient/sfs-client/scripts/setup.sh deleted file mode 100755 index 27900fbb65..0000000000 --- a/src/SfsClient/sfs-client/scripts/setup.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# Synopsis: Sets up dependencies required to build and work with the SFS Client. -# -# Description: This script will install all of the dependencies required to build and work with the SFS Client. -# Use this on non-Windows platforms in a bash session. It must be sourced. -# -# Example: -# $ source ./scripts/setup.sh -# - -COLOR_RED="\033[1;31m" -COLOR_CYAN="\033[1;36m" -NO_COLOR="\033[0m" - -header() { echo -e "${COLOR_CYAN}$*${NO_COLOR}"; } -error() { echo -e "${COLOR_RED}$*${NO_COLOR}" >&2; } - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - error "Script is being run directly, it should be sourced instead." - exit 1 -fi - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" -git_root=$(git -C "$script_dir" rev-parse --show-toplevel) - -sudo apt-get -qq update - -install_with_apt() { - program_to_install=$1 - - if ! sudo apt-get -qq install "$program_to_install"; then - error "Failed to install $program_to_install" - return 1 - fi - return 0 -} - -install_python() { - header "Installing latest Python if it's not installed" - - if ! install_with_apt "python3"; then - return - fi - if ! install_with_apt "python3-pip"; then - return - fi - return 0 -} - -install_pip_dependencies() { - header "\nInstalling dependencies using pip" - - # Upgrade pip and install requirements. Filter out output for dependencies that are already installed - python3 -m pip install --upgrade pip | grep -v -e "already satisfied" -e "Defaulting to user installation" - ret="${PIPESTATUS[0]}" - if [ "$ret" -ne 0 ]; then - return "$ret" - fi - - pip_reqs="$script_dir/pip.requirements.txt" - - pip install -r "$pip_reqs" | grep -v -e "already satisfied" -e "Defaulting to user installation" - ret="${PIPESTATUS[0]}" - if [ "$ret" -ne 0 ]; then - return "$ret" - fi - - return 0 -} - -install_cmake() { - header "\nInstalling cmake if it's not installed" - - if ! install_with_apt "cmake"; then - return - fi - return 0 -} - -install_vcpkg() { - header "\nSetting up vcpkg" - - vcpkg_dir="$git_root/vcpkg" - if [ -d "$vcpkg_dir" ]; then - echo "Checking if vcpkg repo has new commits" - git -C "$vcpkg_dir" pull --show-forced-updates | grep -v 'Already up to date' - else - echo "Cloning vcpkg repo" - git clone https://github.com/microsoft/vcpkg "$vcpkg_dir" - # Needed for the bootstrap and for other vcpkg packages - if ! sudo apt-get -qq install curl zip unzip tar pkg-config; then - return - fi - "$vcpkg_dir/bootstrap-vcpkg.sh" - fi - return 0 -} - -# Dependency for compiling CorrelationVector C++ library -install_uuid() { - header "\nInstalling uuid-dev if it's not installed" - - if ! install_with_apt "pkg-config"; then - return - fi - if ! install_with_apt "uuid-dev"; then - return - fi - return 0 -} - -set_git_hooks() { - header "\nSetting Git hooks" - - hook_dest_dir="$git_root/.git/hooks" - declare -A git_hooks=( ["pre-commit-wrapper.sh"]="pre-commit") - for src in "${!git_hooks[@]}"; do - hook_src="$git_root/$src" - hook_dest="$hook_dest_dir/${git_hooks[$src]}" - - # If the destination doesn't exist or is different than the one in the source, we'll copy it over. - if [ ! -f "$hook_dest" ] || ! cmp -s "$hook_src" "$hook_dest"; then - cp -f "$hook_src" "$hook_dest" - echo "Setup git ${git_hooks[$src]} hook with $hook_src" - fi - done -} - -set_aliases() { - header "\nSetting aliases" - - python_scripts_dir=$(python3 -c 'import os,sysconfig;print(sysconfig.get_path("scripts",f"{os.name}_user"))') - - declare -A aliases=( ["build"]="$git_root/scripts/build.sh" - ["clang-format"]="$python_scripts_dir/clang-format" - ["cmake-format"]="$python_scripts_dir/cmake-format" - ["test"]="$git_root/scripts/test.sh" - ["vcpkg"]="$git_root/vcpkg/vcpkg") - - for alias in "${!aliases[@]}"; do - target="${aliases[$alias]}" - - # If the alias doesn't exist or is set to something different than the one we want to target, we'll add it. - - # If alias doesn't exist, the alias command returns a non-zero result - alias_does_not_exist=false - alias_exists_not_the_same=false - if ! alias "$alias" > /dev/null 2>&1; then - alias_does_not_exist=true - else - output=$(alias "$alias") - expected_output="alias $alias='$target'" - if [ "$output" != "$expected_output" ]; then - alias_exists_not_the_same=true - fi - fi - - if $alias_does_not_exist || $alias_exists_not_the_same; then - eval "alias $alias=$target" - echo "Setup alias $alias" - fi - done -} - -if ! install_python; then - return -fi - -if ! install_pip_dependencies; then - return -fi - -if ! install_cmake; then - return -fi - -if ! install_vcpkg; then - return -fi - -if ! install_uuid; then - return -fi - -set_git_hooks -set_aliases diff --git a/src/SfsClient/sfs-client/scripts/test.sh b/src/SfsClient/sfs-client/scripts/test.sh deleted file mode 100755 index a45069f825..0000000000 --- a/src/SfsClient/sfs-client/scripts/test.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# Synopsis: Simplifies test commands for the SFS Client. -# -# Description: This script will contain the test commands for the SFS Client. -# Use this on non-Windows platforms in a bash session. -# -# Example: -# $ ./scripts/test.sh -# - -# Ensures script stops on errors -set -e - -if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then - error "Script is being sourced, it should be executed instead." - return 1 -fi - -output_on_failure=false - -usage() { echo "Usage: $0 [--output-on-failure]" 1>&2; exit 1; } - -if ! opts=$(getopt \ - --longoptions "output-on-failure" \ - --name "$(basename "$0")" \ - --options "" \ - -- "$@" -); then - usage -fi - -eval set "--$opts" - -while [ $# -gt 0 ]; do - case "$1" in - --output-on-failure) - output_on_failure=true - shift 1 - ;; - *) - break - ;; - esac -done - -script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" -git_root=$(git -C "$script_dir" rev-parse --show-toplevel) -build_folder="$git_root/build" - -cmd="ctest --test-dir \"$build_folder/client\"" - -if $output_on_failure ; then - cmd+=" --output-on-failure" -fi - -eval "$cmd" diff --git a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/portfile.cmake b/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/portfile.cmake deleted file mode 100644 index ffc82dd130..0000000000 --- a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/portfile.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# This file is a template portfile to be used to consume the sfs-client library using the vcpkg infrastructure - -# Choose one of the consumption formats below. SOURCE_PATH must be defined after either step. - -# 1. Get from Github -# vcpkg_from_github( -# OUT_SOURCE_PATH SOURCE_PATH -# REPO microsoft/sfs-client -# REF -# SHA512 0 # Run vcpkg the first time and this value will be shown in the logs for you to replace -# HEAD_REF main -# ) - -# 2. Use a locally available path, like a subtree'd repository -# set(SOURCE_PATH D:/sfs-client) - -# Preferring static linkage in Windows -if(VCPKG_TARGET_IS_WINDOWS) - vcpkg_check_linkage(ONLY_STATIC_LIBRARY) -endif() - -# Configure and install the library -vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}") -vcpkg_cmake_install() -vcpkg_fixup_pkgconfig() - -# Exposes the package targets like other vcpkg packages do -vcpkg_cmake_config_fixup(PACKAGE_NAME sfsclient CONFIG_PATH lib/cmake/sfsclient) - -# Handle copyright -vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") - -# Handle usage -file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") - -# Remove duplicated include files -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") diff --git a/src/SfsClient/sfs-client/vcpkg-custom-triplets/x64-windows-static-custom.cmake b/src/SfsClient/sfs-client/vcpkg-custom-triplets/x64-windows-static-custom.cmake deleted file mode 100644 index 1318b7aee5..0000000000 --- a/src/SfsClient/sfs-client/vcpkg-custom-triplets/x64-windows-static-custom.cmake +++ /dev/null @@ -1,6 +0,0 @@ -set(VCPKG_TARGET_ARCHITECTURE x64) - -message(STATUS "PORTs are preferred with static linkage...") - -set(VCPKG_CRT_LINKAGE static) -set(VCPKG_LIBRARY_LINKAGE static) diff --git a/src/SfsClient/sfs-client/vcpkg.json b/src/SfsClient/sfs-client/vcpkg.json deleted file mode 100644 index 0d6c4d3fc1..0000000000 --- a/src/SfsClient/sfs-client/vcpkg.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", - "$in-case-of-update": "If updating this file, please also update the cgmanifest.json file. See DEVELOPMENT.md for more", - "features": { - "tests": { - "description": "Build tests", - "dependencies": [ - "catch2", - "cpp-httplib" - ] - } - }, - "dependencies": [ - "correlation-vector-cpp", - { - "name": "curl", - "features": [ - "c-ares", - { - "name": "openssl", - "platform": "!windows", - "$comment": "TODO #42: OpenSSL is not needed on Windows yet while we don't have cert pinning. Default is schannel, which uses Windows store" - } - ] - }, - "nlohmann-json" - ], - "overrides": [ - { - "name": "c-ares", - "version": "1.19.1" - }, - { - "name": "catch2", - "version": "3.4.0" - }, - { - "name": "cpp-httplib", - "version": "0.15.3" - }, - { - "name": "correlation-vector-cpp", - "version": "1.0" - }, - { - "name": "curl", - "version": "8.9.1" - }, - { - "name": "nlohmann-json", - "version": "3.11.3" - }, - { - "name": "openssl", - "version": "3.1.4#1" - } - ], - "vcpkg-configuration": { - "overlay-triplets": [ - "./vcpkg-custom-triplets" - ] - }, - "builtin-baseline": "f56238700757aa05975e41fa835739c632810f3f" -} \ No newline at end of file diff --git a/src/VcpkgPortOverlay/CreatePortOverlay.ps1 b/src/VcpkgPortOverlay/CreatePortOverlay.ps1 index f3ff774fb8..13003328a5 100644 --- a/src/VcpkgPortOverlay/CreatePortOverlay.ps1 +++ b/src/VcpkgPortOverlay/CreatePortOverlay.ps1 @@ -50,24 +50,27 @@ function Get-GitTreeAsArchive { param( [Parameter(Mandatory)] - [string]$GitTree + [string]$GitTree, + [string]$Repo = 'microsoft/vcpkg' ) - $archiveUri = "https://github.com/microsoft/vcpkg/archive/$gitTree.zip" + $archiveUri = "https://github.com/$Repo/archive/$gitTree.zip" $response = Invoke-WebRequest -Uri $archiveUri $zipStream = [System.IO.MemoryStream]::new($response.Content) $zipArchive = [System.IO.Compression.ZipArchive]::new($zipStream) return $zipArchive } -# Expands an in-memory archive and writes it to disk +# Expands an in-memory archive and writes it to disk. +# If SubPath is specified, only files under that path are extracted. function Expand-ArchiveFromMemory { param( [Parameter(Mandatory)] [System.IO.Compression.ZipArchive]$Archive, [Parameter(Mandatory)] - [string]$Destination + [string]$Destination, + [string]$SubPath ) # Delete existing directory @@ -78,6 +81,10 @@ function Expand-ArchiveFromMemory # Remove length=0 to ignore the directory itself $entries = $archive.Entries | Where-Object { $_.Length -ne 0 } + if ($SubPath) + { + $entries = $entries | Where-Object { $_.FullName -like "*/$SubPath/*" } + } if (-not $entries) { throw "Archive is empty" @@ -109,6 +116,99 @@ function New-PortOverlay Expand-ArchiveFromMemory $archive $portDir } +# Creates a copy of a port from a GitHub repository in this overlay, +# by downloading the repository at a specific commit and extracting a subdirectory. +function New-PortOverlayFromGitHub +{ + param( + [Parameter(Mandatory)] + [string]$Port, + [Parameter(Mandatory)] + [string]$Repo, # as user/repo + [Parameter(Mandatory)] + [string]$Commit, + [Parameter(Mandatory)] + [string]$SubPath + ) + + $archive = Get-GitTreeAsArchive -GitTree $Commit -Repo $Repo + $portDir = Join-Path $OverlayRoot $Port + Expand-ArchiveFromMemory -Archive $archive -Destination $portDir -SubPath $SubPath +} + +# Expands a portfile.cmake that uses a "commented-option" template format, where the active +# function call is commented out. Uncomments the specified function block, strips remaining +# comment lines, and collapses blank lines. +function Expand-PortfileTemplate +{ + param( + [Parameter(Mandatory)] + [string]$Port, + [string]$CommentedFunction = 'vcpkg_from_github' + ) + + $portFilePath = Join-Path $OverlayRoot $Port 'portfile.cmake' + $lines = Get-Content $portFilePath + + $result = [System.Collections.Generic.List[string]]::new() + $inCommentedBlock = $false + $prevWasBlank = $false + + foreach ($line in $lines) + { + if ($line -match "^# $([regex]::Escape($CommentedFunction))\(") + { + $inCommentedBlock = $true + $result.Add("$CommentedFunction(") + $prevWasBlank = $false + continue + } + + if ($inCommentedBlock) + { + if ($line.TrimEnd() -eq '# )') + { + $result.Add(')') + $inCommentedBlock = $false + } + else + { + $uncommented = $line -replace '^# ?', '' + # Strip trailing inline hint comments from template lines + $uncommented = $uncommented -replace ' # .*$', '' + $result.Add($uncommented) + } + $prevWasBlank = $false + continue + } + + # Skip comment-only lines (template instructions, disabled options, etc.) + if ($line -match '^#') { continue } + + if ($line -eq '') + { + if (-not $prevWasBlank -and $result.Count -gt 0) + { + $result.Add('') + } + $prevWasBlank = $true + } + else + { + $result.Add($line) + $prevWasBlank = $false + } + } + + # Remove trailing blank lines + while ($result.Count -gt 0 -and $result[$result.Count - 1] -eq '') + { + $result.RemoveAt($result.Count - 1) + } + + $result | Out-File $portFilePath +} + # Gets a git patch from a GitHub commit function Get-GitHubPatch { @@ -274,6 +374,45 @@ function Add-PatchToPort Add-PatchToPortFile -Port $Port -PatchName $PatchName } +# Adds a patch from the local patches/ directory to a port. +# Patches stored in patches// are committed to the repo and survive port regeneration. +function Add-LocalPatch +{ + param( + [Parameter(Mandatory)] + [string]$Port, + [Parameter(Mandatory)] + [string]$PatchName + ) + + Copy-Item (Join-Path $OverlayRoot 'patches' $Port $PatchName) (Join-Path $OverlayRoot $Port) + + $portFilePath = Join-Path $OverlayRoot $Port 'portfile.cmake' + $lines = Get-Content $portFilePath + $hasPatchesKeyword = $lines | Where-Object { $_ -match '\bPATCHES\b' } + + if ($hasPatchesKeyword) + { + Add-PatchToPortFile -Port $Port -PatchName $PatchName + return + } + + # Add PATCHES keyword and the patch name before the closing paren of vcpkg_from_github + $result = @() + $foundParen = $false + foreach ($line in $lines) + { + if (-not $foundParen -and $line -eq ')') + { + $result += ' PATCHES' + $result += " $PatchName" + $foundParen = $true + } + $result += $line + } + $result | Out-File $portFilePath +} + # Sets the value of an existing function parameter. # For example, REF in vcpkg_from_github function Set-ParameterInPortFile @@ -303,6 +442,61 @@ function Set-ParameterInPortFile $modifiedPortFile | Out-File $portFilePath } +# Sets cmake configure options for a port by expanding the vcpkg_cmake_configure call. +function Set-CmakeConfigureOptions +{ + param( + [Parameter(Mandatory)] + [string]$Port, + [Parameter(Mandatory)] + [string[]]$Options + ) + + $portFilePath = Join-Path $OverlayRoot $Port 'portfile.cmake' + $originalPortFile = Get-Content $portFilePath + + $modifiedPortFile = @() + foreach ($line in $originalPortFile) + { + if ($line -match '^vcpkg_cmake_configure\(SOURCE_PATH') + { + $modifiedPortFile += 'vcpkg_cmake_configure(' + $modifiedPortFile += ' SOURCE_PATH "${SOURCE_PATH}"' + $modifiedPortFile += ' OPTIONS' + foreach ($option in $Options) + { + $modifiedPortFile += " $option" + } + $modifiedPortFile += ')' + } + else + { + $modifiedPortFile += $line + } + } + + $modifiedPortFile | Out-File $portFilePath +} + +# Replaces a string in a file within a port directory. +function Set-PortFilePlaceholder +{ + param( + [Parameter(Mandatory)] + [string]$Port, + [Parameter(Mandatory)] + [string]$File, + [Parameter(Mandatory)] + [string]$Placeholder, + [Parameter(Mandatory)] + [string]$Value + ) + + $filePath = Join-Path $OverlayRoot $Port $File + $content = (Get-Content -Raw $filePath) -replace "<$Placeholder>", $Value + [System.IO.File]::WriteAllText($filePath, $content, [System.Text.Encoding]::UTF8) +} + # Updates the source commit used for a port. # Takes the commit hash, and the hash of the archive with the code that vcpkg will download. function Update-PortSource @@ -337,7 +531,7 @@ function Update-PortVersion } New-PortOverlay cpprestsdk -Version 2.10.18 -PortVersion 4 -Add-PatchToPort cpprestsdk -PatchRepo 'microsoft/winget-cli' -PatchCommit '888b4ed8f4f7d25cb05a47210e083fe29348163b' -PatchName 'add-server-certificate-validation.patch' -PatchRoot 'src/cpprestsdk/cpprestsdk' +Add-LocalPatch cpprestsdk 'add-server-certificate-validation.patch' New-PortOverlay detours -Version 4.0.1 -PortVersion 8 Update-PortSource detours -RefPattern 'v4.0.1' -Commit '404c153ff390cb14f1787c7feeb4908c6d79b0ab' -SourceHash '1f3f26657927fa153116dce13dbfa3319ea368e6c9017f4999b6ec24d6356c335b3d5326718d3ec707b92832763ffea092088df52596f016d7ca9b8127f7033d' @@ -346,3 +540,19 @@ Remove-PortPatches detours New-PortOverlay libyaml -Version 0.2.5 -PortVersion 5 Update-PortSource libyaml -Commit '840b65c40675e2d06bf40405ad3f12dec7f35923' -SourceHash 'de85560312d53a007a2ddf1fe403676bbd34620480b1ba446b8c16bb366524ba7a6ed08f6316dd783bf980d9e26603a9efc82f134eb0235917b3be1d3eb4b302' Update-PortVersion libyaml + +# sfs-client is not in the official vcpkg registry. +# The port is based on the template from the sfs-client repository. +# See: https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client +$SfsClientCommit = '0e27525d597c730e71646fd0b15bdc8c8503f24d' +$SfsClientSha512 = 'd926d7fdbbd120cbcbd9732a3300cccfeed4a90d6b94456d73a70675df3578a91127f7e9f310fe68d18fa34bb997c29c8455e586d81a2ba404cf19193a80ca6e' +$SfsClientVersion = '1.1.0' + +New-PortOverlayFromGitHub 'sfs-client' -Repo 'microsoft/sfs-client' -Commit $SfsClientCommit -SubPath 'sfs-client-vcpkg-port/sfs-client' +Expand-PortfileTemplate 'sfs-client' +Set-PortFilePlaceholder 'sfs-client' 'portfile.cmake' -Placeholder 'commit-id' -Value $SfsClientCommit +Set-ParameterInPortFile 'sfs-client' -ParameterName 'SHA512' -CurrentValuePattern '0' -NewValue $SfsClientSha512 +Set-CmakeConfigureOptions 'sfs-client' -Options @('-DSFS_BUILD_TESTS=OFF', '-DSFS_BUILD_SAMPLES=OFF') +Set-PortFilePlaceholder 'sfs-client' 'vcpkg.json' -Placeholder 'VERSION' -Value $SfsClientVersion + +Add-LocalPatch 'sfs-client' 'remove-unconditional-toolchain-override.patch' diff --git a/src/VcpkgPortOverlay/README.md b/src/VcpkgPortOverlay/README.md index 9b38261af4..eb9c86de9e 100644 --- a/src/VcpkgPortOverlay/README.md +++ b/src/VcpkgPortOverlay/README.md @@ -1,34 +1,41 @@ -# Overlay ports - -This directory contains an overlay for vcpkg ports, for cases where we need local modifications to a port. -In all cases, most of the recipe is taken from the [official vcpkg registry](https://github.com/Microsoft/vcpkg), and we only make small changes. - -The whole directory can be re-created with `.\CreatePortOverlay.ps1` - -## cpprestsdk - -We add support for certificate pinning. -Note that we use v2.10.18, which is not the latest. - -Changes: -* Add patch file: `add-server-certificate-validation.patch` -* Patch source: https://github.com/microsoft/winget-cli/commit/888b4ed8f4f7d25cb05a47210e083fe29348163b - -## detours - -We use the version used by UndockedRegFreeWinRT (https://github.com/microsoft/winget-cli/tree/release-v1.10/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/detours). -The only official release of detours (4.0.1) does not include complete support for ARM64. -While the exact version that we pulled from UndockedRegFreeWinRT is unclear (https://github.com/microsoft/xlang/pull/644), through manually comparing versions it is equivalent to -https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab (only some whitespace changes are present). - -Changes: -* New source commit: https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab -* Remove the patch on the official port as it is already present in the newer commit - -## libyaml - -We use an unreleased version that fixes a vulnerability. - -Changes: -* New source commit: https://github.com/yaml/libyaml/commit/840b65c40675e2d06bf40405ad3f12dec7f35923 +# Overlay ports + +This directory contains an overlay for vcpkg ports, for cases where we need local modifications to a port. +In all cases, most of the recipe is taken from the [official vcpkg registry](https://github.com/Microsoft/vcpkg), and we only make small changes. + +The whole directory can be re-created with `.\CreatePortOverlay.ps1` + +## cpprestsdk + +We add support for certificate pinning. +Note that we use v2.10.18, which is not the latest. + +Changes: +* Add patch file: `add-server-certificate-validation.patch` +* Patch source: https://github.com/microsoft/winget-cli/commit/888b4ed8f4f7d25cb05a47210e083fe29348163b + +## detours + +We use the version used by UndockedRegFreeWinRT (https://github.com/microsoft/winget-cli/tree/release-v1.10/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/detours). +The only official release of detours (4.0.1) does not include complete support for ARM64. +While the exact version that we pulled from UndockedRegFreeWinRT is unclear (https://github.com/microsoft/xlang/pull/644), through manually comparing versions it is equivalent to +https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab (only some whitespace changes are present). + +Changes: +* New source commit: https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab +* Remove the patch on the official port as it is already present in the newer commit + +## sfs-client + +sfs-client is not in the official vcpkg registry. The port is based on the [template included in the sfs-client repository](https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client). + +We use version 1.1.0 at commit https://github.com/microsoft/sfs-client/commit/0e27525d597c730e71646fd0b15bdc8c8503f24d. +Tests and samples are disabled (`-DSFS_BUILD_TESTS=OFF -DSFS_BUILD_SAMPLES=OFF`). + +## libyaml + +We use an unreleased version that fixes a vulnerability. + +Changes: +* New source commit: https://github.com/yaml/libyaml/commit/840b65c40675e2d06bf40405ad3f12dec7f35923 * Increase the port version so that Component Governance doesn't see it as the vulnerable version anymore \ No newline at end of file diff --git a/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch b/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch new file mode 100644 index 0000000000..84149843d2 --- /dev/null +++ b/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch @@ -0,0 +1,169 @@ +From 888b4ed8f4f7d25cb05a47210e083fe29348163b Mon Sep 17 00:00:00 2001 +From: JohnMcPMS +Date: Wed, 27 Jul 2022 18:03:45 -0700 +Subject: [PATCH] Server certificate pinning for Store source (#2347) + +This change adds a generic certificate chain verification infrastructure for pinning certificate chains. It is specifically used to pin the Microsoft Store source by default. More sources may be pinned later, but currently the packaged index is less in need of it because it is already signed. + +The pinning configuration consists of 1 or more chains, only one of which needs to successfully validate the incoming certificate. This allows for rolling to a new certificate when needed. Each chain consists of a fixed set of certificates, which can each be configured to validate any or all of the following properties: + +- Public Key +- Subject +- Issuer + +If the certificate is configured to validate none of the values, it will allow any certificate through. + +An admin setting is added to disable pinning, both as an emergency measure in the event that there is a bug or rolled certificate that was not communicated, but also because there are test scenarios where the user actively wants to disable it (HTTPS redirection via something like Fiddler). + +The configuration can be loaded from JSON for future dynamic configuration, but it is currently only as a test hook to enable configuration via Group Policy. + +In order to better secure the source by default, reconfiguring (remove then add) the Store source manually will convert it back to the built-in values. This includes the pinning configuration. + +It was necessary to modify the cpprestsdk subtree to add a new callback. This enables the request handle to be passed back to our code when the server certificate is first available. We can then check the server certificate against the configured pinning chain, making a decision to terminate the request before it is sent. +--- + .github/actions/spelling/allow.txt | 7 +- + .github/actions/spelling/expect.txt | 10 + + src/AppInstallerCLI.sln | 8 + + src/AppInstallerCLIE2ETests/BaseCommand.cs | 6 +- + src/AppInstallerCLIE2ETests/Constants.cs | 11 + + .../GroupPolicyHelper.cs | 44 ++ + src/AppInstallerCLIE2ETests/SearchCommand.cs | 73 ++- + src/AppInstallerCLIE2ETests/SetUpFixture.cs | 2 +- + src/AppInstallerCLIE2ETests/SourceCommand.cs | 4 +- + src/AppInstallerCLIE2ETests/TestCommon.cs | 56 +- + src/AppInstallerCLIE2ETests/TestIndexSetup.cs | 6 +- + .../AppInstallerCLITests.vcxproj | 2 + + .../AppInstallerCLITests.vcxproj.filters | 3 + + src/AppInstallerCLITests/Certificates.cpp | 185 ++++++ + src/AppInstallerCLITests/Command.cpp | 2 +- + src/AppInstallerCLITests/Completion.cpp | 48 +- + src/AppInstallerCLITests/GroupPolicy.cpp | 60 +- + src/AppInstallerCLITests/HttpClientHelper.cpp | 23 + + src/AppInstallerCLITests/Sources.cpp | 41 ++ + src/AppInstallerCLITests/Strings.cpp | 9 + + src/AppInstallerCommonCore/AdminSettings.cpp | 146 +++-- + .../AppInstallerCommonCore.vcxproj | 4 + + .../AppInstallerCommonCore.vcxproj.filters | 9 + + .../AppInstallerStrings.cpp | 39 ++ + src/AppInstallerCommonCore/Certificates.cpp | 549 ++++++++++++++++++ + src/AppInstallerCommonCore/Errors.cpp | 2 + + src/AppInstallerCommonCore/GroupPolicy.cpp | 13 + + .../JsonSchemaValidation.cpp | 36 +- + .../Manifest/ManifestSchemaValidation.cpp | 3 +- + .../Public/AppInstallerErrors.h | 1 + + .../Public/AppInstallerStrings.h | 6 + + .../Public/winget/AdminSettings.h | 1 + + .../Public/winget/Certificates.h | 153 +++++ + .../Public/winget/GroupPolicy.h | 11 +- + .../Public/winget/JsonSchemaValidation.h | 5 +- + .../Public/winget/Resources.h | 68 ++- + src/AppInstallerCommonCore/Resources.cpp | 58 ++ + src/AppInstallerCommonCore/SHA256.cpp | 33 +- + src/AppInstallerCommonCore/pch.h | 1 + + .../AppInstallerRepositoryCore.vcxproj | 4 +- + .../Public/winget/RepositorySource.h | 8 +- + .../RepositorySource.cpp | 24 +- + .../Rest/RestSourceFactory.cpp | 6 +- + .../Rest/Schema/HttpClientHelper.cpp | 26 +- + .../Rest/Schema/HttpClientHelper.h | 10 +- + src/AppInstallerRepositoryCore/SourceList.cpp | 54 ++ + src/AppInstallerRepositoryCore/SourceList.h | 1 + + src/AppInstallerRepositoryCore/pch.h | 1 + + .../CertificateResources.h | 9 + + .../CertificateResources.rc | 69 +++ + .../CertificateResources.vcxitems | 28 + + .../CertificateResources.vcxitems.filters | 26 + + .../StoreIntermediate1.cer | Bin 0 -> 1527 bytes + src/CertificateResources/StoreLeaf1.cer | Bin 0 -> 2642 bytes + src/CertificateResources/StoreRoot1.cer | Bin 0 -> 914 bytes + src/CertificateResources/resource.h | 14 + + src/LocalhostWebServer/Program.cs | 14 +- + .../Run-LocalhostWebServer.ps1 | 2 +- + src/LocalhostWebServer/Startup.cs | 2 + + .../Properties/Resources.Designer.cs | 2 +- + .../WindowsPackageManager.vcxproj | 1 + + .../Release/include/cpprest/http_client.h | 27 + + .../src/http/client/http_client_winhttp.cpp | 12 + + 63 files changed, 1852 insertions(+), 226 deletions(-) + create mode 100644 src/AppInstallerCLITests/Certificates.cpp + create mode 100644 src/AppInstallerCommonCore/Certificates.cpp + create mode 100644 src/AppInstallerCommonCore/Public/winget/Certificates.h + create mode 100644 src/AppInstallerCommonCore/Resources.cpp + create mode 100644 src/CertificateResources/CertificateResources.h + create mode 100644 src/CertificateResources/CertificateResources.rc + create mode 100644 src/CertificateResources/CertificateResources.vcxitems + create mode 100644 src/CertificateResources/CertificateResources.vcxitems.filters + create mode 100644 src/CertificateResources/StoreIntermediate1.cer + create mode 100644 src/CertificateResources/StoreLeaf1.cer + create mode 100644 src/CertificateResources/StoreRoot1.cer + create mode 100644 src/CertificateResources/resource.h + +diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h +index fb7c6067ab..b862a5778f 100644 +--- a/Release/include/cpprest/http_client.h ++++ b/Release/include/cpprest/http_client.h +@@ -362,6 +362,32 @@ class http_client_config + if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle); + } + ++ /// ++ /// Sets a callback to enable custom handling when the server certificate is available. ++ /// ++ /// ++ /// The native_handle is the following type depending on the underlying platform: ++ /// Windows Desktop, WinHTTP - HINTERNET !!! Is only implemented to call in here !!! ++ /// Windows Runtime, WinRT - IXMLHTTPRequest2 * ++ /// All other platforms, Boost.Asio: ++ /// https - boost::asio::ssl::stream * ++ /// http - boost::asio::ip::tcp::socket * ++ /// ++ /// A user callback allowing for validation of the server certificate. ++ void set_nativehandle_servercertificate_validation(const std::function& callback) ++ { ++ m_nativehandle_servercertificate_validation = callback; ++ } ++ ++ /// ++ /// Invokes a user's callback to validate the server certificate. ++ /// ++ /// A internal implementation handle. ++ void invoke_nativehandle_servercertificate_validation(native_handle handle) const ++ { ++ if (m_nativehandle_servercertificate_validation) m_nativehandle_servercertificate_validation(handle); ++ } ++ + #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) + /// + /// Sets a callback to enable custom setting of the ssl context, at construction time. +@@ -418,6 +444,7 @@ class http_client_config + + std::function m_set_user_nativehandle_options; + std::function m_set_user_nativesessionhandle_options; ++ std::function m_nativehandle_servercertificate_validation; + + #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) + std::function m_ssl_context_callback; +diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp +index d6cdb5384a..5a517ec334 100644 +--- a/Release/src/http/client/http_client_winhttp.cpp ++++ b/Release/src/http/client/http_client_winhttp.cpp +@@ -2039,6 +2039,18 @@ class winhttp_client final : public _http_client_communicator + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: + { + p_request_context->on_send_request_validate_cn(); ++ ++ try ++ { ++ p_request_context->m_http_client->client_config().invoke_nativehandle_servercertificate_validation(hRequestHandle); ++ } ++ catch (...) ++ { ++ p_request_context->report_exception(std::current_exception()); ++ p_request_context->cleanup(); ++ return; ++ } ++ + return; + } + case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + diff --git a/src/VcpkgPortOverlay/patches/sfs-client/remove-unconditional-toolchain-override.patch b/src/VcpkgPortOverlay/patches/sfs-client/remove-unconditional-toolchain-override.patch new file mode 100644 index 0000000000..30115c567e --- /dev/null +++ b/src/VcpkgPortOverlay/patches/sfs-client/remove-unconditional-toolchain-override.patch @@ -0,0 +1,12 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -12,8 +12,6 @@ if(SFS_BUILD_TESTS) + list(APPEND VCPKG_MANIFEST_FEATURES "tests") + endif() + +-set(CMAKE_TOOLCHAIN_FILE "vcpkg/scripts/buildsystems/vcpkg.cmake") +- + # By default using x64 static custom triplet for Windows. Can be overridden by + # setting VCPKG_TARGET_TRIPLET + if(SFS_WINDOWS_STATIC_ONLY diff --git a/src/VcpkgPortOverlay/sfs-client/portfile.cmake b/src/VcpkgPortOverlay/sfs-client/portfile.cmake new file mode 100644 index 0000000000..3aee83ec94 --- /dev/null +++ b/src/VcpkgPortOverlay/sfs-client/portfile.cmake @@ -0,0 +1,30 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO microsoft/sfs-client + REF 0e27525d597c730e71646fd0b15bdc8c8503f24d + SHA512 d926d7fdbbd120cbcbd9732a3300cccfeed4a90d6b94456d73a70675df3578a91127f7e9f310fe68d18fa34bb997c29c8455e586d81a2ba404cf19193a80ca6e + HEAD_REF main + PATCHES + remove-unconditional-toolchain-override.patch +) + +if(VCPKG_TARGET_IS_WINDOWS) + vcpkg_check_linkage(ONLY_STATIC_LIBRARY) +endif() + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DSFS_BUILD_TESTS=OFF + -DSFS_BUILD_SAMPLES=OFF +) +vcpkg_cmake_install() +vcpkg_fixup_pkgconfig() + +vcpkg_cmake_config_fixup(PACKAGE_NAME sfsclient CONFIG_PATH lib/cmake/sfsclient) + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") diff --git a/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch b/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch new file mode 100644 index 0000000000..30115c567e --- /dev/null +++ b/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch @@ -0,0 +1,12 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -12,8 +12,6 @@ if(SFS_BUILD_TESTS) + list(APPEND VCPKG_MANIFEST_FEATURES "tests") + endif() + +-set(CMAKE_TOOLCHAIN_FILE "vcpkg/scripts/buildsystems/vcpkg.cmake") +- + # By default using x64 static custom triplet for Windows. Can be overridden by + # setting VCPKG_TARGET_TRIPLET + if(SFS_WINDOWS_STATIC_ONLY diff --git a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/usage b/src/VcpkgPortOverlay/sfs-client/usage similarity index 97% rename from src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/usage rename to src/VcpkgPortOverlay/sfs-client/usage index a6376e32bb..c979a1a512 100644 --- a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/usage +++ b/src/VcpkgPortOverlay/sfs-client/usage @@ -1,4 +1,4 @@ -The package sfs-client provides CMake targets: - - find_package(sfsclient CONFIG REQUIRED) - target_link_libraries(main PRIVATE microsoft::sfsclient) +The package sfs-client provides CMake targets: + + find_package(sfsclient CONFIG REQUIRED) + target_link_libraries(main PRIVATE microsoft::sfsclient) diff --git a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/vcpkg.json b/src/VcpkgPortOverlay/sfs-client/vcpkg.json similarity index 96% rename from src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/vcpkg.json rename to src/VcpkgPortOverlay/sfs-client/vcpkg.json index 1cb93fa8c6..462f18d7e9 100644 --- a/src/SfsClient/sfs-client/sfs-client-vcpkg-port/sfs-client/vcpkg.json +++ b/src/VcpkgPortOverlay/sfs-client/vcpkg.json @@ -1,6 +1,6 @@ -{ +{ "name": "sfs-client", - "version": "", + "version": "1.1.0", "description": "Simple File Solution (SFS) Client", "homepage": "https://github.com/microsoft/sfs-client", "license": "MIT", diff --git a/src/WindowsPackageManager/WindowsPackageManager.vcxproj b/src/WindowsPackageManager/WindowsPackageManager.vcxproj index 071e9e6f10..2d016963f2 100644 --- a/src/WindowsPackageManager/WindowsPackageManager.vcxproj +++ b/src/WindowsPackageManager/WindowsPackageManager.vcxproj @@ -427,9 +427,6 @@ {1cc41a9a-ae66-459d-9210-1e572dd7be69} - - {1b9077b3-8923-4ecd-8fc9-b3190fcbe4d4} - diff --git a/src/vcpkg.json b/src/vcpkg.json index 1e946182e6..8266468b0a 100644 --- a/src/vcpkg.json +++ b/src/vcpkg.json @@ -12,6 +12,7 @@ "dependencies": [ "catch2", "correlation-vector-cpp", + "sfs-client", { "name": "curl", "features": [ From 758e90c4bd303449f17ecec8e311836c0d1d1701 Mon Sep 17 00:00:00 2001 From: Flor Elisa Chacon Ochoa Date: Thu, 21 May 2026 18:29:09 -0700 Subject: [PATCH 2/6] Remove patch description --- .../add-server-certificate-validation.patch | 100 ------------------ .../add-server-certificate-validation.patch | 100 ------------------ 2 files changed, 200 deletions(-) diff --git a/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch b/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch index 84149843d2..c5eb4d08da 100644 --- a/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch +++ b/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch @@ -1,103 +1,3 @@ -From 888b4ed8f4f7d25cb05a47210e083fe29348163b Mon Sep 17 00:00:00 2001 -From: JohnMcPMS -Date: Wed, 27 Jul 2022 18:03:45 -0700 -Subject: [PATCH] Server certificate pinning for Store source (#2347) - -This change adds a generic certificate chain verification infrastructure for pinning certificate chains. It is specifically used to pin the Microsoft Store source by default. More sources may be pinned later, but currently the packaged index is less in need of it because it is already signed. - -The pinning configuration consists of 1 or more chains, only one of which needs to successfully validate the incoming certificate. This allows for rolling to a new certificate when needed. Each chain consists of a fixed set of certificates, which can each be configured to validate any or all of the following properties: - -- Public Key -- Subject -- Issuer - -If the certificate is configured to validate none of the values, it will allow any certificate through. - -An admin setting is added to disable pinning, both as an emergency measure in the event that there is a bug or rolled certificate that was not communicated, but also because there are test scenarios where the user actively wants to disable it (HTTPS redirection via something like Fiddler). - -The configuration can be loaded from JSON for future dynamic configuration, but it is currently only as a test hook to enable configuration via Group Policy. - -In order to better secure the source by default, reconfiguring (remove then add) the Store source manually will convert it back to the built-in values. This includes the pinning configuration. - -It was necessary to modify the cpprestsdk subtree to add a new callback. This enables the request handle to be passed back to our code when the server certificate is first available. We can then check the server certificate against the configured pinning chain, making a decision to terminate the request before it is sent. ---- - .github/actions/spelling/allow.txt | 7 +- - .github/actions/spelling/expect.txt | 10 + - src/AppInstallerCLI.sln | 8 + - src/AppInstallerCLIE2ETests/BaseCommand.cs | 6 +- - src/AppInstallerCLIE2ETests/Constants.cs | 11 + - .../GroupPolicyHelper.cs | 44 ++ - src/AppInstallerCLIE2ETests/SearchCommand.cs | 73 ++- - src/AppInstallerCLIE2ETests/SetUpFixture.cs | 2 +- - src/AppInstallerCLIE2ETests/SourceCommand.cs | 4 +- - src/AppInstallerCLIE2ETests/TestCommon.cs | 56 +- - src/AppInstallerCLIE2ETests/TestIndexSetup.cs | 6 +- - .../AppInstallerCLITests.vcxproj | 2 + - .../AppInstallerCLITests.vcxproj.filters | 3 + - src/AppInstallerCLITests/Certificates.cpp | 185 ++++++ - src/AppInstallerCLITests/Command.cpp | 2 +- - src/AppInstallerCLITests/Completion.cpp | 48 +- - src/AppInstallerCLITests/GroupPolicy.cpp | 60 +- - src/AppInstallerCLITests/HttpClientHelper.cpp | 23 + - src/AppInstallerCLITests/Sources.cpp | 41 ++ - src/AppInstallerCLITests/Strings.cpp | 9 + - src/AppInstallerCommonCore/AdminSettings.cpp | 146 +++-- - .../AppInstallerCommonCore.vcxproj | 4 + - .../AppInstallerCommonCore.vcxproj.filters | 9 + - .../AppInstallerStrings.cpp | 39 ++ - src/AppInstallerCommonCore/Certificates.cpp | 549 ++++++++++++++++++ - src/AppInstallerCommonCore/Errors.cpp | 2 + - src/AppInstallerCommonCore/GroupPolicy.cpp | 13 + - .../JsonSchemaValidation.cpp | 36 +- - .../Manifest/ManifestSchemaValidation.cpp | 3 +- - .../Public/AppInstallerErrors.h | 1 + - .../Public/AppInstallerStrings.h | 6 + - .../Public/winget/AdminSettings.h | 1 + - .../Public/winget/Certificates.h | 153 +++++ - .../Public/winget/GroupPolicy.h | 11 +- - .../Public/winget/JsonSchemaValidation.h | 5 +- - .../Public/winget/Resources.h | 68 ++- - src/AppInstallerCommonCore/Resources.cpp | 58 ++ - src/AppInstallerCommonCore/SHA256.cpp | 33 +- - src/AppInstallerCommonCore/pch.h | 1 + - .../AppInstallerRepositoryCore.vcxproj | 4 +- - .../Public/winget/RepositorySource.h | 8 +- - .../RepositorySource.cpp | 24 +- - .../Rest/RestSourceFactory.cpp | 6 +- - .../Rest/Schema/HttpClientHelper.cpp | 26 +- - .../Rest/Schema/HttpClientHelper.h | 10 +- - src/AppInstallerRepositoryCore/SourceList.cpp | 54 ++ - src/AppInstallerRepositoryCore/SourceList.h | 1 + - src/AppInstallerRepositoryCore/pch.h | 1 + - .../CertificateResources.h | 9 + - .../CertificateResources.rc | 69 +++ - .../CertificateResources.vcxitems | 28 + - .../CertificateResources.vcxitems.filters | 26 + - .../StoreIntermediate1.cer | Bin 0 -> 1527 bytes - src/CertificateResources/StoreLeaf1.cer | Bin 0 -> 2642 bytes - src/CertificateResources/StoreRoot1.cer | Bin 0 -> 914 bytes - src/CertificateResources/resource.h | 14 + - src/LocalhostWebServer/Program.cs | 14 +- - .../Run-LocalhostWebServer.ps1 | 2 +- - src/LocalhostWebServer/Startup.cs | 2 + - .../Properties/Resources.Designer.cs | 2 +- - .../WindowsPackageManager.vcxproj | 1 + - .../Release/include/cpprest/http_client.h | 27 + - .../src/http/client/http_client_winhttp.cpp | 12 + - 63 files changed, 1852 insertions(+), 226 deletions(-) - create mode 100644 src/AppInstallerCLITests/Certificates.cpp - create mode 100644 src/AppInstallerCommonCore/Certificates.cpp - create mode 100644 src/AppInstallerCommonCore/Public/winget/Certificates.h - create mode 100644 src/AppInstallerCommonCore/Resources.cpp - create mode 100644 src/CertificateResources/CertificateResources.h - create mode 100644 src/CertificateResources/CertificateResources.rc - create mode 100644 src/CertificateResources/CertificateResources.vcxitems - create mode 100644 src/CertificateResources/CertificateResources.vcxitems.filters - create mode 100644 src/CertificateResources/StoreIntermediate1.cer - create mode 100644 src/CertificateResources/StoreLeaf1.cer - create mode 100644 src/CertificateResources/StoreRoot1.cer - create mode 100644 src/CertificateResources/resource.h - diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index fb7c6067ab..b862a5778f 100644 --- a/Release/include/cpprest/http_client.h diff --git a/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch b/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch index 84149843d2..c5eb4d08da 100644 --- a/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch +++ b/src/VcpkgPortOverlay/patches/cpprestsdk/add-server-certificate-validation.patch @@ -1,103 +1,3 @@ -From 888b4ed8f4f7d25cb05a47210e083fe29348163b Mon Sep 17 00:00:00 2001 -From: JohnMcPMS -Date: Wed, 27 Jul 2022 18:03:45 -0700 -Subject: [PATCH] Server certificate pinning for Store source (#2347) - -This change adds a generic certificate chain verification infrastructure for pinning certificate chains. It is specifically used to pin the Microsoft Store source by default. More sources may be pinned later, but currently the packaged index is less in need of it because it is already signed. - -The pinning configuration consists of 1 or more chains, only one of which needs to successfully validate the incoming certificate. This allows for rolling to a new certificate when needed. Each chain consists of a fixed set of certificates, which can each be configured to validate any or all of the following properties: - -- Public Key -- Subject -- Issuer - -If the certificate is configured to validate none of the values, it will allow any certificate through. - -An admin setting is added to disable pinning, both as an emergency measure in the event that there is a bug or rolled certificate that was not communicated, but also because there are test scenarios where the user actively wants to disable it (HTTPS redirection via something like Fiddler). - -The configuration can be loaded from JSON for future dynamic configuration, but it is currently only as a test hook to enable configuration via Group Policy. - -In order to better secure the source by default, reconfiguring (remove then add) the Store source manually will convert it back to the built-in values. This includes the pinning configuration. - -It was necessary to modify the cpprestsdk subtree to add a new callback. This enables the request handle to be passed back to our code when the server certificate is first available. We can then check the server certificate against the configured pinning chain, making a decision to terminate the request before it is sent. ---- - .github/actions/spelling/allow.txt | 7 +- - .github/actions/spelling/expect.txt | 10 + - src/AppInstallerCLI.sln | 8 + - src/AppInstallerCLIE2ETests/BaseCommand.cs | 6 +- - src/AppInstallerCLIE2ETests/Constants.cs | 11 + - .../GroupPolicyHelper.cs | 44 ++ - src/AppInstallerCLIE2ETests/SearchCommand.cs | 73 ++- - src/AppInstallerCLIE2ETests/SetUpFixture.cs | 2 +- - src/AppInstallerCLIE2ETests/SourceCommand.cs | 4 +- - src/AppInstallerCLIE2ETests/TestCommon.cs | 56 +- - src/AppInstallerCLIE2ETests/TestIndexSetup.cs | 6 +- - .../AppInstallerCLITests.vcxproj | 2 + - .../AppInstallerCLITests.vcxproj.filters | 3 + - src/AppInstallerCLITests/Certificates.cpp | 185 ++++++ - src/AppInstallerCLITests/Command.cpp | 2 +- - src/AppInstallerCLITests/Completion.cpp | 48 +- - src/AppInstallerCLITests/GroupPolicy.cpp | 60 +- - src/AppInstallerCLITests/HttpClientHelper.cpp | 23 + - src/AppInstallerCLITests/Sources.cpp | 41 ++ - src/AppInstallerCLITests/Strings.cpp | 9 + - src/AppInstallerCommonCore/AdminSettings.cpp | 146 +++-- - .../AppInstallerCommonCore.vcxproj | 4 + - .../AppInstallerCommonCore.vcxproj.filters | 9 + - .../AppInstallerStrings.cpp | 39 ++ - src/AppInstallerCommonCore/Certificates.cpp | 549 ++++++++++++++++++ - src/AppInstallerCommonCore/Errors.cpp | 2 + - src/AppInstallerCommonCore/GroupPolicy.cpp | 13 + - .../JsonSchemaValidation.cpp | 36 +- - .../Manifest/ManifestSchemaValidation.cpp | 3 +- - .../Public/AppInstallerErrors.h | 1 + - .../Public/AppInstallerStrings.h | 6 + - .../Public/winget/AdminSettings.h | 1 + - .../Public/winget/Certificates.h | 153 +++++ - .../Public/winget/GroupPolicy.h | 11 +- - .../Public/winget/JsonSchemaValidation.h | 5 +- - .../Public/winget/Resources.h | 68 ++- - src/AppInstallerCommonCore/Resources.cpp | 58 ++ - src/AppInstallerCommonCore/SHA256.cpp | 33 +- - src/AppInstallerCommonCore/pch.h | 1 + - .../AppInstallerRepositoryCore.vcxproj | 4 +- - .../Public/winget/RepositorySource.h | 8 +- - .../RepositorySource.cpp | 24 +- - .../Rest/RestSourceFactory.cpp | 6 +- - .../Rest/Schema/HttpClientHelper.cpp | 26 +- - .../Rest/Schema/HttpClientHelper.h | 10 +- - src/AppInstallerRepositoryCore/SourceList.cpp | 54 ++ - src/AppInstallerRepositoryCore/SourceList.h | 1 + - src/AppInstallerRepositoryCore/pch.h | 1 + - .../CertificateResources.h | 9 + - .../CertificateResources.rc | 69 +++ - .../CertificateResources.vcxitems | 28 + - .../CertificateResources.vcxitems.filters | 26 + - .../StoreIntermediate1.cer | Bin 0 -> 1527 bytes - src/CertificateResources/StoreLeaf1.cer | Bin 0 -> 2642 bytes - src/CertificateResources/StoreRoot1.cer | Bin 0 -> 914 bytes - src/CertificateResources/resource.h | 14 + - src/LocalhostWebServer/Program.cs | 14 +- - .../Run-LocalhostWebServer.ps1 | 2 +- - src/LocalhostWebServer/Startup.cs | 2 + - .../Properties/Resources.Designer.cs | 2 +- - .../WindowsPackageManager.vcxproj | 1 + - .../Release/include/cpprest/http_client.h | 27 + - .../src/http/client/http_client_winhttp.cpp | 12 + - 63 files changed, 1852 insertions(+), 226 deletions(-) - create mode 100644 src/AppInstallerCLITests/Certificates.cpp - create mode 100644 src/AppInstallerCommonCore/Certificates.cpp - create mode 100644 src/AppInstallerCommonCore/Public/winget/Certificates.h - create mode 100644 src/AppInstallerCommonCore/Resources.cpp - create mode 100644 src/CertificateResources/CertificateResources.h - create mode 100644 src/CertificateResources/CertificateResources.rc - create mode 100644 src/CertificateResources/CertificateResources.vcxitems - create mode 100644 src/CertificateResources/CertificateResources.vcxitems.filters - create mode 100644 src/CertificateResources/StoreIntermediate1.cer - create mode 100644 src/CertificateResources/StoreLeaf1.cer - create mode 100644 src/CertificateResources/StoreRoot1.cer - create mode 100644 src/CertificateResources/resource.h - diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h index fb7c6067ab..b862a5778f 100644 --- a/Release/include/cpprest/http_client.h From 4dd878c313304e876c4d36ed70dc549216add91c Mon Sep 17 00:00:00 2001 From: Flor Elisa Chacon Ochoa Date: Fri, 22 May 2026 13:02:42 -0700 Subject: [PATCH 3/6] Remove generated port files --- src/VcpkgPortOverlay/.gitignore | 6 + .../add-server-certificate-validation.patch | 69 ----------- .../cpprestsdk/fix-find-openssl.patch | 18 --- src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch | 28 ----- .../cpprestsdk/fix_narrowing.patch | 50 -------- .../cpprestsdk/portfile.cmake | 61 --------- src/VcpkgPortOverlay/cpprestsdk/test.patch | 23 ---- src/VcpkgPortOverlay/cpprestsdk/vcpkg.json | 117 ------------------ .../detours/find-jmp-bounds-arm64.patch | 24 ---- src/VcpkgPortOverlay/detours/portfile.cmake | 34 ----- src/VcpkgPortOverlay/detours/usage | 7 -- src/VcpkgPortOverlay/detours/vcpkg.json | 9 -- .../libyaml/export-pkgconfig.patch | 16 --- .../libyaml/fix-POSIX_name.patch | 13 -- src/VcpkgPortOverlay/libyaml/portfile.cmake | 33 ----- src/VcpkgPortOverlay/libyaml/vcpkg.json | 17 --- .../sfs-client/portfile.cmake | 30 ----- ...ove-unconditional-toolchain-override.patch | 12 -- src/VcpkgPortOverlay/sfs-client/usage | 4 - src/VcpkgPortOverlay/sfs-client/vcpkg.json | 30 ----- src/vcpkg.props | 16 +++ 21 files changed, 22 insertions(+), 595 deletions(-) create mode 100644 src/VcpkgPortOverlay/.gitignore delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/fix-find-openssl.patch delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/fix_narrowing.patch delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/portfile.cmake delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/test.patch delete mode 100644 src/VcpkgPortOverlay/cpprestsdk/vcpkg.json delete mode 100644 src/VcpkgPortOverlay/detours/find-jmp-bounds-arm64.patch delete mode 100644 src/VcpkgPortOverlay/detours/portfile.cmake delete mode 100644 src/VcpkgPortOverlay/detours/usage delete mode 100644 src/VcpkgPortOverlay/detours/vcpkg.json delete mode 100644 src/VcpkgPortOverlay/libyaml/export-pkgconfig.patch delete mode 100644 src/VcpkgPortOverlay/libyaml/fix-POSIX_name.patch delete mode 100644 src/VcpkgPortOverlay/libyaml/portfile.cmake delete mode 100644 src/VcpkgPortOverlay/libyaml/vcpkg.json delete mode 100644 src/VcpkgPortOverlay/sfs-client/portfile.cmake delete mode 100644 src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch delete mode 100644 src/VcpkgPortOverlay/sfs-client/usage delete mode 100644 src/VcpkgPortOverlay/sfs-client/vcpkg.json diff --git a/src/VcpkgPortOverlay/.gitignore b/src/VcpkgPortOverlay/.gitignore new file mode 100644 index 0000000000..43a24cfd8d --- /dev/null +++ b/src/VcpkgPortOverlay/.gitignore @@ -0,0 +1,6 @@ +# Generated port directories are ignored; recreate with CreatePortOverlay.ps1 +/* +!/.gitignore +!/CreatePortOverlay.ps1 +!/README.md +!/patches diff --git a/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch b/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch deleted file mode 100644 index c5eb4d08da..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/add-server-certificate-validation.patch +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/Release/include/cpprest/http_client.h b/Release/include/cpprest/http_client.h -index fb7c6067ab..b862a5778f 100644 ---- a/Release/include/cpprest/http_client.h -+++ b/Release/include/cpprest/http_client.h -@@ -362,6 +362,32 @@ class http_client_config - if (m_set_user_nativehandle_options) m_set_user_nativehandle_options(handle); - } - -+ /// -+ /// Sets a callback to enable custom handling when the server certificate is available. -+ /// -+ /// -+ /// The native_handle is the following type depending on the underlying platform: -+ /// Windows Desktop, WinHTTP - HINTERNET !!! Is only implemented to call in here !!! -+ /// Windows Runtime, WinRT - IXMLHTTPRequest2 * -+ /// All other platforms, Boost.Asio: -+ /// https - boost::asio::ssl::stream * -+ /// http - boost::asio::ip::tcp::socket * -+ /// -+ /// A user callback allowing for validation of the server certificate. -+ void set_nativehandle_servercertificate_validation(const std::function& callback) -+ { -+ m_nativehandle_servercertificate_validation = callback; -+ } -+ -+ /// -+ /// Invokes a user's callback to validate the server certificate. -+ /// -+ /// A internal implementation handle. -+ void invoke_nativehandle_servercertificate_validation(native_handle handle) const -+ { -+ if (m_nativehandle_servercertificate_validation) m_nativehandle_servercertificate_validation(handle); -+ } -+ - #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) - /// - /// Sets a callback to enable custom setting of the ssl context, at construction time. -@@ -418,6 +444,7 @@ class http_client_config - - std::function m_set_user_nativehandle_options; - std::function m_set_user_nativesessionhandle_options; -+ std::function m_nativehandle_servercertificate_validation; - - #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) - std::function m_ssl_context_callback; -diff --git a/Release/src/http/client/http_client_winhttp.cpp b/Release/src/http/client/http_client_winhttp.cpp -index d6cdb5384a..5a517ec334 100644 ---- a/Release/src/http/client/http_client_winhttp.cpp -+++ b/Release/src/http/client/http_client_winhttp.cpp -@@ -2039,6 +2039,18 @@ class winhttp_client final : public _http_client_communicator - case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: - { - p_request_context->on_send_request_validate_cn(); -+ -+ try -+ { -+ p_request_context->m_http_client->client_config().invoke_nativehandle_servercertificate_validation(hRequestHandle); -+ } -+ catch (...) -+ { -+ p_request_context->report_exception(std::current_exception()); -+ p_request_context->cleanup(); -+ return; -+ } -+ - return; - } - case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: - diff --git a/src/VcpkgPortOverlay/cpprestsdk/fix-find-openssl.patch b/src/VcpkgPortOverlay/cpprestsdk/fix-find-openssl.patch deleted file mode 100644 index ec420e6771..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/fix-find-openssl.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/Release/cmake/cpprest_find_openssl.cmake b/Release/cmake/cpprest_find_openssl.cmake -index 9333663..c1df089 100644 ---- a/Release/cmake/cpprest_find_openssl.cmake -+++ b/Release/cmake/cpprest_find_openssl.cmake -@@ -36,8 +36,11 @@ function(cpprest_find_openssl) - # Prefer a homebrew version of OpenSSL over the one in /usr/lib - file(GLOB OPENSSL_ROOT_DIR /usr/local/Cellar/openssl*/*) - # Prefer the latest (make the latest one first) -- list(REVERSE OPENSSL_ROOT_DIR) -- list(GET OPENSSL_ROOT_DIR 0 OPENSSL_ROOT_DIR) -+ if(OPENSSL_ROOT_DIR) -+ # Prefer the latest (make the latest one first) -+ list(REVERSE OPENSSL_ROOT_DIR) -+ list(GET OPENSSL_ROOT_DIR 0 OPENSSL_ROOT_DIR) -+ endif() - endif() - # This should prevent linking against the system provided 0.9.8y - message(STATUS "OPENSSL_ROOT_DIR = ${OPENSSL_ROOT_DIR}") diff --git a/src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch b/src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch deleted file mode 100644 index c7e77ebf6c..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/fix-uwp.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/Release/CMakeLists.txt b/Release/CMakeLists.txt -index b8f3809..3857cfc 100644 ---- a/Release/CMakeLists.txt -+++ b/Release/CMakeLists.txt -@@ -187,7 +187,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /profile /OPT:REF /OPT:ICF") - - if (WINDOWS_STORE OR WINDOWS_PHONE) -- add_compile_options(/ZW) -+ # add_compile_options(/ZW) - else() - if (NOT (MSVC_VERSION LESS 1920)) - add_compile_options(/permissive-) -diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt -index 128f6d6..098d33f 100644 ---- a/Release/src/CMakeLists.txt -+++ b/Release/src/CMakeLists.txt -@@ -47,6 +47,10 @@ target_include_directories(cpprest - pch - ) - -+if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND (WINDOWS_STORE OR WINDOWS_PHONE)) -+ target_compile_options(cpprest PUBLIC /ZW) -+endif() -+ - ## Sub-components - # Websockets component - if(CPPREST_WEBSOCKETS_IMPL STREQUAL "none") diff --git a/src/VcpkgPortOverlay/cpprestsdk/fix_narrowing.patch b/src/VcpkgPortOverlay/cpprestsdk/fix_narrowing.patch deleted file mode 100644 index 975a759ca9..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/fix_narrowing.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt -index e15aeb7fc..128f6d6af 100644 ---- a/Release/src/CMakeLists.txt -+++ b/Release/src/CMakeLists.txt -@@ -185,12 +185,12 @@ endif() - - configure_pch(cpprest stdafx.h pch/stdafx.cpp /Zm120) - --if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") -+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT MSVC) - if(WERROR) - target_compile_options(cpprest PRIVATE -Werror) - endif() - target_compile_options(cpprest PRIVATE -pedantic ${WARNINGS}) --elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") -+elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC") - if(WERROR) - target_compile_options(cpprest PRIVATE /WX ${WARNINGS}) - endif() -diff --git a/Release/src/streams/fileio_win32.cpp b/Release/src/streams/fileio_win32.cpp -index 057dd9b67..a65439cb7 100644 ---- a/Release/src/streams/fileio_win32.cpp -+++ b/Release/src/streams/fileio_win32.cpp -@@ -616,7 +616,7 @@ size_t _fill_buffer_fsb(_In_ _file_info_impl* fInfo, - // pending - return read; - -- case (-1): -+ case ((size_t)(-1)): - // error - delete cb; - return read; -@@ -668,7 +668,7 @@ size_t _fill_buffer_fsb(_In_ _file_info_impl* fInfo, - // pending - return read; - -- case (-1): -+ case ((size_t)(-1)): - // error - delete cb; - return read; -@@ -719,7 +719,7 @@ size_t _fill_buffer_fsb(_In_ _file_info_impl* fInfo, - // pending - return read; - -- case (-1): -+ case ((size_t)(-1)): - // error - delete cb; - return read; diff --git a/src/VcpkgPortOverlay/cpprestsdk/portfile.cmake b/src/VcpkgPortOverlay/cpprestsdk/portfile.cmake deleted file mode 100644 index 0568ae7e8f..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/portfile.cmake +++ /dev/null @@ -1,61 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO Microsoft/cpprestsdk - REF 122d09549201da5383321d870bed45ecb9e168c5 - SHA512 c9ded33d3c67880e2471e479a38b40a14a9ff45d241e928b6339eca697b06ad621846260eca47b6b1b8a2bc9ab7bf4fea8d3e8e795cd430d8839beb530e16dd7 - HEAD_REF master - PATCHES - fix-find-openssl.patch - fix_narrowing.patch - fix-uwp.patch - add-server-certificate-validation.patch -) - -set(OPTIONS) -if(NOT VCPKG_TARGET_IS_UWP) - SET(WEBSOCKETPP_PATH "${CURRENT_INSTALLED_DIR}/share/websocketpp") - list(APPEND OPTIONS - -DWEBSOCKETPP_CONFIG=${WEBSOCKETPP_PATH} - -DWEBSOCKETPP_CONFIG_VERSION=${WEBSOCKETPP_PATH}) -endif() - -vcpkg_check_features( - OUT_FEATURE_OPTIONS FEATURE_OPTIONS - INVERTED_FEATURES - brotli CPPREST_EXCLUDE_BROTLI - compression CPPREST_EXCLUDE_COMPRESSION - websockets CPPREST_EXCLUDE_WEBSOCKETS -) - -if(VCPKG_TARGET_IS_UWP) - set(configure_opts WINDOWS_USE_MSBUILD) -endif() - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}/Release" - ${configure_opts} - OPTIONS - ${OPTIONS} - ${FEATURE_OPTIONS} - -DBUILD_TESTS=OFF - -DBUILD_SAMPLES=OFF - -DCPPREST_EXPORT_DIR=share/cpprestsdk - -DWERROR=OFF - -DPKG_CONFIG_EXECUTABLE=FALSE - OPTIONS_DEBUG - -DCPPREST_INSTALL_HEADERS=OFF -) - -vcpkg_cmake_install() - -vcpkg_copy_pdbs() - -vcpkg_cmake_config_fixup(CONFIG_PATH "lib/share/${PORT}") -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/share" "${CURRENT_PACKAGES_DIR}/lib/share") - -if (VCPKG_LIBRARY_LINKAGE STREQUAL static) - vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/cpprest/details/cpprest_compat.h" - "#ifdef _NO_ASYNCRTIMP" "#if 1") -endif() - -file(INSTALL "${SOURCE_PATH}/license.txt" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) diff --git a/src/VcpkgPortOverlay/cpprestsdk/test.patch b/src/VcpkgPortOverlay/cpprestsdk/test.patch deleted file mode 100644 index c5507e88ad..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/test.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/Release/src/CMakeLists.txt b/Release/src/CMakeLists.txt -index 098d33f..7f1f650 100644 ---- a/Release/src/CMakeLists.txt -+++ b/Release/src/CMakeLists.txt -@@ -218,15 +218,15 @@ if (WIN32 AND NOT WINDOWS_STORE AND NOT WINDOWS_PHONE) - crypt32.lib - ) - elseif(WINDOWS_STORE) -- if(NOT CMAKE_GENERATOR MATCHES "Visual Studio .*") -- target_compile_definitions(cpprest PRIVATE -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP) -+ -+ target_compile_definitions(cpprest PUBLIC -DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP) - get_target_property(LINK_FLAGS cpprest LINK_FLAGS) - if(NOT LINK_FLAGS) - set(LINK_FLAGS "") - endif() - set(LINK_FLAGS "${LINK_FLAGS} /APPCONTAINER") - set_target_properties(cpprest PROPERTIES LINK_FLAGS "${LINK_FLAGS}") -- endif() -+ - endif() - - set_target_properties(cpprest PROPERTIES OUTPUT_NAME "cpprest${CPPREST_ABI_TAG}") diff --git a/src/VcpkgPortOverlay/cpprestsdk/vcpkg.json b/src/VcpkgPortOverlay/cpprestsdk/vcpkg.json deleted file mode 100644 index a270085312..0000000000 --- a/src/VcpkgPortOverlay/cpprestsdk/vcpkg.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "name": "cpprestsdk", - "version": "2.10.18", - "port-version": 4, - "description": [ - "C++11 JSON, REST, and OAuth library", - "The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services." - ], - "homepage": "https://github.com/Microsoft/cpprestsdk", - "license": "MIT", - "dependencies": [ - { - "name": "boost-asio", - "platform": "!uwp & !windows" - }, - { - "name": "boost-chrono", - "platform": "!uwp & !windows" - }, - { - "name": "boost-date-time", - "platform": "!uwp & !windows" - }, - { - "name": "boost-filesystem", - "platform": "!uwp & !windows" - }, - { - "name": "boost-random", - "platform": "!uwp & !windows" - }, - { - "name": "boost-regex", - "platform": "!uwp & !windows" - }, - { - "name": "boost-system", - "platform": "!uwp & !windows" - }, - { - "name": "boost-thread", - "platform": "!uwp & !windows" - }, - { - "name": "openssl", - "platform": "!uwp & !windows" - }, - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ], - "default-features": [ - { - "name": "brotli", - "platform": "windows" - }, - "compression" - ], - "features": { - "brotli": { - "description": "Brotli compression support", - "dependencies": [ - "brotli", - { - "name": "cpprestsdk", - "default-features": false, - "features": [ - "compression" - ] - } - ] - }, - "compression": { - "description": "HTTP Compression support", - "dependencies": [ - "zlib" - ] - }, - "websockets": { - "description": "Websockets support", - "dependencies": [ - { - "name": "boost-date-time", - "platform": "!uwp" - }, - { - "name": "boost-regex", - "platform": "!uwp" - }, - { - "name": "boost-system", - "platform": "!uwp" - }, - { - "name": "cpprestsdk", - "default-features": false, - "features": [ - "compression" - ] - }, - { - "name": "openssl", - "platform": "!uwp" - }, - { - "name": "websocketpp", - "platform": "!uwp" - } - ] - } - } -} diff --git a/src/VcpkgPortOverlay/detours/find-jmp-bounds-arm64.patch b/src/VcpkgPortOverlay/detours/find-jmp-bounds-arm64.patch deleted file mode 100644 index 21390cec05..0000000000 --- a/src/VcpkgPortOverlay/detours/find-jmp-bounds-arm64.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/src/detours.cpp b/src/detours.cpp -index 8345c4d..3cd0e9d 100644 ---- a/src/detours.cpp -+++ b/src/detours.cpp -@@ -974,6 +974,19 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) - return pbCode; - } - -+inline void detour_find_jmp_bounds(PBYTE pbCode, -+ PDETOUR_TRAMPOLINE *ppLower, -+ PDETOUR_TRAMPOLINE *ppUpper) -+{ -+ // We have to place trampolines within +/- 2GB of code. -+ ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); -+ ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); -+ DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); -+ -+ *ppLower = (PDETOUR_TRAMPOLINE)lo; -+ *ppUpper = (PDETOUR_TRAMPOLINE)hi; -+} -+ - inline BOOL detour_does_code_end_function(PBYTE pbCode) - { - ULONG Opcode = fetch_opcode(pbCode); diff --git a/src/VcpkgPortOverlay/detours/portfile.cmake b/src/VcpkgPortOverlay/detours/portfile.cmake deleted file mode 100644 index 1a025f9a04..0000000000 --- a/src/VcpkgPortOverlay/detours/portfile.cmake +++ /dev/null @@ -1,34 +0,0 @@ -vcpkg_check_linkage(ONLY_STATIC_LIBRARY) - -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO microsoft/Detours - REF 404c153ff390cb14f1787c7feeb4908c6d79b0ab # Unreleased - SHA512 1f3f26657927fa153116dce13dbfa3319ea368e6c9017f4999b6ec24d6356c335b3d5326718d3ec707b92832763ffea092088df52596f016d7ca9b8127f7033d - HEAD_REF master -) - -vcpkg_build_nmake( - SOURCE_PATH "${SOURCE_PATH}" - PROJECT_SUBPATH "src" - PROJECT_NAME "Makefile" - OPTIONS "PROCESSOR_ARCHITECTURE=${VCPKG_TARGET_ARCHITECTURE}" - OPTIONS_RELEASE "DETOURS_CONFIG=Release" - OPTIONS_DEBUG "DETOURS_CONFIG=Debug" -) - -if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/lib.${VCPKG_TARGET_ARCHITECTURE}Release/" DESTINATION "${CURRENT_PACKAGES_DIR}/lib") -endif() -if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/lib.${VCPKG_TARGET_ARCHITECTURE}Debug/" DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") -endif() - -if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/include/" DESTINATION "${CURRENT_PACKAGES_DIR}/include" RENAME detours) -else() - file(INSTALL "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/include/" DESTINATION "${CURRENT_PACKAGES_DIR}/include" RENAME detours) -endif() - -file(INSTALL "${SOURCE_PATH}/LICENSE.md" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) -file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") diff --git a/src/VcpkgPortOverlay/detours/usage b/src/VcpkgPortOverlay/detours/usage deleted file mode 100644 index 00f978fb68..0000000000 --- a/src/VcpkgPortOverlay/detours/usage +++ /dev/null @@ -1,7 +0,0 @@ -detours can be used from CMake via: - - find_path(DETOURS_INCLUDE_DIRS "detours/detours.h") - find_library(DETOURS_LIBRARY detours REQUIRED) - - target_include_directories(main PRIVATE ${DETOURS_INCLUDE_DIRS}) - target_link_libraries(main PRIVATE ${DETOURS_LIBRARY}) diff --git a/src/VcpkgPortOverlay/detours/vcpkg.json b/src/VcpkgPortOverlay/detours/vcpkg.json deleted file mode 100644 index b0f745daf6..0000000000 --- a/src/VcpkgPortOverlay/detours/vcpkg.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "detours", - "version": "4.0.1", - "port-version": 8, - "description": "Detours is a software package for monitoring and instrumenting API calls on Windows.", - "homepage": "https://github.com/microsoft/Detours", - "license": "MIT", - "supports": "windows & !uwp" -} diff --git a/src/VcpkgPortOverlay/libyaml/export-pkgconfig.patch b/src/VcpkgPortOverlay/libyaml/export-pkgconfig.patch deleted file mode 100644 index 5e0d5a3b74..0000000000 --- a/src/VcpkgPortOverlay/libyaml/export-pkgconfig.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 4f81148..8006536 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -158,3 +158,11 @@ install( - FILES ${config_version_file} - DESTINATION ${INSTALL_CMAKE_DIR} COMPONENT Development - ) -+ -+set(prefix ${CMAKE_INSTALL_PREFIX}) -+set(exec_prefix ${CMAKE_INSTALL_PREFIX}) -+set(includedir ${CMAKE_INSTALL_PREFIX}/include) -+set(libdir ${CMAKE_INSTALL_PREFIX}/lib) -+set(PACKAGE_VERSION 0.1) -+configure_file(yaml-0.1.pc.in ${PROJECT_BINARY_DIR}/yaml-0.1.pc @ONLY) -+install(FILES ${PROJECT_BINARY_DIR}/yaml-0.1.pc DESTINATION lib/pkgconfig) diff --git a/src/VcpkgPortOverlay/libyaml/fix-POSIX_name.patch b/src/VcpkgPortOverlay/libyaml/fix-POSIX_name.patch deleted file mode 100644 index 0aa6d211c2..0000000000 --- a/src/VcpkgPortOverlay/libyaml/fix-POSIX_name.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/api.c b/src/api.c -index e793b08..6f16fc5 100644 ---- a/src/api.c -+++ b/src/api.c -@@ -63,7 +63,7 @@ yaml_strdup(const yaml_char_t *str) - if (!str) - return NULL; - -- return (yaml_char_t *)strdup((char *)str); -+ return (yaml_char_t *)_strdup((char *)str); - } - - /* diff --git a/src/VcpkgPortOverlay/libyaml/portfile.cmake b/src/VcpkgPortOverlay/libyaml/portfile.cmake deleted file mode 100644 index 7835356ac9..0000000000 --- a/src/VcpkgPortOverlay/libyaml/portfile.cmake +++ /dev/null @@ -1,33 +0,0 @@ -if(VCPKG_TARGET_IS_WINDOWS) - set(PATCHES fix-POSIX_name.patch) -endif() - -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO yaml/libyaml - REF 840b65c40675e2d06bf40405ad3f12dec7f35923 # Unreleased - SHA512 de85560312d53a007a2ddf1fe403676bbd34620480b1ba446b8c16bb366524ba7a6ed08f6316dd783bf980d9e26603a9efc82f134eb0235917b3be1d3eb4b302 - HEAD_REF master - PATCHES - ${PATCHES} - export-pkgconfig.patch -) - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS - -DBUILD_TESTING=OFF - -DINSTALL_CMAKE_DIR=share/yaml -) - -vcpkg_cmake_install() - -vcpkg_copy_pdbs() - -vcpkg_cmake_config_fixup(PACKAGE_NAME yaml CONFIG_PATH share/yaml) - -vcpkg_fixup_pkgconfig() - -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include" "${CURRENT_PACKAGES_DIR}/include/config.h" "${CURRENT_PACKAGES_DIR}/debug/share") - -configure_file("${SOURCE_PATH}/License" "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" COPYONLY) diff --git a/src/VcpkgPortOverlay/libyaml/vcpkg.json b/src/VcpkgPortOverlay/libyaml/vcpkg.json deleted file mode 100644 index 463182d0bc..0000000000 --- a/src/VcpkgPortOverlay/libyaml/vcpkg.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "libyaml", - "version": "0.2.5", - "port-version": 6, - "description": "A C library for parsing and emitting YAML.", - "homepage": "https://github.com/yaml/libyaml", - "dependencies": [ - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ] -} diff --git a/src/VcpkgPortOverlay/sfs-client/portfile.cmake b/src/VcpkgPortOverlay/sfs-client/portfile.cmake deleted file mode 100644 index 3aee83ec94..0000000000 --- a/src/VcpkgPortOverlay/sfs-client/portfile.cmake +++ /dev/null @@ -1,30 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO microsoft/sfs-client - REF 0e27525d597c730e71646fd0b15bdc8c8503f24d - SHA512 d926d7fdbbd120cbcbd9732a3300cccfeed4a90d6b94456d73a70675df3578a91127f7e9f310fe68d18fa34bb997c29c8455e586d81a2ba404cf19193a80ca6e - HEAD_REF main - PATCHES - remove-unconditional-toolchain-override.patch -) - -if(VCPKG_TARGET_IS_WINDOWS) - vcpkg_check_linkage(ONLY_STATIC_LIBRARY) -endif() - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS - -DSFS_BUILD_TESTS=OFF - -DSFS_BUILD_SAMPLES=OFF -) -vcpkg_cmake_install() -vcpkg_fixup_pkgconfig() - -vcpkg_cmake_config_fixup(PACKAGE_NAME sfsclient CONFIG_PATH lib/cmake/sfsclient) - -vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") - -file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") - -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") diff --git a/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch b/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch deleted file mode 100644 index 30115c567e..0000000000 --- a/src/VcpkgPortOverlay/sfs-client/remove-unconditional-toolchain-override.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -12,8 +12,6 @@ if(SFS_BUILD_TESTS) - list(APPEND VCPKG_MANIFEST_FEATURES "tests") - endif() - --set(CMAKE_TOOLCHAIN_FILE "vcpkg/scripts/buildsystems/vcpkg.cmake") -- - # By default using x64 static custom triplet for Windows. Can be overridden by - # setting VCPKG_TARGET_TRIPLET - if(SFS_WINDOWS_STATIC_ONLY diff --git a/src/VcpkgPortOverlay/sfs-client/usage b/src/VcpkgPortOverlay/sfs-client/usage deleted file mode 100644 index c979a1a512..0000000000 --- a/src/VcpkgPortOverlay/sfs-client/usage +++ /dev/null @@ -1,4 +0,0 @@ -The package sfs-client provides CMake targets: - - find_package(sfsclient CONFIG REQUIRED) - target_link_libraries(main PRIVATE microsoft::sfsclient) diff --git a/src/VcpkgPortOverlay/sfs-client/vcpkg.json b/src/VcpkgPortOverlay/sfs-client/vcpkg.json deleted file mode 100644 index 462f18d7e9..0000000000 --- a/src/VcpkgPortOverlay/sfs-client/vcpkg.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "sfs-client", - "version": "1.1.0", - "description": "Simple File Solution (SFS) Client", - "homepage": "https://github.com/microsoft/sfs-client", - "license": "MIT", - "dependencies": [ - { - "name": "curl", - "features": [ - "c-ares", - { - "name": "openssl", - "platform": "!windows", - "$comment": "TODO #42: OpenSSL is not needed on Windows yet while we don't have cert pinning. Default is schannel, which uses Windows store" - } - ] - }, - "nlohmann-json", - "correlation-vector-cpp", - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ] -} \ No newline at end of file diff --git a/src/vcpkg.props b/src/vcpkg.props index a3b2750546..f1d48c8945 100644 --- a/src/vcpkg.props +++ b/src/vcpkg.props @@ -52,6 +52,22 @@ + + + <_VcpkgPortOverlayInput Include="$(MSBuildThisFileDirectory)VcpkgPortOverlay\CreatePortOverlay.ps1" /> + <_VcpkgPortOverlayInput Include="$(MSBuildThisFileDirectory)VcpkgPortOverlay\patches\**\*" /> + + + + + + + From b6e1da0e2775aae584b94e2a8c6833b5089e8fa2 Mon Sep 17 00:00:00 2001 From: Flor Elisa Chacon Ochoa Date: Fri, 22 May 2026 18:19:43 -0700 Subject: [PATCH 4/6] Removed checked-in ports in favor of script at build time --- azure-pipelines.yml | 25 ------- src/VcpkgPortOverlay/CreatePortOverlay.ps1 | 79 ++++++++++++++++------ src/vcpkg.props | 17 ++--- 3 files changed, 62 insertions(+), 59 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 44cd9f2149..f343fb4156 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -720,28 +720,3 @@ jobs: env: onefuzzDropDirectory: '$(buildOutDir)\WinGetYamlFuzzing' SYSTEM_ACCESSTOKEN: $(System.AccessToken) - -- job: 'VerifyPortOverlay' - displayName: 'Verify Port Overlay' - variables: - runCodesignValidationInjection: ${{ false }} - skipComponentGovernanceDetection: ${{ true }} - - steps: - - task: PowerShell@2 - displayName: Regenerate Port Overlay - inputs: - filePath: 'src\VcpkgPortOverlay\CreatePortOverlay.ps1' - workingDirectory: 'src\VcpkgPortOverlay' - - - task: PowerShell@2 - displayName: Verify No Changes - inputs: - targetType: inline - script: | - $diff = git diff --name-only src/VcpkgPortOverlay - if ($diff) { - Write-Host "##[error]Port overlay is out of sync. Run CreatePortOverlay.ps1 and commit the result." - Write-Host $diff - exit 1 - } diff --git a/src/VcpkgPortOverlay/CreatePortOverlay.ps1 b/src/VcpkgPortOverlay/CreatePortOverlay.ps1 index 13003328a5..4b0685c050 100644 --- a/src/VcpkgPortOverlay/CreatePortOverlay.ps1 +++ b/src/VcpkgPortOverlay/CreatePortOverlay.ps1 @@ -1,5 +1,12 @@ # Helper functions for dealing with the port overlay +[CmdletBinding()] +param( + # When provided, serializes concurrent invocations via a named mutex and skips work + # if the stamp file is already up-to-date. Used by VcpkgPortOverlay.proj. + [string]$StampFile +) + $OverlayRoot = $PSScriptRoot $ErrorActionPreference = "Stop" @@ -530,29 +537,59 @@ function Update-PortVersion $portDefinition | ConvertTo-Json -Depth 5 | Out-File $portJsonPath } -New-PortOverlay cpprestsdk -Version 2.10.18 -PortVersion 4 -Add-LocalPatch cpprestsdk 'add-server-certificate-validation.patch' +# Acquire mutex if running from MSBuild (StampFile provided) to serialize parallel project builds +$_mutex = $null +if ($StampFile) { + $_mutex = [System.Threading.Mutex]::new($false, 'Local\WingetVcpkgPortOverlay') + try { $_mutex.WaitOne() | Out-Null } catch [System.Threading.AbandonedMutexException] {} + + # Another process may have already rebuilt the overlay while we waited; skip if so. + if (Test-Path $StampFile) { + $stampTime = (Get-Item $StampFile).LastWriteTime + + $overlayInputs = @($PSCommandPath) + if (Test-Path $OverlayRoot\patches) { + $overlayInputs += Get-ChildItem -Path $OverlayRoot\patches -Recurse -File + } + + if (-not ($overlayInputs | Where-Object { $_.LastWriteTime -gt $stampTime } | Select-Object -First 1)) { + $_mutex.ReleaseMutex() + return + } + } +} + +try { + New-PortOverlay cpprestsdk -Version 2.10.18 -PortVersion 4 + Add-LocalPatch cpprestsdk 'add-server-certificate-validation.patch' -New-PortOverlay detours -Version 4.0.1 -PortVersion 8 -Update-PortSource detours -RefPattern 'v4.0.1' -Commit '404c153ff390cb14f1787c7feeb4908c6d79b0ab' -SourceHash '1f3f26657927fa153116dce13dbfa3319ea368e6c9017f4999b6ec24d6356c335b3d5326718d3ec707b92832763ffea092088df52596f016d7ca9b8127f7033d' -Remove-PortPatches detours + New-PortOverlay detours -Version 4.0.1 -PortVersion 8 + Update-PortSource detours -RefPattern 'v4.0.1' -Commit '404c153ff390cb14f1787c7feeb4908c6d79b0ab' -SourceHash '1f3f26657927fa153116dce13dbfa3319ea368e6c9017f4999b6ec24d6356c335b3d5326718d3ec707b92832763ffea092088df52596f016d7ca9b8127f7033d' + Remove-PortPatches detours -New-PortOverlay libyaml -Version 0.2.5 -PortVersion 5 -Update-PortSource libyaml -Commit '840b65c40675e2d06bf40405ad3f12dec7f35923' -SourceHash 'de85560312d53a007a2ddf1fe403676bbd34620480b1ba446b8c16bb366524ba7a6ed08f6316dd783bf980d9e26603a9efc82f134eb0235917b3be1d3eb4b302' -Update-PortVersion libyaml + New-PortOverlay libyaml -Version 0.2.5 -PortVersion 5 + Update-PortSource libyaml -Commit '840b65c40675e2d06bf40405ad3f12dec7f35923' -SourceHash 'de85560312d53a007a2ddf1fe403676bbd34620480b1ba446b8c16bb366524ba7a6ed08f6316dd783bf980d9e26603a9efc82f134eb0235917b3be1d3eb4b302' + Update-PortVersion libyaml -# sfs-client is not in the official vcpkg registry. -# The port is based on the template from the sfs-client repository. -# See: https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client -$SfsClientCommit = '0e27525d597c730e71646fd0b15bdc8c8503f24d' -$SfsClientSha512 = 'd926d7fdbbd120cbcbd9732a3300cccfeed4a90d6b94456d73a70675df3578a91127f7e9f310fe68d18fa34bb997c29c8455e586d81a2ba404cf19193a80ca6e' -$SfsClientVersion = '1.1.0' + # sfs-client is not in the official vcpkg registry. + # The port is based on the template from the sfs-client repository. + # See: https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client + $SfsClientCommit = '0e27525d597c730e71646fd0b15bdc8c8503f24d' + $SfsClientSha512 = 'd926d7fdbbd120cbcbd9732a3300cccfeed4a90d6b94456d73a70675df3578a91127f7e9f310fe68d18fa34bb997c29c8455e586d81a2ba404cf19193a80ca6e' + $SfsClientVersion = '1.1.0' -New-PortOverlayFromGitHub 'sfs-client' -Repo 'microsoft/sfs-client' -Commit $SfsClientCommit -SubPath 'sfs-client-vcpkg-port/sfs-client' -Expand-PortfileTemplate 'sfs-client' -Set-PortFilePlaceholder 'sfs-client' 'portfile.cmake' -Placeholder 'commit-id' -Value $SfsClientCommit -Set-ParameterInPortFile 'sfs-client' -ParameterName 'SHA512' -CurrentValuePattern '0' -NewValue $SfsClientSha512 -Set-CmakeConfigureOptions 'sfs-client' -Options @('-DSFS_BUILD_TESTS=OFF', '-DSFS_BUILD_SAMPLES=OFF') -Set-PortFilePlaceholder 'sfs-client' 'vcpkg.json' -Placeholder 'VERSION' -Value $SfsClientVersion + New-PortOverlayFromGitHub 'sfs-client' -Repo 'microsoft/sfs-client' -Commit $SfsClientCommit -SubPath 'sfs-client-vcpkg-port/sfs-client' + Expand-PortfileTemplate 'sfs-client' + Set-PortFilePlaceholder 'sfs-client' 'portfile.cmake' -Placeholder 'commit-id' -Value $SfsClientCommit + Set-ParameterInPortFile 'sfs-client' -ParameterName 'SHA512' -CurrentValuePattern '0' -NewValue $SfsClientSha512 + Set-CmakeConfigureOptions 'sfs-client' -Options @('-DSFS_BUILD_TESTS=OFF', '-DSFS_BUILD_SAMPLES=OFF') + Set-PortFilePlaceholder 'sfs-client' 'vcpkg.json' -Placeholder 'VERSION' -Value $SfsClientVersion -Add-LocalPatch 'sfs-client' 'remove-unconditional-toolchain-override.patch' + Add-LocalPatch 'sfs-client' 'remove-unconditional-toolchain-override.patch' + + if ($StampFile) { + $null = New-Item -ItemType File -Path $StampFile -Force + } +} finally { + if ($_mutex) { $_mutex.ReleaseMutex() } +} diff --git a/src/vcpkg.props b/src/vcpkg.props index f1d48c8945..61470146a8 100644 --- a/src/vcpkg.props +++ b/src/vcpkg.props @@ -52,20 +52,11 @@ - - - <_VcpkgPortOverlayInput Include="$(MSBuildThisFileDirectory)VcpkgPortOverlay\CreatePortOverlay.ps1" /> - <_VcpkgPortOverlayInput Include="$(MSBuildThisFileDirectory)VcpkgPortOverlay\patches\**\*" /> - - + - - + BeforeTargets="VcpkgInstallManifestDependencies"> + Date: Fri, 22 May 2026 18:33:30 -0700 Subject: [PATCH 5/6] .sln indent --- src/AppInstallerCLI.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln index 31a5df57e6..70771bdfd6 100644 --- a/src/AppInstallerCLI.sln +++ b/src/AppInstallerCLI.sln @@ -1087,7 +1087,7 @@ Global {C54F80ED-B736-49B0-9BD3-662F57024D01} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {272B2B0E-40D4-4F0F-B187-519A6EF89B10} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} {5A52D9FC-0059-4A4A-8196-427A7AA0D1C5} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9} - {76B26B2C-602A-4AD0-9736-4162D3FCA92A} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} + {76B26B2C-602A-4AD0-9736-4162D3FCA92A} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} {EF18BED6-EBC0-45E9-8D61-4202528E8AAB} = {1A5D7A7D-5CB2-47D5-B40D-4E61CAEDC798} {A0B4F808-B190-41C4-97CB-C8EA1932F84F} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7} {A33223D2-550B-4D99-A53D-488B1F68683E} = {60618CAC-2995-4DF9-9914-45C6FC02C995} From 60deede4770cfa979372e860a431154be1a32602 Mon Sep 17 00:00:00 2001 From: Flor Elisa Chacon Ochoa Date: Fri, 22 May 2026 18:36:16 -0700 Subject: [PATCH 6/6] \n --- src/VcpkgPortOverlay/README.md | 80 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/VcpkgPortOverlay/README.md b/src/VcpkgPortOverlay/README.md index eb9c86de9e..6067b54151 100644 --- a/src/VcpkgPortOverlay/README.md +++ b/src/VcpkgPortOverlay/README.md @@ -1,41 +1,41 @@ -# Overlay ports - -This directory contains an overlay for vcpkg ports, for cases where we need local modifications to a port. -In all cases, most of the recipe is taken from the [official vcpkg registry](https://github.com/Microsoft/vcpkg), and we only make small changes. - -The whole directory can be re-created with `.\CreatePortOverlay.ps1` - -## cpprestsdk - -We add support for certificate pinning. -Note that we use v2.10.18, which is not the latest. - -Changes: -* Add patch file: `add-server-certificate-validation.patch` -* Patch source: https://github.com/microsoft/winget-cli/commit/888b4ed8f4f7d25cb05a47210e083fe29348163b - -## detours - -We use the version used by UndockedRegFreeWinRT (https://github.com/microsoft/winget-cli/tree/release-v1.10/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/detours). -The only official release of detours (4.0.1) does not include complete support for ARM64. -While the exact version that we pulled from UndockedRegFreeWinRT is unclear (https://github.com/microsoft/xlang/pull/644), through manually comparing versions it is equivalent to -https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab (only some whitespace changes are present). - -Changes: -* New source commit: https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab -* Remove the patch on the official port as it is already present in the newer commit - -## sfs-client - -sfs-client is not in the official vcpkg registry. The port is based on the [template included in the sfs-client repository](https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client). - -We use version 1.1.0 at commit https://github.com/microsoft/sfs-client/commit/0e27525d597c730e71646fd0b15bdc8c8503f24d. -Tests and samples are disabled (`-DSFS_BUILD_TESTS=OFF -DSFS_BUILD_SAMPLES=OFF`). - -## libyaml - -We use an unreleased version that fixes a vulnerability. - -Changes: -* New source commit: https://github.com/yaml/libyaml/commit/840b65c40675e2d06bf40405ad3f12dec7f35923 +# Overlay ports + +This directory contains an overlay for vcpkg ports, for cases where we need local modifications to a port. +In all cases, most of the recipe is taken from the [official vcpkg registry](https://github.com/Microsoft/vcpkg), and we only make small changes. + +The whole directory can be re-created with `.\CreatePortOverlay.ps1` + +## cpprestsdk + +We add support for certificate pinning. +Note that we use v2.10.18, which is not the latest. + +Changes: +* Add patch file: `add-server-certificate-validation.patch` +* Patch source: https://github.com/microsoft/winget-cli/commit/888b4ed8f4f7d25cb05a47210e083fe29348163b + +## detours + +We use the version used by UndockedRegFreeWinRT (https://github.com/microsoft/winget-cli/tree/release-v1.10/src/Xlang/UndockedRegFreeWinRT/src/UndockedRegFreeWinRT/detours). +The only official release of detours (4.0.1) does not include complete support for ARM64. +While the exact version that we pulled from UndockedRegFreeWinRT is unclear (https://github.com/microsoft/xlang/pull/644), through manually comparing versions it is equivalent to +https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab (only some whitespace changes are present). + +Changes: +* New source commit: https://github.com/microsoft/Detours/commit/404c153ff390cb14f1787c7feeb4908c6d79b0ab +* Remove the patch on the official port as it is already present in the newer commit + +## sfs-client + +sfs-client is not in the official vcpkg registry. The port is based on the [template included in the sfs-client repository](https://github.com/microsoft/sfs-client/tree/main/sfs-client-vcpkg-port/sfs-client). + +We use version 1.1.0 at commit https://github.com/microsoft/sfs-client/commit/0e27525d597c730e71646fd0b15bdc8c8503f24d. +Tests and samples are disabled (`-DSFS_BUILD_TESTS=OFF -DSFS_BUILD_SAMPLES=OFF`). + +## libyaml + +We use an unreleased version that fixes a vulnerability. + +Changes: +* New source commit: https://github.com/yaml/libyaml/commit/840b65c40675e2d06bf40405ad3f12dec7f35923 * Increase the port version so that Component Governance doesn't see it as the vulnerable version anymore \ No newline at end of file