Initial commit
This commit is contained in:
Коммит
73bca2d926
|
@ -0,0 +1,78 @@
|
|||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## Mac OS generated
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData/
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata/
|
||||
|
||||
## Other
|
||||
*.moved-aside
|
||||
*.xccheckout
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
.build/
|
||||
.swiftpm/
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots/**/*.png
|
||||
fastlane/test_output
|
|
@ -0,0 +1,9 @@
|
|||
# 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
|
|
@ -0,0 +1,24 @@
|
|||
# Contributing to IomtFhirClient
|
||||
|
||||
This document describes guidelines for contributing to the IomtFhirClient repo.
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
- **DO** submit all changes via pull requests (PRs). They will be reviewed and potentially be merged by maintainers after a peer review that includes at least one of the team members.
|
||||
- **DO** give PRs short but descriptive names.
|
||||
- **DO** write a useful but brief description of what the PR is for.
|
||||
- **DO** refer to any relevant issues and use [keywords](https://help.github.com/articles/closing-issues-using-keywords/) that automatically close issues when the PR is merged.
|
||||
- **DO** ensure each commit successfully builds. The entire PR must pass all checks before it will be merged.
|
||||
- **DO** address PR feedback in additional commits instead of amending.
|
||||
- **DO** assume that [Squash and Merge](https://blog.github.com/2016-04-01-squash-your-commits/) will be used to merge the commits unless specifically requested otherwise.
|
||||
- **DO NOT** submit "work in progress" PRs. A PR should only be submitted when it is considered ready for review.
|
||||
- **DO NOT** mix independent and unrelated changes in one PR.
|
||||
|
||||
## Creating Issues
|
||||
|
||||
- **DO** use a descriptive title that identifies the issue or the requested feature.
|
||||
- **DO** write a detailed description of the issue or the requested feature.
|
||||
- **DO** provide details for issues you create:
|
||||
- Describe the expected and actual behavior.
|
||||
- Provide any relevant exception message or OperationOutcome.
|
||||
- **DO** subscribe to notifications for created issues in case there are any follow-up questions.
|
|
@ -0,0 +1,23 @@
|
|||
<!-- List of Include Files and folders and exception lists for this repo for GeoPolitical scanning. Contact Joerow for detail. -->
|
||||
<!-- Consult Global Readiness Notebook @ aka.ms/NExTGeoPol for further details -->
|
||||
<!-- This file is consumed by PowerShell scripts in the 'health-localization' repo under the LocBuild\GeoPolitical folder(s) -->
|
||||
<!DOCTYPE varsdefined [
|
||||
<!ENTITY GitReposFolder "C:\GITs\Repos">
|
||||
<!ENTITY GitRepoName "readmissions-event-hubs-ios">
|
||||
]>
|
||||
|
||||
<GeoPol_Folders>
|
||||
<!-- List of Folders to include for GeoPolitical scanning -->
|
||||
<GitRepoName>&GitRepoName;</GitRepoName>
|
||||
<Component Include="List here folders to Include in a GeoPol Scan">
|
||||
<!-- . means the entire repo -->
|
||||
<!-- Use back slash \ to indicate folder path e.g. C:\Temp\Git\ -->
|
||||
<IncludeFolder>.</IncludeFolder>
|
||||
</Component>
|
||||
<Component Exclude="List exceptions here to not be scanned, that have been included above">
|
||||
<!-- Make sure to consult http://aka.ms/NExtStart if excluding 3rd party or OSS components -->
|
||||
<!-- Use back slash \ to indicate folder path e.g. C:\Temp\Git\ -->
|
||||
<ExcludeFolder>.gitignore</ExcludeFolder>
|
||||
<ExcludeFolder>GeoPol.xml</ExcludeFolder>
|
||||
</Component>
|
||||
</GeoPol_Folders>
|
|
@ -0,0 +1,551 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 52;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A52BB53F2346970200650972 /* IomtFhirClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BB53E2346970200650972 /* IomtFhirClientTests.swift */; };
|
||||
A52BB541234697B400650972 /* IomtFhirClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BB540234697B400650972 /* IomtFhirClient.swift */; };
|
||||
A52BB543234697F500650972 /* IomtFhirClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BB542234697F500650972 /* IomtFhirClientError.swift */; };
|
||||
A52BB5452346986200650972 /* HttpIomtFhirClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BB5442346986200650972 /* HttpIomtFhirClient.swift */; };
|
||||
A55FD907231059AC003E6220 /* MockDataSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55FD906231059AC003E6220 /* MockDataSender.swift */; };
|
||||
A55FD9092310823A003E6220 /* MockError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55FD9082310823A003E6220 /* MockError.swift */; };
|
||||
A55FD90B23108C68003E6220 /* UrlExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55FD90A23108C68003E6220 /* UrlExtensionsTests.swift */; };
|
||||
A57A678D234650C9006BFEEE /* ConnectionManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6771234650C9006BFEEE /* ConnectionManagerProtocol.swift */; };
|
||||
A57A678E234650C9006BFEEE /* EventData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6772234650C9006BFEEE /* EventData.swift */; };
|
||||
A57A678F234650C9006BFEEE /* EventDataSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6773234650C9006BFEEE /* EventDataSenderProtocol.swift */; };
|
||||
A57A6790234650C9006BFEEE /* EventDataSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6774234650C9006BFEEE /* EventDataSender.swift */; };
|
||||
A57A6792234650C9006BFEEE /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6776234650C9006BFEEE /* ConnectionManager.swift */; };
|
||||
A57A6793234650C9006BFEEE /* UrlExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6778234650C9006BFEEE /* UrlExtensions.swift */; };
|
||||
A57A6794234650C9006BFEEE /* TransportType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A677A234650C9006BFEEE /* TransportType.swift */; };
|
||||
A57A6795234650C9006BFEEE /* TokenProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A677B234650C9006BFEEE /* TokenProviderProtocol.swift */; };
|
||||
A57A6796234650C9006BFEEE /* DateFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A677C234650C9006BFEEE /* DateFactory.swift */; };
|
||||
A57A6797234650C9006BFEEE /* EventHubsTokenError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A677D234650C9006BFEEE /* EventHubsTokenError.swift */; };
|
||||
A57A6798234650C9006BFEEE /* SecurityToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A677E234650C9006BFEEE /* SecurityToken.swift */; };
|
||||
A57A679A234650C9006BFEEE /* EventHubsMessageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6780234650C9006BFEEE /* EventHubsMessageError.swift */; };
|
||||
A57A679B234650C9006BFEEE /* EventHubsConnectionStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6781234650C9006BFEEE /* EventHubsConnectionStringBuilder.swift */; };
|
||||
A57A679C234650C9006BFEEE /* SharedAccessSignatureTokenProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6782234650C9006BFEEE /* SharedAccessSignatureTokenProvider.swift */; };
|
||||
A57A679D234650C9006BFEEE /* DateFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6783234650C9006BFEEE /* DateFactoryProtocol.swift */; };
|
||||
A57A679E234650C9006BFEEE /* TokenScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6784234650C9006BFEEE /* TokenScope.swift */; };
|
||||
A57A679F234650C9006BFEEE /* SharedAccessSignatureToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6785234650C9006BFEEE /* SharedAccessSignatureToken.swift */; };
|
||||
A57A67A0234650C9006BFEEE /* HttpEventDataSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6787234650C9006BFEEE /* HttpEventDataSender.swift */; };
|
||||
A57A67A1234650C9006BFEEE /* HttpMessageConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A6788234650C9006BFEEE /* HttpMessageConverter.swift */; };
|
||||
A57A67A3234650C9006BFEEE /* HttpMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A678A234650C9006BFEEE /* HttpMessage.swift */; };
|
||||
A57A67A4234650C9006BFEEE /* HttpEventHubsConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A678B234650C9006BFEEE /* HttpEventHubsConnection.swift */; };
|
||||
A57A67A5234650C9006BFEEE /* EventHubsConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A57A678C234650C9006BFEEE /* EventHubsConnection.swift */; };
|
||||
A57A67A823465159006BFEEE /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = A57A67A723465159006BFEEE /* Quick */; };
|
||||
A57A67AB2346518A006BFEEE /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = A57A67AA2346518A006BFEEE /* Nimble */; };
|
||||
A58684F923148E67003FEC6A /* HttpEventDataSenderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58684F823148E67003FEC6A /* HttpEventDataSenderTests.swift */; };
|
||||
A58684FB23148E8A003FEC6A /* HttpEventHubsConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58684FA23148E8A003FEC6A /* HttpEventHubsConnectionTests.swift */; };
|
||||
A58684FD23148EA5003FEC6A /* HttpMessageConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58684FC23148EA5003FEC6A /* HttpMessageConverterTests.swift */; };
|
||||
A58684FF23148EBE003FEC6A /* EventHubsConnectionStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58684FE23148EBE003FEC6A /* EventHubsConnectionStringBuilderTests.swift */; };
|
||||
A586850123148ED9003FEC6A /* SharedAccessSignatureTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A586850023148ED9003FEC6A /* SharedAccessSignatureTokenTests.swift */; };
|
||||
A586850323148EF0003FEC6A /* SharedAccessSignatureTokenProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A586850223148EF0003FEC6A /* SharedAccessSignatureTokenProviderTests.swift */; };
|
||||
A58685052314947D003FEC6A /* MockConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58685042314947D003FEC6A /* MockConnectionManager.swift */; };
|
||||
A58685072314B59D003FEC6A /* MockHttpEventHubsConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58685062314B59D003FEC6A /* MockHttpEventHubsConnection.swift */; };
|
||||
A58685092314D4E0003FEC6A /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58685082314D4E0003FEC6A /* MockURLSession.swift */; };
|
||||
A586850B2314D6B1003FEC6A /* MockTokenProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A586850A2314D6B1003FEC6A /* MockTokenProvider.swift */; };
|
||||
A586850D23157A8D003FEC6A /* MockURLSessionUploadTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = A586850C23157A8D003FEC6A /* MockURLSessionUploadTask.swift */; };
|
||||
A5F46C4823186E4C009AB43E /* MockDateFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5F46C4723186E4C009AB43E /* MockDateFactory.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
607FACE51AFB9204008FA782 /* IomtFhirClient_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IomtFhirClient_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
A52BB53E2346970200650972 /* IomtFhirClientTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IomtFhirClientTests.swift; sourceTree = "<group>"; };
|
||||
A52BB540234697B400650972 /* IomtFhirClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IomtFhirClient.swift; sourceTree = "<group>"; };
|
||||
A52BB542234697F500650972 /* IomtFhirClientError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IomtFhirClientError.swift; sourceTree = "<group>"; };
|
||||
A52BB5442346986200650972 /* HttpIomtFhirClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpIomtFhirClient.swift; sourceTree = "<group>"; };
|
||||
A55FD906231059AC003E6220 /* MockDataSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataSender.swift; sourceTree = "<group>"; };
|
||||
A55FD9082310823A003E6220 /* MockError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockError.swift; sourceTree = "<group>"; };
|
||||
A55FD90A23108C68003E6220 /* UrlExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlExtensionsTests.swift; sourceTree = "<group>"; };
|
||||
A57A6771234650C9006BFEEE /* ConnectionManagerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManagerProtocol.swift; sourceTree = "<group>"; };
|
||||
A57A6772234650C9006BFEEE /* EventData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventData.swift; sourceTree = "<group>"; };
|
||||
A57A6773234650C9006BFEEE /* EventDataSenderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventDataSenderProtocol.swift; sourceTree = "<group>"; };
|
||||
A57A6774234650C9006BFEEE /* EventDataSender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventDataSender.swift; sourceTree = "<group>"; };
|
||||
A57A6776234650C9006BFEEE /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
|
||||
A57A6778234650C9006BFEEE /* UrlExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UrlExtensions.swift; sourceTree = "<group>"; };
|
||||
A57A677A234650C9006BFEEE /* TransportType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransportType.swift; sourceTree = "<group>"; };
|
||||
A57A677B234650C9006BFEEE /* TokenProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
A57A677C234650C9006BFEEE /* DateFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFactory.swift; sourceTree = "<group>"; };
|
||||
A57A677D234650C9006BFEEE /* EventHubsTokenError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHubsTokenError.swift; sourceTree = "<group>"; };
|
||||
A57A677E234650C9006BFEEE /* SecurityToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurityToken.swift; sourceTree = "<group>"; };
|
||||
A57A6780234650C9006BFEEE /* EventHubsMessageError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHubsMessageError.swift; sourceTree = "<group>"; };
|
||||
A57A6781234650C9006BFEEE /* EventHubsConnectionStringBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHubsConnectionStringBuilder.swift; sourceTree = "<group>"; };
|
||||
A57A6782234650C9006BFEEE /* SharedAccessSignatureTokenProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedAccessSignatureTokenProvider.swift; sourceTree = "<group>"; };
|
||||
A57A6783234650C9006BFEEE /* DateFactoryProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFactoryProtocol.swift; sourceTree = "<group>"; };
|
||||
A57A6784234650C9006BFEEE /* TokenScope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenScope.swift; sourceTree = "<group>"; };
|
||||
A57A6785234650C9006BFEEE /* SharedAccessSignatureToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedAccessSignatureToken.swift; sourceTree = "<group>"; };
|
||||
A57A6787234650C9006BFEEE /* HttpEventDataSender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpEventDataSender.swift; sourceTree = "<group>"; };
|
||||
A57A6788234650C9006BFEEE /* HttpMessageConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpMessageConverter.swift; sourceTree = "<group>"; };
|
||||
A57A678A234650C9006BFEEE /* HttpMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpMessage.swift; sourceTree = "<group>"; };
|
||||
A57A678B234650C9006BFEEE /* HttpEventHubsConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpEventHubsConnection.swift; sourceTree = "<group>"; };
|
||||
A57A678C234650C9006BFEEE /* EventHubsConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHubsConnection.swift; sourceTree = "<group>"; };
|
||||
A58684F823148E67003FEC6A /* HttpEventDataSenderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpEventDataSenderTests.swift; sourceTree = "<group>"; };
|
||||
A58684FA23148E8A003FEC6A /* HttpEventHubsConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpEventHubsConnectionTests.swift; sourceTree = "<group>"; };
|
||||
A58684FC23148EA5003FEC6A /* HttpMessageConverterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpMessageConverterTests.swift; sourceTree = "<group>"; };
|
||||
A58684FE23148EBE003FEC6A /* EventHubsConnectionStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventHubsConnectionStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
A586850023148ED9003FEC6A /* SharedAccessSignatureTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccessSignatureTokenTests.swift; sourceTree = "<group>"; };
|
||||
A586850223148EF0003FEC6A /* SharedAccessSignatureTokenProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccessSignatureTokenProviderTests.swift; sourceTree = "<group>"; };
|
||||
A58685042314947D003FEC6A /* MockConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConnectionManager.swift; sourceTree = "<group>"; };
|
||||
A58685062314B59D003FEC6A /* MockHttpEventHubsConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHttpEventHubsConnection.swift; sourceTree = "<group>"; };
|
||||
A58685082314D4E0003FEC6A /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = "<group>"; };
|
||||
A586850A2314D6B1003FEC6A /* MockTokenProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTokenProvider.swift; sourceTree = "<group>"; };
|
||||
A586850C23157A8D003FEC6A /* MockURLSessionUploadTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSessionUploadTask.swift; sourceTree = "<group>"; };
|
||||
A5F46C4723186E4C009AB43E /* MockDateFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDateFactory.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
607FACE21AFB9204008FA782 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A57A67A823465159006BFEEE /* Quick in Frameworks */,
|
||||
A57A67AB2346518A006BFEEE /* Nimble in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
607FACC71AFB9204008FA782 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
607FACE81AFB9204008FA782 /* Tests */,
|
||||
A57A6770234650C9006BFEEE /* Sources */,
|
||||
607FACD11AFB9204008FA782 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
607FACD11AFB9204008FA782 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
607FACE51AFB9204008FA782 /* IomtFhirClient_Tests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
607FACE81AFB9204008FA782 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A55FD90523105992003E6220 /* Mocks */,
|
||||
A52BB53E2346970200650972 /* IomtFhirClientTests.swift */,
|
||||
A55FD90A23108C68003E6220 /* UrlExtensionsTests.swift */,
|
||||
A58684F823148E67003FEC6A /* HttpEventDataSenderTests.swift */,
|
||||
A58684FA23148E8A003FEC6A /* HttpEventHubsConnectionTests.swift */,
|
||||
A58684FC23148EA5003FEC6A /* HttpMessageConverterTests.swift */,
|
||||
A58684FE23148EBE003FEC6A /* EventHubsConnectionStringBuilderTests.swift */,
|
||||
A586850023148ED9003FEC6A /* SharedAccessSignatureTokenTests.swift */,
|
||||
A586850223148EF0003FEC6A /* SharedAccessSignatureTokenProviderTests.swift */,
|
||||
607FACE91AFB9204008FA782 /* Supporting Files */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
607FACE91AFB9204008FA782 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
607FACEA1AFB9204008FA782 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A55FD90523105992003E6220 /* Mocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A55FD906231059AC003E6220 /* MockDataSender.swift */,
|
||||
A55FD9082310823A003E6220 /* MockError.swift */,
|
||||
A58685042314947D003FEC6A /* MockConnectionManager.swift */,
|
||||
A58685062314B59D003FEC6A /* MockHttpEventHubsConnection.swift */,
|
||||
A58685082314D4E0003FEC6A /* MockURLSession.swift */,
|
||||
A586850A2314D6B1003FEC6A /* MockTokenProvider.swift */,
|
||||
A586850C23157A8D003FEC6A /* MockURLSessionUploadTask.swift */,
|
||||
A5F46C4723186E4C009AB43E /* MockDateFactory.swift */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A57A6770234650C9006BFEEE /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A52BB540234697B400650972 /* IomtFhirClient.swift */,
|
||||
A57A6771234650C9006BFEEE /* ConnectionManagerProtocol.swift */,
|
||||
A57A6776234650C9006BFEEE /* ConnectionManager.swift */,
|
||||
A57A6772234650C9006BFEEE /* EventData.swift */,
|
||||
A57A6773234650C9006BFEEE /* EventDataSenderProtocol.swift */,
|
||||
A57A6774234650C9006BFEEE /* EventDataSender.swift */,
|
||||
A57A6777234650C9006BFEEE /* Extensions */,
|
||||
A57A6779234650C9006BFEEE /* Primitives */,
|
||||
A57A6786234650C9006BFEEE /* Http */,
|
||||
A57A678C234650C9006BFEEE /* EventHubsConnection.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A57A6777234650C9006BFEEE /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A57A6778234650C9006BFEEE /* UrlExtensions.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A57A6779234650C9006BFEEE /* Primitives */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A52BB542234697F500650972 /* IomtFhirClientError.swift */,
|
||||
A57A677D234650C9006BFEEE /* EventHubsTokenError.swift */,
|
||||
A57A6780234650C9006BFEEE /* EventHubsMessageError.swift */,
|
||||
A57A677A234650C9006BFEEE /* TransportType.swift */,
|
||||
A57A677B234650C9006BFEEE /* TokenProviderProtocol.swift */,
|
||||
A57A677C234650C9006BFEEE /* DateFactory.swift */,
|
||||
A57A677E234650C9006BFEEE /* SecurityToken.swift */,
|
||||
A57A6781234650C9006BFEEE /* EventHubsConnectionStringBuilder.swift */,
|
||||
A57A6782234650C9006BFEEE /* SharedAccessSignatureTokenProvider.swift */,
|
||||
A57A6783234650C9006BFEEE /* DateFactoryProtocol.swift */,
|
||||
A57A6784234650C9006BFEEE /* TokenScope.swift */,
|
||||
A57A6785234650C9006BFEEE /* SharedAccessSignatureToken.swift */,
|
||||
);
|
||||
path = Primitives;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A57A6786234650C9006BFEEE /* Http */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A57A6787234650C9006BFEEE /* HttpEventDataSender.swift */,
|
||||
A57A678B234650C9006BFEEE /* HttpEventHubsConnection.swift */,
|
||||
A52BB5442346986200650972 /* HttpIomtFhirClient.swift */,
|
||||
A57A678A234650C9006BFEEE /* HttpMessage.swift */,
|
||||
A57A6788234650C9006BFEEE /* HttpMessageConverter.swift */,
|
||||
);
|
||||
path = Http;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
607FACE41AFB9204008FA782 /* IomtFhirClient_Tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "IomtFhirClient_Tests" */;
|
||||
buildPhases = (
|
||||
607FACE11AFB9204008FA782 /* Sources */,
|
||||
607FACE21AFB9204008FA782 /* Frameworks */,
|
||||
607FACE31AFB9204008FA782 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = IomtFhirClient_Tests;
|
||||
packageProductDependencies = (
|
||||
A57A67A723465159006BFEEE /* Quick */,
|
||||
A57A67AA2346518A006BFEEE /* Nimble */,
|
||||
);
|
||||
productName = Tests;
|
||||
productReference = 607FACE51AFB9204008FA782 /* IomtFhirClient_Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
607FACC81AFB9204008FA782 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0830;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = CocoaPods;
|
||||
TargetAttributes = {
|
||||
607FACE41AFB9204008FA782 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
LastSwiftMigration = 1020;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "IomtFhirClient" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 607FACC71AFB9204008FA782;
|
||||
packageReferences = (
|
||||
A57A67A623465159006BFEEE /* XCRemoteSwiftPackageReference "Quick" */,
|
||||
A57A67A92346518A006BFEEE /* XCRemoteSwiftPackageReference "Nimble" */,
|
||||
);
|
||||
productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
607FACE41AFB9204008FA782 /* IomtFhirClient_Tests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
607FACE31AFB9204008FA782 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
607FACE11AFB9204008FA782 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A57A6797234650C9006BFEEE /* EventHubsTokenError.swift in Sources */,
|
||||
A5F46C4823186E4C009AB43E /* MockDateFactory.swift in Sources */,
|
||||
A55FD907231059AC003E6220 /* MockDataSender.swift in Sources */,
|
||||
A52BB53F2346970200650972 /* IomtFhirClientTests.swift in Sources */,
|
||||
A57A678E234650C9006BFEEE /* EventData.swift in Sources */,
|
||||
A55FD90B23108C68003E6220 /* UrlExtensionsTests.swift in Sources */,
|
||||
A58685052314947D003FEC6A /* MockConnectionManager.swift in Sources */,
|
||||
A57A6795234650C9006BFEEE /* TokenProviderProtocol.swift in Sources */,
|
||||
A52BB5452346986200650972 /* HttpIomtFhirClient.swift in Sources */,
|
||||
A57A6790234650C9006BFEEE /* EventDataSender.swift in Sources */,
|
||||
A52BB543234697F500650972 /* IomtFhirClientError.swift in Sources */,
|
||||
A57A679C234650C9006BFEEE /* SharedAccessSignatureTokenProvider.swift in Sources */,
|
||||
A58684FB23148E8A003FEC6A /* HttpEventHubsConnectionTests.swift in Sources */,
|
||||
A57A67A4234650C9006BFEEE /* HttpEventHubsConnection.swift in Sources */,
|
||||
A57A67A3234650C9006BFEEE /* HttpMessage.swift in Sources */,
|
||||
A58684FF23148EBE003FEC6A /* EventHubsConnectionStringBuilderTests.swift in Sources */,
|
||||
A57A67A0234650C9006BFEEE /* HttpEventDataSender.swift in Sources */,
|
||||
A57A679B234650C9006BFEEE /* EventHubsConnectionStringBuilder.swift in Sources */,
|
||||
A57A6792234650C9006BFEEE /* ConnectionManager.swift in Sources */,
|
||||
A57A6793234650C9006BFEEE /* UrlExtensions.swift in Sources */,
|
||||
A57A679E234650C9006BFEEE /* TokenScope.swift in Sources */,
|
||||
A586850323148EF0003FEC6A /* SharedAccessSignatureTokenProviderTests.swift in Sources */,
|
||||
A57A679D234650C9006BFEEE /* DateFactoryProtocol.swift in Sources */,
|
||||
A586850D23157A8D003FEC6A /* MockURLSessionUploadTask.swift in Sources */,
|
||||
A57A6798234650C9006BFEEE /* SecurityToken.swift in Sources */,
|
||||
A57A67A1234650C9006BFEEE /* HttpMessageConverter.swift in Sources */,
|
||||
A58684FD23148EA5003FEC6A /* HttpMessageConverterTests.swift in Sources */,
|
||||
A586850123148ED9003FEC6A /* SharedAccessSignatureTokenTests.swift in Sources */,
|
||||
A586850B2314D6B1003FEC6A /* MockTokenProvider.swift in Sources */,
|
||||
A55FD9092310823A003E6220 /* MockError.swift in Sources */,
|
||||
A57A679F234650C9006BFEEE /* SharedAccessSignatureToken.swift in Sources */,
|
||||
A57A6796234650C9006BFEEE /* DateFactory.swift in Sources */,
|
||||
A58684F923148E67003FEC6A /* HttpEventDataSenderTests.swift in Sources */,
|
||||
A52BB541234697B400650972 /* IomtFhirClient.swift in Sources */,
|
||||
A58685072314B59D003FEC6A /* MockHttpEventHubsConnection.swift in Sources */,
|
||||
A57A678F234650C9006BFEEE /* EventDataSenderProtocol.swift in Sources */,
|
||||
A57A6794234650C9006BFEEE /* TransportType.swift in Sources */,
|
||||
A57A678D234650C9006BFEEE /* ConnectionManagerProtocol.swift in Sources */,
|
||||
A57A67A5234650C9006BFEEE /* EventHubsConnection.swift in Sources */,
|
||||
A58685092314D4E0003FEC6A /* MockURLSession.swift in Sources */,
|
||||
A57A679A234650C9006BFEEE /* EventHubsMessageError.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
607FACED1AFB9204008FA782 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
607FACEE1AFB9204008FA782 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
607FACF31AFB9204008FA782 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
607FACF41AFB9204008FA782 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "IomtFhirClient" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
607FACED1AFB9204008FA782 /* Debug */,
|
||||
607FACEE1AFB9204008FA782 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "IomtFhirClient_Tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
607FACF31AFB9204008FA782 /* Debug */,
|
||||
607FACF41AFB9204008FA782 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
A57A67A623465159006BFEEE /* XCRemoteSwiftPackageReference "Quick" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Quick/Quick";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.2.0;
|
||||
};
|
||||
};
|
||||
A57A67A92346518A006BFEEE /* XCRemoteSwiftPackageReference "Nimble" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Quick/Nimble";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 8.0.2;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
A57A67A723465159006BFEEE /* Quick */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = A57A67A623465159006BFEEE /* XCRemoteSwiftPackageReference "Quick" */;
|
||||
productName = Quick;
|
||||
};
|
||||
A57A67AA2346518A006BFEEE /* Nimble */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = A57A67A92346518A006BFEEE /* XCRemoteSwiftPackageReference "Nimble" */;
|
||||
productName = Nimble;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 607FACC81AFB9204008FA782 /* Project object */;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:AzureEventHubs.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:IomtFhirClient.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Nimble",
|
||||
"repositoryURL": "https://github.com/Quick/Nimble",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f8657642dfdec9973efc79cc68bcef43a653a2bc",
|
||||
"version": "8.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Quick",
|
||||
"repositoryURL": "https://github.com/Quick/Quick",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "33682c2f6230c60614861dfc61df267e11a1602f",
|
||||
"version": "2.2.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACE41AFB9204008FA782"
|
||||
BuildableName = "IomtFhirClient_Tests.xctest"
|
||||
BlueprintName = "IomtFhirClient_Tests"
|
||||
ReferencedContainer = "container:IomtFhirClient.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACE41AFB9204008FA782"
|
||||
BuildableName = "AzureEventHubs_Tests.xctest"
|
||||
BlueprintName = "AzureEventHubs_Tests"
|
||||
ReferencedContainer = "container:AzureEventHubs.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACE41AFB9204008FA782"
|
||||
BuildableName = "IomtFhirClient_Tests.xctest"
|
||||
BlueprintName = "IomtFhirClient_Tests"
|
||||
ReferencedContainer = "container:IomtFhirClient.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "607FACE41AFB9204008FA782"
|
||||
BuildableName = "IomtFhirClient_Tests.xctest"
|
||||
BlueprintName = "IomtFhirClient_Tests"
|
||||
ReferencedContainer = "container:IomtFhirClient.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,21 @@
|
|||
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
|
|
@ -0,0 +1,25 @@
|
|||
// swift-tools-version:5.0
|
||||
// Package.swift
|
||||
// AzureEventHubs
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "IomtFhirClient",
|
||||
platforms: [
|
||||
.iOS(.v11)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "IomtFhirClient",
|
||||
targets: ["IomtFhirClient"]),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "IomtFhirClient",
|
||||
path: "Sources"),
|
||||
]
|
||||
)
|
|
@ -0,0 +1,29 @@
|
|||
# Xcode
|
||||
# Build, test, and archive an Xcode workspace on macOS.
|
||||
# Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/xcode
|
||||
|
||||
trigger: none
|
||||
|
||||
schedules:
|
||||
- cron: "0 12 * * *"
|
||||
displayName: Daily build
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'macos-latest'
|
||||
|
||||
steps:
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
actions: 'test'
|
||||
configuration: 'Debug'
|
||||
sdk: 'iphoneos'
|
||||
xcWorkspacePath: '**/IomtFhirClient.xcworkspace'
|
||||
scheme: 'IomtFhirClient_Tests'
|
||||
xcodeVersion: '11'
|
||||
packageApp: false
|
||||
destinationPlatformOption: 'iOS'
|
||||
destinationSimulators: 'iPhone 8'
|
|
@ -0,0 +1,22 @@
|
|||
# Xcode
|
||||
# Build, test, and archive an Xcode workspace on macOS.
|
||||
# Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/xcode
|
||||
|
||||
trigger: none
|
||||
|
||||
pool:
|
||||
vmImage: 'macos-latest'
|
||||
|
||||
steps:
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
actions: 'test'
|
||||
configuration: 'Debug'
|
||||
sdk: 'iphoneos'
|
||||
xcWorkspacePath: '**/IomtFhirClient.xcworkspace'
|
||||
scheme: 'IomtFhirClient_Tests'
|
||||
xcodeVersion: '11'
|
||||
packageApp: false
|
||||
destinationPlatformOption: 'iOS'
|
||||
destinationSimulators: 'iPhone 8'
|
|
@ -0,0 +1,69 @@
|
|||
# IomtFhirClient Swift Library
|
||||
|
||||
[![Build Status](https://microsofthealth.visualstudio.com/Health/_apis/build/status/POET/IomtFhirClient_Daily?branchName=master)](https://microsofthealth.visualstudio.com/Health/_build/latest?definitionId=436&branchName=master)
|
||||
|
||||
The IomtFhirClient Swift library simplifies sending IoMT (Internet of Medical Things) data to an [IoMT FHIR Connector for Azure](https://github.com/microsoft/iomt-fhir) endpoint for persistance in a FHIR® server.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Instantiate an IomtFhirClient
|
||||
|
||||
An IomtFhirClient can be instantiated using a connection string.
|
||||
|
||||
```swift
|
||||
let iomtFhirClient = try IomtFhirClient.CreateFromConnectionString(connectionString: "YOUR_CONNECTION_STRING")
|
||||
```
|
||||
|
||||
### Create EventData
|
||||
|
||||
In the example below, a simple json payload is used to create an EventData object.
|
||||
|
||||
```swift
|
||||
let json = "{\"eventPayload\":\"payload data\"}"
|
||||
|
||||
let payload = json.data(using: .utf8)
|
||||
|
||||
let eventData = EventData(data: payload)
|
||||
```
|
||||
|
||||
### Send the data to the Azure Event Hub
|
||||
|
||||
The IomtFhirClient has methods for sending single EventHub objects or collections.
|
||||
|
||||
```swift
|
||||
do {
|
||||
try iomtFhirClient.send(eventData: eventData) { (success, error) in
|
||||
if (success && error == nil) {
|
||||
// The event was send successfully
|
||||
} else {
|
||||
// Handle any errors
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Handle any errors
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
There are many other ways to contribute to the IomtFhirClient Project.
|
||||
|
||||
* [Submit bugs](https://github.com/microsoft/iomt-fhir-client/issues) and help us verify fixes as they are checked in.
|
||||
* Review the [source code changes](https://github.com/microsoft/iomt-fhir-client/pulls).
|
||||
* [Contribute bug fixes](CONTRIBUTING.md).
|
||||
|
||||
See [Contributing to IomtFhirClient](CONTRIBUTING.md) for more information.
|
||||
|
||||
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.
|
||||
|
||||
FHIR® is the registered trademark of HL7 and is used with the permission of HL7. Use of the FHIR trademark does not constitute endorsement of this product by HL7.
|
|
@ -0,0 +1,41 @@
|
|||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.3 BLOCK -->
|
||||
|
||||
## 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), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, 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://msrc.microsoft.com/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 the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
|
||||
|
||||
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://microsoft.com/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://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class ConnectionManager : ConnectionManagerProtocol
|
||||
{
|
||||
private var connectionMap: [String : EventHubsConnection] = [:]
|
||||
private let lock = NSObject()
|
||||
|
||||
internal func getOrCreate<T: EventHubsConnection>(iomtFhirClient: IomtFhirClient, partitionId: String?) throws -> T
|
||||
{
|
||||
objc_sync_enter(lock)
|
||||
defer
|
||||
{
|
||||
objc_sync_exit(lock)
|
||||
}
|
||||
|
||||
let key = String(describing: T.self)
|
||||
|
||||
if let manager = connectionMap[key],
|
||||
manager is T
|
||||
{
|
||||
return manager as! T
|
||||
}
|
||||
|
||||
let manager = try T.init(iomtFhirClient: iomtFhirClient, partitionId: partitionId)
|
||||
connectionMap[key] = manager
|
||||
return manager;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol ConnectionManagerProtocol
|
||||
{
|
||||
func getOrCreate<T: EventHubsConnection>(iomtFhirClient: IomtFhirClient, partitionId: String?) throws -> T
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class EventData
|
||||
{
|
||||
public var properties: [String : Any] = [:]
|
||||
|
||||
public var data: Data
|
||||
|
||||
public init(data: Data)
|
||||
{
|
||||
self.data = data
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class EventDataSender : EventDataSenderProtocol
|
||||
{
|
||||
internal let iomtFhirClient: IomtFhirClient
|
||||
|
||||
internal let partitionId: String?
|
||||
|
||||
internal init(iomtFhirClient: IomtFhirClient, partitionId: String? = nil)
|
||||
{
|
||||
self.iomtFhirClient = iomtFhirClient
|
||||
self.partitionId = partitionId
|
||||
}
|
||||
|
||||
internal func send(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
try onSend(eventDatas: eventDatas, completion: completion)
|
||||
}
|
||||
|
||||
internal func onSend(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
preconditionFailure("Method mus be overridden by child class.")
|
||||
}
|
||||
|
||||
internal static func validateEvents(eventDatas: [EventData]) throws -> Int
|
||||
{
|
||||
let count = eventDatas.count
|
||||
|
||||
if count == 0
|
||||
{
|
||||
throw IomtFhirClientError.eventDataEmpty
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol EventDataSenderProtocol
|
||||
{
|
||||
func send(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
|
||||
func onSend(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
|
||||
static func validateEvents(eventDatas: [EventData]) throws -> Int
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class EventHubsConnection
|
||||
{
|
||||
internal let iomtFhirClient: IomtFhirClient
|
||||
|
||||
internal let partitionId: String?
|
||||
|
||||
internal required init(iomtFhirClient: IomtFhirClient, partitionId: String?) throws
|
||||
{
|
||||
self.iomtFhirClient = iomtFhirClient
|
||||
self.partitionId = partitionId
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal extension URL
|
||||
{
|
||||
func appliesToUriString(tokenScope: TokenScope, ensureTrailingSlash: Bool) -> String?
|
||||
{
|
||||
var uri = ""
|
||||
|
||||
if let host = self.host
|
||||
{
|
||||
uri.append(host)
|
||||
}
|
||||
|
||||
if tokenScope == .entity
|
||||
{
|
||||
if !self.path.starts(with: "/")
|
||||
{
|
||||
uri.append("/")
|
||||
}
|
||||
|
||||
uri.append(self.path)
|
||||
}
|
||||
|
||||
if ensureTrailingSlash,
|
||||
!uri.hasSuffix("/")
|
||||
{
|
||||
uri.append("/")
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class HttpEventDataSender : EventDataSender
|
||||
{
|
||||
override internal func onSend(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
// Ensure the data is valid.
|
||||
let _ = try EventDataSender.validateEvents(eventDatas: eventDatas)
|
||||
|
||||
// Convert to the event data to an http message.
|
||||
let message = try HttpMessageConverter.EventDatasToHttpMessage(eventDatas: eventDatas, partitionId: partitionId)
|
||||
|
||||
// Get or create the connection and send the events.
|
||||
let connection: HttpEventHubsConnection = try iomtFhirClient.connectionManager.getOrCreate(iomtFhirClient: iomtFhirClient, partitionId: partitionId)
|
||||
try connection.send(httpMessage: message, completion: { (success, error) in completion(success, error) })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class HttpEventHubsConnection : EventHubsConnection
|
||||
{
|
||||
internal var session = URLSession.shared
|
||||
|
||||
private let httpMethod = "POST"
|
||||
private let httpHeaderFieldHost = "Host"
|
||||
private let httpHeaderFieldContentType = "Content-Type"
|
||||
private let httpHeaderFieldAuthorization = "Authorization"
|
||||
|
||||
private let endpoint: URL
|
||||
|
||||
internal required init(iomtFhirClient: IomtFhirClient, partitionId: String?) throws
|
||||
{
|
||||
guard let endpoint = iomtFhirClient.connectionStringBuilder.endpoint else
|
||||
{
|
||||
throw IomtFhirClientError.invalidConnectionString(reason: "A valid URL could not be created using the provided connection string.")
|
||||
}
|
||||
|
||||
self.endpoint = endpoint
|
||||
|
||||
try super.init(iomtFhirClient: iomtFhirClient, partitionId: partitionId)
|
||||
}
|
||||
|
||||
internal func send(httpMessage: HttpMessage, completion: ((Bool, Error?) -> Void)?) throws
|
||||
{
|
||||
let request = try generateRequest(httpMessage: httpMessage)
|
||||
let task = session.uploadTask(with: request, from: httpMessage.httpBody) { (data, response, error) in
|
||||
completion?(self.isSuccessful(response: response, error: error), error)
|
||||
}
|
||||
|
||||
task.resume()
|
||||
}
|
||||
|
||||
private func generateRequest(httpMessage: HttpMessage) throws -> URLRequest
|
||||
{
|
||||
var request = URLRequest(url: endpoint)
|
||||
request.httpMethod = httpMethod
|
||||
|
||||
request.addValue(httpMessage.contentType, forHTTPHeaderField: httpHeaderFieldContentType)
|
||||
|
||||
if let host = endpoint.host
|
||||
{
|
||||
request.addValue(host, forHTTPHeaderField: httpHeaderFieldHost)
|
||||
}
|
||||
|
||||
if let token = try iomtFhirClient.tokenProvider?.getToken(appliesTo: endpoint, timeToLive: nil)
|
||||
{
|
||||
request.addValue(token.tokenValue, forHTTPHeaderField: httpHeaderFieldAuthorization)
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
private func isSuccessful(response: URLResponse?, error: Error?) -> Bool
|
||||
{
|
||||
guard error == nil else
|
||||
{
|
||||
return false
|
||||
}
|
||||
|
||||
if let httpResponse = response as? HTTPURLResponse
|
||||
{
|
||||
return httpResponse.statusCode < 300
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class HttpIomtFhirClient : IomtFhirClient
|
||||
{
|
||||
|
||||
/// An specific implemetation of the data sender can be provided for testing.
|
||||
public var eventDataSender: EventDataSenderProtocol?
|
||||
|
||||
internal convenience init(connectionStringBuilder: EventHubsConnectionStringBuilder) throws
|
||||
{
|
||||
var tokenProvider: SharedAccessSignatureTokenProvider
|
||||
|
||||
// Create the token provider
|
||||
if let sharedAccessSignature = connectionStringBuilder.sharedAccessSignature
|
||||
{
|
||||
tokenProvider = try SharedAccessSignatureTokenProvider(sharedAccessSignature: sharedAccessSignature)
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenProvider = SharedAccessSignatureTokenProvider(keyName: connectionStringBuilder.sasKeyName!, sharedAccessKey: connectionStringBuilder.sasKey!)
|
||||
}
|
||||
|
||||
try self.init(connectionStringBuilder: connectionStringBuilder, tokenProvider: tokenProvider);
|
||||
}
|
||||
|
||||
override internal func onCreateEventSender(partitionId: String? = nil) -> EventDataSenderProtocol
|
||||
{
|
||||
if eventDataSender != nil
|
||||
{
|
||||
return eventDataSender!
|
||||
}
|
||||
|
||||
return HttpEventDataSender(iomtFhirClient: self, partitionId: partitionId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class HttpMessage
|
||||
{
|
||||
internal let httpBody: Data
|
||||
|
||||
internal let contentType: String
|
||||
|
||||
internal init(httpBody: Data, contentType: String)
|
||||
{
|
||||
self.httpBody = httpBody
|
||||
self.contentType = contentType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class HttpMessageConverter
|
||||
{
|
||||
private static let bodyKey = "Body"
|
||||
private static let userPropertiesKey = "UserProperties"
|
||||
private static let brokerPropertiesKey = "BrokerProperties"
|
||||
private static let serviceBusBatchContentType = "application/vnd.microsoft.servicebus.json"
|
||||
|
||||
internal static func EventDatasToHttpMessage(eventDatas: [EventData], partitionId: String?) throws -> HttpMessage
|
||||
{
|
||||
guard eventDatas.count > 0 else
|
||||
{
|
||||
throw EventHubsMessageError.noEventData
|
||||
}
|
||||
|
||||
var payloadArray: [[String : Any]] = []
|
||||
|
||||
for eventdata in eventDatas
|
||||
{
|
||||
if let dataString = String(data: eventdata.data, encoding: .utf8),
|
||||
dataString.count > 0
|
||||
{
|
||||
payloadArray.append([bodyKey : dataString])
|
||||
|
||||
// Add user provided properties.
|
||||
if eventdata.properties.count > 0
|
||||
{
|
||||
payloadArray.append([userPropertiesKey : eventdata.properties])
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw EventHubsMessageError.serializationError(reason: "One or more event data could not be serialized.")
|
||||
}
|
||||
}
|
||||
|
||||
if (JSONSerialization.isValidJSONObject(payloadArray))
|
||||
{
|
||||
if let jsonData = try? JSONSerialization.data(withJSONObject: payloadArray, options: [])
|
||||
{
|
||||
return HttpMessage(httpBody: jsonData, contentType: serviceBusBatchContentType)
|
||||
}
|
||||
}
|
||||
|
||||
throw EventHubsMessageError.serializationError(reason: "The event data could not be serialized into valid JSON.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class IomtFhirClient
|
||||
{
|
||||
public let eventHubName: String?
|
||||
|
||||
public lazy var connectionManager: ConnectionManagerProtocol = { return ConnectionManager() }()
|
||||
|
||||
public var tokenProvider: TokenProviderProtocol?
|
||||
|
||||
internal let connectionStringBuilder: EventHubsConnectionStringBuilder
|
||||
|
||||
public lazy var sender: EventDataSenderProtocol = { return onCreateEventSender() }()
|
||||
|
||||
/// Creates a new instance of the IoMT FHIR Client using the specified connection string. You can populate the EntityPath property with the name of the Event Hub.
|
||||
///
|
||||
/// - Parameter connectionString: The IoMT FHIR Client connection string (e.g. Endpoint=sb://{yournamespace}.servicebus.windows.net/;SharedAccessKeyName={policyname};SharedAccessKey={key};EntityPath={eventhubname}).
|
||||
/// - Parameter transportType: The connection transport type (currently only HTTPS is supported).
|
||||
/// - Returns: A new instance of IomtFhirClient configured for the given connection and transort type.
|
||||
/** - Throws: Errors thrown by CreateFromConnectionString
|
||||
|
||||
'InvalidConnectionString' The connection string is not formatted correctly or is missing a required value.
|
||||
|
||||
'UnsupportedTransportType' The TransportType is not supported.
|
||||
|
||||
*/
|
||||
public static func CreateFromConnectionString(connectionString: String) throws -> IomtFhirClient
|
||||
{
|
||||
// Instantiate a new connection string builder.
|
||||
let connectionStringBuilder = try EventHubsConnectionStringBuilder(connectionString: connectionString);
|
||||
|
||||
// Ensure the connection string is valid.
|
||||
try connectionStringBuilder.validate();
|
||||
|
||||
if connectionStringBuilder.transportType == .https
|
||||
{
|
||||
return try HttpIomtFhirClient(connectionStringBuilder: connectionStringBuilder);
|
||||
}
|
||||
|
||||
// Currently, only HTTPS is supported - throw here if another type was specified.
|
||||
throw IomtFhirClientError.unsupportedTransportType(transportType: connectionStringBuilder.transportType)
|
||||
}
|
||||
|
||||
internal init(connectionStringBuilder: EventHubsConnectionStringBuilder, tokenProvider: TokenProviderProtocol) throws
|
||||
{
|
||||
self.connectionStringBuilder = connectionStringBuilder
|
||||
self.eventHubName = connectionStringBuilder.entityPath
|
||||
|
||||
// Set the token provider if an instance has not already been provided.
|
||||
if self.tokenProvider == nil
|
||||
{
|
||||
self.tokenProvider = tokenProvider
|
||||
}
|
||||
}
|
||||
|
||||
public func send(eventData: EventData, completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
try send(eventDatas: [eventData], partitionKey: nil, completion: completion)
|
||||
}
|
||||
|
||||
public func send(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
try send(eventDatas: eventDatas, partitionKey: nil, completion: completion)
|
||||
}
|
||||
|
||||
public func send(eventDatas: [EventData], partitionKey: String?, completion: @escaping (Bool, Error?) -> Void) throws
|
||||
{
|
||||
try sender.onSend(eventDatas: eventDatas, completion: completion)
|
||||
}
|
||||
|
||||
internal func onCreateEventSender(partitionId: String? = nil) -> EventDataSenderProtocol
|
||||
{
|
||||
preconditionFailure("Method must be overridden by child class.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class DateFactory : DateFactoryProtocol
|
||||
{
|
||||
internal func now() -> Date
|
||||
{
|
||||
return Date()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol DateFactoryProtocol
|
||||
{
|
||||
func now() -> Date
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class EventHubsConnectionStringBuilder
|
||||
{
|
||||
private let keyValueSeparator = "="
|
||||
private let keyValuePairDelimeter = ";"
|
||||
|
||||
private let endpointConfigName = "Endpoint"
|
||||
private let sharedAccessKeyNameConfigName = "SharedAccessKeyName"
|
||||
private let sharedAccessKeyConfigName = "SharedAccessKey"
|
||||
private let entityPathConfigName = "EntityPath"
|
||||
private let operationTimeoutConfigName = "OperationTimeout"
|
||||
private let transportTypeConfigName = "TransportType"
|
||||
private let sharedAccessSignatureConfigName = "SharedAccessSignature"
|
||||
|
||||
public var endpoint: URL?
|
||||
|
||||
public var sasKey: String?
|
||||
|
||||
public var sasKeyName: String?
|
||||
|
||||
public var sharedAccessSignature: String?
|
||||
|
||||
public var entityPath: String?
|
||||
|
||||
public var operationTimeout: TimeInterval?
|
||||
|
||||
public var transportType = TransportType.https
|
||||
|
||||
public init(connectionString: String) throws
|
||||
{
|
||||
try parseConnectionString(connectionString: connectionString)
|
||||
try validate()
|
||||
}
|
||||
|
||||
public init(endpointAddress: URL, entityPath: String, sharedAccessKeyName: String, sharedAccessKey: String, operationTimeout: TimeInterval?) throws
|
||||
{
|
||||
self.endpoint = endpointAddress
|
||||
self.entityPath = entityPath
|
||||
self.sasKeyName = sharedAccessKeyName
|
||||
self.operationTimeout = operationTimeout
|
||||
try validate()
|
||||
}
|
||||
|
||||
public func validate() throws
|
||||
{
|
||||
if sharedAccessSignature != nil
|
||||
{
|
||||
if sasKeyName != nil,
|
||||
sasKey != nil
|
||||
{
|
||||
throw IomtFhirClientError.invalidConnectionString(reason: "'\(sharedAccessSignatureConfigName)' cannot be specified along with '\(sharedAccessKeyNameConfigName)' or '\(sharedAccessKeyConfigName)'. '\(sharedAccessSignatureConfigName)' alone should be sufficient to Authenticate the request.")
|
||||
}
|
||||
}
|
||||
|
||||
if sasKeyName != nil && sasKey == nil ||
|
||||
sasKeyName == nil && sasKey != nil
|
||||
{
|
||||
throw IomtFhirClientError.invalidConnectionString(reason: "Please make sure either all or none of the following arguments are defined: '\(sharedAccessKeyNameConfigName), \(sharedAccessKeyConfigName)'")
|
||||
}
|
||||
}
|
||||
|
||||
private func parseConnectionString(connectionString: String) throws
|
||||
{
|
||||
// Split the connection string into key value pairs using ';'.
|
||||
let keyValuePairs = connectionString.components(separatedBy: keyValuePairDelimeter)
|
||||
|
||||
var error: IomtFhirClientError?
|
||||
|
||||
// Loop through each key value pair.
|
||||
for keyValuePair in keyValuePairs
|
||||
{
|
||||
// Split the key value pair using the first '=' (The key may contain '=')
|
||||
if let index = keyValuePair.firstIndex(of: keyValueSeparator.first!)
|
||||
{
|
||||
let key = String(keyValuePair.prefix(upTo: index))
|
||||
let value = String(keyValuePair.suffix(from: keyValuePair.index(index, offsetBy: 1)))
|
||||
|
||||
if key.caseInsensitiveCompare(endpointConfigName) == .orderedSame
|
||||
{
|
||||
if let url = getEndpoint(uri: value, path: entityPath)
|
||||
{
|
||||
endpoint = url
|
||||
}
|
||||
else
|
||||
{
|
||||
error = IomtFhirClientError.invalidConnectionString(reason: "The '\(endpointConfigName)' parameter is invalid: '\(value)'")
|
||||
break
|
||||
}
|
||||
}
|
||||
else if key.caseInsensitiveCompare(entityPathConfigName) == .orderedSame
|
||||
{
|
||||
entityPath = value
|
||||
|
||||
if let uri = endpoint?.absoluteString
|
||||
{
|
||||
endpoint = getEndpoint(uri: uri, path: entityPath)
|
||||
}
|
||||
}
|
||||
else if key.caseInsensitiveCompare(sharedAccessKeyNameConfigName) == .orderedSame
|
||||
{
|
||||
sasKeyName = value
|
||||
}
|
||||
else if key.caseInsensitiveCompare(sharedAccessKeyConfigName) == .orderedSame
|
||||
{
|
||||
sasKey = value
|
||||
}
|
||||
else if key.caseInsensitiveCompare(sharedAccessSignatureConfigName) == .orderedSame
|
||||
{
|
||||
sharedAccessSignature = value
|
||||
}
|
||||
else if key.caseInsensitiveCompare(operationTimeoutConfigName) == .orderedSame,
|
||||
let doubleValue = Double(value)
|
||||
{
|
||||
operationTimeout = TimeInterval(doubleValue)
|
||||
}
|
||||
else if key.caseInsensitiveCompare(transportTypeConfigName) == .orderedSame
|
||||
{
|
||||
if let type = TransportType(rawValue: value.lowercased())
|
||||
{
|
||||
transportType = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = IomtFhirClientError.invalidConnectionString(reason: "The specified transport type is invalid '\(value)'.")
|
||||
break
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = IomtFhirClientError.invalidConnectionString(reason: "Illegal connection string parameter name '\(key)'")
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
error = IomtFhirClientError.invalidConnectionString(reason: "The connection string is not formatted correctly '\(connectionString)'.")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if error != nil
|
||||
{
|
||||
throw error!
|
||||
}
|
||||
}
|
||||
|
||||
private func getEndpoint(uri: String, path: String?) -> URL?
|
||||
{
|
||||
var uri = uri
|
||||
|
||||
if uri.hasPrefix("sb://")
|
||||
{
|
||||
uri = uri.replacingOccurrences(of: "sb://", with: "https://")
|
||||
}
|
||||
|
||||
if let path = path
|
||||
{
|
||||
if !uri.hasSuffix("/"),
|
||||
!path.hasPrefix("/")
|
||||
{
|
||||
uri.append("/")
|
||||
}
|
||||
|
||||
uri.append(path)
|
||||
uri.append("/messages")
|
||||
}
|
||||
|
||||
return URL(string: uri)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum EventHubsMessageError : Error
|
||||
{
|
||||
case serializationError(reason: String)
|
||||
case noEventData
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum EventHubsTokenError : Error
|
||||
{
|
||||
case invalidTokenString(tokenString: String)
|
||||
case missingTokenField(tokenField: String)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum IomtFhirClientError : Error
|
||||
{
|
||||
case invalidConnectionString(reason: String)
|
||||
case unsupportedTransportType(transportType: TransportType)
|
||||
case eventDataEmpty
|
||||
case unableToCreateRequest
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Provides information about a security token such as audience, expiry time, and the string token value.
|
||||
public class SecurityToken
|
||||
{
|
||||
|
||||
/// Gets the actual token.
|
||||
public let tokenValue: String
|
||||
|
||||
/// Gets the expiration time of this token.
|
||||
public let expiresAt: Date
|
||||
|
||||
/// Gets the audience of this token.
|
||||
public let audience: String
|
||||
|
||||
/// Gets the token type.
|
||||
public let tokenType: String
|
||||
|
||||
|
||||
/// Creates a new instance of the SecurityToken class.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - tokenString: The token
|
||||
/// - expiresAt: The expiration time
|
||||
/// - audience: The audience
|
||||
/// - tokenType: The type of token
|
||||
public init(tokenString: String, expiresAt: Date, audience: String, tokenType: String)
|
||||
{
|
||||
self.tokenValue = tokenString
|
||||
self.expiresAt = expiresAt
|
||||
self.audience = audience
|
||||
self.tokenType = tokenType
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class SharedAccessSignatureToken : SecurityToken
|
||||
{
|
||||
public static let sasTokenType = "servicebus.windows.net:sastoken";
|
||||
internal static let sharedAccessSignature = "SharedAccessSignature";
|
||||
internal static let signedResource = "sr";
|
||||
internal static let signature = "sig";
|
||||
internal static let signedKeyName = "skn";
|
||||
internal static let signedExpiry = "se";
|
||||
internal static let MaxKeyNameLength = 256;
|
||||
internal static let MaxKeyLength = 256;
|
||||
|
||||
internal static let sasPairSeparator = "&";
|
||||
internal static let sasKeyValueSeparator = "=";
|
||||
|
||||
public convenience init(tokenString: String) throws
|
||||
{
|
||||
let expiresAt = try SharedAccessSignatureToken.getExpirationDateFromToken(tokenString: tokenString)
|
||||
let audience = try SharedAccessSignatureToken.getAudienceFromToken(tokenString: tokenString)
|
||||
|
||||
self.init(tokenString: tokenString, expiresAt: expiresAt, audience: audience, tokenType: SharedAccessSignatureToken.sasTokenType );
|
||||
}
|
||||
|
||||
public static func validate(sharedAccessSignature: String) throws
|
||||
{
|
||||
let decodedToken = try decode(tokenString: sharedAccessSignature)
|
||||
|
||||
guard decodedToken[signedResource] != nil else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: signedResource)
|
||||
}
|
||||
|
||||
guard decodedToken[signature] != nil else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: signature)
|
||||
}
|
||||
|
||||
guard decodedToken[signedExpiry] != nil else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: signedExpiry)
|
||||
}
|
||||
|
||||
guard decodedToken[signedKeyName] != nil else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: signedKeyName)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getAudienceFromToken(tokenString: String) throws -> String
|
||||
{
|
||||
return try getTokenValue(tokenString: tokenString, field: signedResource)
|
||||
}
|
||||
|
||||
public static func getExpirationDateFromToken(tokenString: String) throws -> Date
|
||||
{
|
||||
let expiresOn = try getTokenValue(tokenString: tokenString, field: signedExpiry)
|
||||
|
||||
if let epochTime = TimeInterval(expiresOn)
|
||||
{
|
||||
return Date(timeIntervalSince1970: epochTime)
|
||||
}
|
||||
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: signedExpiry)
|
||||
}
|
||||
|
||||
public static func getKeyNameFromToken(tokenString: String) throws -> String
|
||||
{
|
||||
return try getTokenValue(tokenString: tokenString, field: signedKeyName)
|
||||
}
|
||||
|
||||
private static func getTokenValue(tokenString: String, field: String) throws -> String
|
||||
{
|
||||
let decodedToken = try decode(tokenString: tokenString)
|
||||
|
||||
if let value = decodedToken[field]
|
||||
{
|
||||
return value
|
||||
}
|
||||
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: field)
|
||||
}
|
||||
|
||||
private static func decode(tokenString: String) throws -> [String : String]
|
||||
{
|
||||
let prefix = "\(sharedAccessSignature) "
|
||||
|
||||
guard tokenString.hasPrefix(prefix) else
|
||||
{
|
||||
throw EventHubsTokenError.invalidTokenString(tokenString: tokenString)
|
||||
}
|
||||
|
||||
let tokenPairs = String(tokenString.dropFirst(prefix.count))
|
||||
|
||||
var result: [String : String] = [:]
|
||||
|
||||
let sasPairs = tokenPairs.components(separatedBy: sasPairSeparator)
|
||||
|
||||
for sasPair in sasPairs
|
||||
{
|
||||
if let index = sasPair.firstIndex(of: sasKeyValueSeparator.first!)
|
||||
{
|
||||
let key = String(sasPair.prefix(upTo: index))
|
||||
let value = String(sasPair.suffix(from: sasPair.index(index, offsetBy: 1)))
|
||||
|
||||
if let decodedValue = value.removingPercentEncoding
|
||||
{
|
||||
result[key] = decodedValue
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
throw EventHubsTokenError.invalidTokenString(tokenString: tokenString)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import CommonCrypto
|
||||
|
||||
internal class SharedAccessSignatureTokenProvider : TokenProviderProtocol
|
||||
{
|
||||
public var dateFactory: DateFactoryProtocol = DateFactory()
|
||||
|
||||
private var keyName: String
|
||||
private var sharedAccessKey: String?
|
||||
private var tokenScope: TokenScope
|
||||
private var tokenTimeToLive: TimeInterval
|
||||
private var sharedAccessSignature: String?
|
||||
|
||||
internal init(sharedAccessSignature: String) throws
|
||||
{
|
||||
// TODO: get scope from token string
|
||||
tokenScope = .entity
|
||||
try SharedAccessSignatureToken.validate(sharedAccessSignature: sharedAccessSignature)
|
||||
self.keyName = try SharedAccessSignatureToken.getKeyNameFromToken(tokenString: sharedAccessSignature)
|
||||
let expirationDate = try SharedAccessSignatureToken.getExpirationDateFromToken(tokenString: sharedAccessSignature)
|
||||
self.tokenTimeToLive = expirationDate.timeIntervalSince1970
|
||||
self.sharedAccessSignature = sharedAccessSignature
|
||||
}
|
||||
|
||||
internal init(keyName: String, sharedAccessKey: String, tokenTimeToLive: TimeInterval? = nil, tokenScope: TokenScope = TokenScope.entity)
|
||||
{
|
||||
self.keyName = keyName
|
||||
self.sharedAccessKey = sharedAccessKey
|
||||
self.tokenScope = tokenScope
|
||||
self.tokenTimeToLive = tokenTimeToLive ?? TimeInterval(integerLiteral: 3600) // Default token TTL is 60 minutes
|
||||
}
|
||||
|
||||
internal func getToken(appliesTo: URL, timeToLive: TimeInterval?) throws -> SecurityToken
|
||||
{
|
||||
if sharedAccessSignature != nil
|
||||
{
|
||||
return try SharedAccessSignatureToken(tokenString: sharedAccessSignature!)
|
||||
}
|
||||
|
||||
if let targetUri = appliesTo.appliesToUriString(tokenScope: tokenScope, ensureTrailingSlash: true)
|
||||
{
|
||||
let tokenString = try buildSignature(keyName: keyName, sharedAccessKey: sharedAccessKey!, targetUri: targetUri, timeToLive: timeToLive ?? tokenTimeToLive)
|
||||
return try SharedAccessSignatureToken(tokenString: tokenString)
|
||||
}
|
||||
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: SharedAccessSignatureToken.signedResource)
|
||||
}
|
||||
|
||||
private func buildSignature(keyName: String, sharedAccessKey: String, targetUri: String, timeToLive:TimeInterval) throws -> String
|
||||
{
|
||||
guard let audienceUri = targetUri.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: SharedAccessSignatureToken.signedResource)
|
||||
}
|
||||
|
||||
guard let encodedKeyName = keyName.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: SharedAccessSignatureToken.signedKeyName)
|
||||
}
|
||||
|
||||
let expiresOn = buildExpiresOn(timeToLive: timeToLive)
|
||||
let data = "\(audienceUri)\n\(expiresOn)".lowercased()
|
||||
let signature = buildSignature(key: sharedAccessKey, data: data)
|
||||
|
||||
guard let encodedSignature = signature.addingPercentEncoding(withAllowedCharacters: .alphanumerics) else
|
||||
{
|
||||
throw EventHubsTokenError.missingTokenField(tokenField: SharedAccessSignatureToken.signature)
|
||||
}
|
||||
|
||||
let pairSep = SharedAccessSignatureToken.sasPairSeparator
|
||||
let valSep = SharedAccessSignatureToken.sasKeyValueSeparator
|
||||
|
||||
return "\(SharedAccessSignatureToken.sharedAccessSignature) \(SharedAccessSignatureToken.signedResource)\(valSep)\(audienceUri.lowercased())\(pairSep)\(SharedAccessSignatureToken.signature)\(valSep)\(encodedSignature)\(pairSep)\(SharedAccessSignatureToken.signedExpiry)\(valSep)\(expiresOn)\(pairSep)\(SharedAccessSignatureToken.signedKeyName)\(valSep)\(encodedKeyName)"
|
||||
}
|
||||
|
||||
private func buildExpiresOn(timeToLive: TimeInterval) -> String
|
||||
{
|
||||
let now = dateFactory.now()
|
||||
let expiresOn = now.timeIntervalSince1970 + timeToLive
|
||||
|
||||
return String(describing: Int(expiresOn));
|
||||
}
|
||||
|
||||
private func buildSignature(key: String, data: String) -> String
|
||||
{
|
||||
let keyString = key.cString(using: .ascii)
|
||||
let keyLength = Int(key.lengthOfBytes(using: .ascii))
|
||||
|
||||
let dataString = data.cString(using: .ascii)
|
||||
let dataLength = Int(data.lengthOfBytes(using: .ascii))
|
||||
|
||||
let resultLength = Int(CC_SHA256_DIGEST_LENGTH)
|
||||
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: resultLength)
|
||||
|
||||
CCHmac(UInt32(kCCHmacAlgSHA256) ,keyString, keyLength, dataString, dataLength, result)
|
||||
|
||||
let data = NSData(bytes: result, length: resultLength)
|
||||
return data.base64EncodedString()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
/// Provides interface definition of a token provider.
|
||||
public protocol TokenProviderProtocol
|
||||
{
|
||||
|
||||
/// Gets a SecurityToken
|
||||
///
|
||||
/// - Parameter appliesTo: The URI which the access token applies to.
|
||||
/// - Parameter timeToLive: The duration (from now) the token is valid. Optional - a default TTL will be assigned if a value is not provided.
|
||||
/// - Returns: A new instance of SecurityToken
|
||||
/** - Throws: Errors thrown by getToken
|
||||
|
||||
'InvalidTokenString' The token string is not formatted correctly.
|
||||
|
||||
'MissingTokenField' A required field to construct the token is missing.
|
||||
|
||||
*/
|
||||
func getToken(appliesTo: URL, timeToLive: TimeInterval?) throws -> SecurityToken
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal enum TokenScope
|
||||
{
|
||||
case namespace
|
||||
case entity
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum TransportType : String
|
||||
{
|
||||
case https = "https"
|
||||
case amqps = "amqps"
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class EventHubsConnectionStringBuilderSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("EventHubsConnectionStringBuilder") {
|
||||
context("init is called") {
|
||||
context("with a string containing a SharedAccessSignature") {
|
||||
it("does not throw an error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "SharedAccessSignature=TESTSAS") }.toNot(throwError())
|
||||
}
|
||||
context("and a SharedAccessKeyName") {
|
||||
it("throws the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "SharedAccessSignature=TESTSAS;SharedAccessKeyName=TESTKEYNAME") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "Please make sure either all or none of the following arguments are defined: \'SharedAccessKeyName, SharedAccessKey\'")))
|
||||
}
|
||||
}
|
||||
context("and a SharedAccessKey") {
|
||||
it("throws the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "SharedAccessSignature=TESTSAS;SharedAccessKey=TESTKEY") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "Please make sure either all or none of the following arguments are defined: \'SharedAccessKeyName, SharedAccessKey\'")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with a string containing a SharedAccessKeyName") {
|
||||
it("throws the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "SharedAccessKeyName=TESTKEYNAME") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "Please make sure either all or none of the following arguments are defined: \'SharedAccessKeyName, SharedAccessKey\'")))
|
||||
}
|
||||
context("and a SharedAccessKey") {
|
||||
it("does not throw an error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTKEY") }.toNot(throwError())
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with a string containing an EntityPath") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "EntityPath=TESTPATH")
|
||||
it("sets the entityPath property") {
|
||||
expect(builder.entityPath) == "TESTPATH"
|
||||
}
|
||||
}
|
||||
context("with a string containing an Endpoint") {
|
||||
context("with a valid url") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "Endpoint=sb://test.servicebus.windows.net/")
|
||||
it("sets the endpoint property") {
|
||||
expect(builder.endpoint?.absoluteString) == "https://test.servicebus.windows.net/"
|
||||
}
|
||||
context("and an EntityPath") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "Endpoint=sb://test.servicebus.windows.net/;EntityPath=TESTPATH")
|
||||
it("sets the endpoint property") {
|
||||
expect(builder.endpoint?.absoluteString) == "https://test.servicebus.windows.net/TESTPATH/messages"
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with an invalid url") {
|
||||
it("throws with the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "Endpoint=\\") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "The \'Endpoint\' parameter is invalid: \'\\\'")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with a string containing an OperationTimeout") {
|
||||
context("with a numerical value") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "OperationTimeout=30")
|
||||
it("sets the operationTimeout property") {
|
||||
expect(builder.operationTimeout) == 30
|
||||
}
|
||||
}
|
||||
context("with a non-numerical value") {
|
||||
it("throws with the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "OperationTimeout=Thirty") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "Illegal connection string parameter name \'OperationTimeout\'")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with a string containing a TransportType") {
|
||||
context("https") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "TransportType=https")
|
||||
it("sets the transportType property") {
|
||||
expect(builder.transportType) == .https
|
||||
}
|
||||
}
|
||||
context("amqps") {
|
||||
let builder = try! EventHubsConnectionStringBuilder(connectionString: "TransportType=amqps")
|
||||
it("sets the transportType property") {
|
||||
expect(builder.transportType) == .amqps
|
||||
}
|
||||
}
|
||||
context("unsupported transport type") {
|
||||
it("throws with the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "TransportType=mqtt") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "The specified transport type is invalid \'mqtt\'.")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with an invalid connection string") {
|
||||
it("throws with the expected error") {
|
||||
expect { try EventHubsConnectionStringBuilder(connectionString: "INVALID_CONNECTION_STRING") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "The connection string is not formatted correctly \'INVALID_CONNECTION_STRING\'.")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class HttpEventDataSenderSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("HttpEventDataSender") {
|
||||
context("validateEvents is called") {
|
||||
context("with an array containing at least one message") {
|
||||
it("returns the expected count") {
|
||||
expect(try! HttpEventDataSender.validateEvents(eventDatas: self.eventData(count: 1))) == 1
|
||||
}
|
||||
}
|
||||
context("with an empty array") {
|
||||
it("throws the expected error") {
|
||||
expect { try HttpEventDataSender.validateEvents(eventDatas: self.eventData(count: 0)) }.to(throwError(IomtFhirClientError.eventDataEmpty))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("send is called") {
|
||||
context("with one message") {
|
||||
context("the message converter fails to convert the event data") {
|
||||
let test = testObjects()
|
||||
let eventData = EventData(data: Data())
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.send(eventDatas: [eventData], completion: { _, _ in }) }.to(throwError(EventHubsMessageError.serializationError(reason: "")))
|
||||
}
|
||||
}
|
||||
context("creating the connection fails") {
|
||||
let test = testObjects()
|
||||
test.connectionManager.getOrCreateReturns.append(MockError.getOrCreateThrow)
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.send(eventDatas: self.eventData(count: 1), completion: { _, _ in }) }.to(throwError(MockError.getOrCreateThrow))
|
||||
}
|
||||
}
|
||||
context("connection send fails") {
|
||||
let test = testObjects()
|
||||
let connection = try! MockHttpEventHubsConnection(iomtFhirClient: test.client, partitionId: nil)
|
||||
connection.sendCompletions.append((false, MockError.onSendError))
|
||||
test.connectionManager.getOrCreateReturns.append(connection)
|
||||
waitUntil { completed in
|
||||
try! test.sender.send(eventDatas: self.eventData(count: 1), completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("completes with the expected error") {
|
||||
expect(error).to(matchError(MockError.onSendError))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("connection send succeeds") {
|
||||
let test = testObjects()
|
||||
let connection = try! MockHttpEventHubsConnection(iomtFhirClient: test.client, partitionId: nil)
|
||||
connection.sendCompletions.append((true, nil))
|
||||
test.connectionManager.getOrCreateReturns.append(connection)
|
||||
waitUntil { completed in
|
||||
try! test.sender.send(eventDatas: self.eventData(count: 1), completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("completes with no error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls the connection with the expected message") {
|
||||
let message = connection.sendParams.first
|
||||
expect(message?.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
expect(String(data:message!.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"}]"
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with an empty array") {
|
||||
let test = testObjects()
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.send(eventDatas: [], completion: { _, _ in }) }.to(throwError(IomtFhirClientError.eventDataEmpty))
|
||||
}
|
||||
}
|
||||
context("with multiple messages") {
|
||||
context("connection send succeeds") {
|
||||
let test = testObjects()
|
||||
let connection = try! MockHttpEventHubsConnection(iomtFhirClient: test.client, partitionId: nil)
|
||||
connection.sendCompletions.append((true, nil))
|
||||
test.connectionManager.getOrCreateReturns.append(connection)
|
||||
waitUntil { completed in
|
||||
try! test.sender.send(eventDatas: self.eventData(count: 4), completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("completes with no error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls the connection with the expected message") {
|
||||
let message = connection.sendParams.first
|
||||
expect(message?.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
expect(String(data:message!.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"},{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"},{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"},{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"}]"
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context("onSend is called") {
|
||||
context("with one message") {
|
||||
context("the message converter fails to convert the event data") {
|
||||
let test = testObjects()
|
||||
let eventData = EventData(data: Data())
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.onSend(eventDatas: [eventData], completion: { _, _ in }) }.to(throwError(EventHubsMessageError.serializationError(reason: "")))
|
||||
}
|
||||
}
|
||||
context("creating the connection fails") {
|
||||
let test = testObjects()
|
||||
test.connectionManager.getOrCreateReturns.append(MockError.getOrCreateThrow)
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.onSend(eventDatas: self.eventData(count: 1), completion: { _, _ in }) }.to(throwError(MockError.getOrCreateThrow))
|
||||
}
|
||||
}
|
||||
context("connection send fails") {
|
||||
let test = testObjects()
|
||||
let connection = try! MockHttpEventHubsConnection(iomtFhirClient: test.client, partitionId: nil)
|
||||
connection.sendCompletions.append((false, MockError.onSendError))
|
||||
test.connectionManager.getOrCreateReturns.append(connection)
|
||||
waitUntil { completed in
|
||||
try! test.sender.onSend(eventDatas: self.eventData(count: 1), completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("completes with the expected error") {
|
||||
expect(error).to(matchError(MockError.onSendError))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("connection send succeeds") {
|
||||
let test = testObjects()
|
||||
let connection = try! MockHttpEventHubsConnection(iomtFhirClient: test.client, partitionId: nil)
|
||||
connection.sendCompletions.append((true, nil))
|
||||
test.connectionManager.getOrCreateReturns.append(connection)
|
||||
waitUntil { completed in
|
||||
try! test.sender.onSend(eventDatas: self.eventData(count: 1), completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("completes with no error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls the connection with the expected message") {
|
||||
let message = connection.sendParams.first
|
||||
expect(message?.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
expect(String(data:message!.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"}]"
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with an empty array") {
|
||||
let test = testObjects()
|
||||
it("throws the expected error") {
|
||||
expect { try test.sender.onSend(eventDatas: [], completion: { _, _ in }) }.to(throwError(IomtFhirClientError.eventDataEmpty))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func testObjects() -> (connectionManager: MockConnectionManager, client: HttpIomtFhirClient, sender: HttpEventDataSender) {
|
||||
let mockConnectionManager = MockConnectionManager()
|
||||
let client = try! IomtFhirClient.CreateFromConnectionString(connectionString: "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTTOKEN;EntityPath=TESTPATH")
|
||||
client.connectionManager = mockConnectionManager
|
||||
let sender = HttpEventDataSender(iomtFhirClient: client)
|
||||
return (mockConnectionManager, client as! HttpIomtFhirClient, sender)
|
||||
}
|
||||
|
||||
private func eventData(count: Int) -> [EventData] {
|
||||
var eventData = [EventData]()
|
||||
for _ in 0..<count {
|
||||
let data = "{\"valid\":\"json\"}".data(using: .utf8)!
|
||||
eventData.append(EventData(data: data))
|
||||
}
|
||||
return eventData
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class HttpEventHubsConnectionSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("HttpEventHubsConnection") {
|
||||
context("send is called") {
|
||||
context("the token provider throws") {
|
||||
let test = testObjects()
|
||||
test.tokenProvider.getTokenReturns.append(MockError.getTokenThrows)
|
||||
it("throws the expected error") {
|
||||
expect { try test.connection.send(httpMessage: HttpMessage(httpBody: Data(), contentType: "TEST_TYPE"), completion: nil) }.to(throwError(MockError.getTokenThrows))
|
||||
}
|
||||
}
|
||||
context("the upload task fails with an error") {
|
||||
let test = testObjects()
|
||||
test.tokenProvider.getTokenReturns.append(SecurityToken(tokenString: "value", expiresAt: Date(), audience: "audience", tokenType: "testToken"))
|
||||
test.urlSession.uploadTaskCompletions.append((nil, error: MockError.uploadTaskError))
|
||||
waitUntil { completed in
|
||||
try! test.connection.send(httpMessage: HttpMessage(httpBody: Data(), contentType: "TEST_TYPE"), completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("completes with the expected error") {
|
||||
expect(error).to(matchError(MockError.uploadTaskError))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the upload task fails with a 500") {
|
||||
let test = testObjects()
|
||||
test.tokenProvider.getTokenReturns.append(SecurityToken(tokenString: "value", expiresAt: Date(), audience: "audience", tokenType: "testToken"))
|
||||
let response = HTTPURLResponse(url: URL(string: "https://test.com")!, statusCode: 500, httpVersion: nil, headerFields: nil)
|
||||
test.urlSession.uploadTaskCompletions.append((response, error: nil))
|
||||
waitUntil { completed in
|
||||
try! test.connection.send(httpMessage: HttpMessage(httpBody: Data(), contentType: "TEST_TYPE"), completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("completes with no error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the upload task is successful") {
|
||||
let test = testObjects()
|
||||
test.tokenProvider.getTokenReturns.append(SecurityToken(tokenString: "tokenString", expiresAt: Date(), audience: "audience", tokenType: "testToken"))
|
||||
let response = HTTPURLResponse(url: URL(string: "https://test.com")!, statusCode: 201, httpVersion: nil, headerFields: nil)
|
||||
test.urlSession.uploadTaskCompletions.append((response, error: nil))
|
||||
let expectedData = Data(base64Encoded: "TEST")!
|
||||
waitUntil { completed in
|
||||
try! test.connection.send(httpMessage: HttpMessage(httpBody: expectedData, contentType: "testContentType"), completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("completes with no error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls upload with the expected endpoint"){
|
||||
expect(test.urlSession.uploadTaskParams.first?.request.url?.absoluteString) == "https://test.servicebus.windows.net/TESTPATH/messages"
|
||||
}
|
||||
it("calls upload with the expected method"){
|
||||
expect(test.urlSession.uploadTaskParams.first?.request.httpMethod) == "POST"
|
||||
}
|
||||
it("calls upload with the expected Host header") {
|
||||
expect(test.urlSession.uploadTaskParams.first?.request.allHTTPHeaderFields?["Host"]) == "test.servicebus.windows.net"
|
||||
}
|
||||
it("calls upload with the expected Content-Type header") {
|
||||
expect(test.urlSession.uploadTaskParams.first?.request.allHTTPHeaderFields?["Content-Type"]) == "testContentType"
|
||||
}
|
||||
it("calls upload with the expected Authorization header") {
|
||||
expect(test.urlSession.uploadTaskParams.first?.request.allHTTPHeaderFields?["Authorization"]) == "tokenString"
|
||||
}
|
||||
it("calls upload with the expected http body data") {
|
||||
expect(test.urlSession.uploadTaskParams.first?.bodyData).to(equal(expectedData))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func testObjects() -> (client: IomtFhirClient, tokenProvider: MockTokenProvider, urlSession: MockURLSession, connection: HttpEventHubsConnection) {
|
||||
let client = try! IomtFhirClient.CreateFromConnectionString(connectionString: "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTTOKEN;EntityPath=TESTPATH")
|
||||
let tokenProvider = MockTokenProvider()
|
||||
client.tokenProvider = tokenProvider
|
||||
let connection = try! HttpEventHubsConnection(iomtFhirClient: client, partitionId: nil)
|
||||
let urlSession = MockURLSession()
|
||||
connection.session = urlSession
|
||||
return (client, tokenProvider, urlSession, connection)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class HttpMessageConverterSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("HttpMessageConverter") {
|
||||
context("EventDatasToHttpMessage is called") {
|
||||
context("with an empty array") {
|
||||
it("throws the expected error") {
|
||||
expect { try HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [], partitionId: nil) }.to(throwError(EventHubsMessageError.noEventData))
|
||||
}
|
||||
}
|
||||
context("with an empty event") {
|
||||
it("throws the expected error") {
|
||||
expect { return try HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [EventData(data: Data())], partitionId: nil) }.to(throwError(EventHubsMessageError.serializationError(reason: "One or more event data could not be serialized.")))
|
||||
}
|
||||
}
|
||||
context("with an event that contains invalid json") {
|
||||
let eventData = EventData(data: "{\"valid\":\"json\"}".data(using: .utf8)!)
|
||||
eventData.properties = ["user" : NSData()]
|
||||
it("throws the expected error") {
|
||||
expect { return try HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [eventData], partitionId: nil) }.to(throwError(EventHubsMessageError.serializationError(reason: "The event data could not be serialized into valid JSON.")))
|
||||
}
|
||||
}
|
||||
context("with a single event") {
|
||||
let message = try! HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [EventData(data: "{\"valid\":\"json\"}".data(using: .utf8)!)], partitionId: nil)
|
||||
it("creates a message with the expected http body") {
|
||||
expect(String(data: message.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"}]"
|
||||
}
|
||||
it("creates a message with the expected content type") {
|
||||
expect(message.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
}
|
||||
}
|
||||
context("with an event that has user properties") {
|
||||
let eventData = EventData(data: "{\"valid\":\"json\"}".data(using: .utf8)!)
|
||||
eventData.properties = ["user" : "property"]
|
||||
let message = try! HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [eventData], partitionId: nil)
|
||||
it("creates a message with the expected http body") {
|
||||
expect(String(data: message.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"valid\\\":\\\"json\\\"}\"},{\"UserProperties\":{\"user\":\"property\"}}]"
|
||||
}
|
||||
it("creates a message with the expected content type") {
|
||||
expect(message.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
}
|
||||
}
|
||||
context("with a multiple events") {
|
||||
let eventData1 = EventData(data: "{\"one\":\"1\"}".data(using: .utf8)!)
|
||||
let eventData2 = EventData(data: "{\"two\":\"2\"}".data(using: .utf8)!)
|
||||
let eventData3 = EventData(data: "{\"three\":\"3\"}".data(using: .utf8)!)
|
||||
let message = try! HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [eventData1, eventData2, eventData3], partitionId: nil)
|
||||
it("creates a message with the expected http body") {
|
||||
expect(String(data: message.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"one\\\":\\\"1\\\"}\"},{\"Body\":\"{\\\"two\\\":\\\"2\\\"}\"},{\"Body\":\"{\\\"three\\\":\\\"3\\\"}\"}]"
|
||||
}
|
||||
it("creates a message with the expected content type") {
|
||||
expect(message.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
}
|
||||
}
|
||||
context("with a multiple events with user properties") {
|
||||
let eventData1 = EventData(data: "{\"one\":\"1\"}".data(using: .utf8)!)
|
||||
eventData1.properties = ["user1" : "property1"]
|
||||
let eventData2 = EventData(data: "{\"two\":\"2\"}".data(using: .utf8)!)
|
||||
eventData2.properties = ["user2" : "property2"]
|
||||
let eventData3 = EventData(data: "{\"three\":\"3\"}".data(using: .utf8)!)
|
||||
eventData3.properties = ["user3" : "property3"]
|
||||
let message = try! HttpMessageConverter.EventDatasToHttpMessage(eventDatas: [eventData1, eventData2, eventData3], partitionId: nil)
|
||||
it("creates a message with the expected http body") {
|
||||
expect(String(data: message.httpBody, encoding: .utf8)) == "[{\"Body\":\"{\\\"one\\\":\\\"1\\\"}\"},{\"UserProperties\":{\"user1\":\"property1\"}},{\"Body\":\"{\\\"two\\\":\\\"2\\\"}\"},{\"UserProperties\":{\"user2\":\"property2\"}},{\"Body\":\"{\\\"three\\\":\\\"3\\\"}\"},{\"UserProperties\":{\"user3\":\"property3\"}}]"
|
||||
}
|
||||
it("creates a message with the expected content type") {
|
||||
expect(message.contentType) == "application/vnd.microsoft.servicebus.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class IomtFhirClientSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("IomtFhirClient") {
|
||||
context("CreateFromConnectionString is called") {
|
||||
context("with an invalid connection string") {
|
||||
it("throws the expected error") {
|
||||
expect { _ = try IomtFhirClient.CreateFromConnectionString(connectionString: "INVALID_STRING") }.to(throwError(IomtFhirClientError.invalidConnectionString(reason: "The connection string is not formatted correctly \'INVALID_STRING\'.")))
|
||||
}
|
||||
}
|
||||
context("with a connection string using an unsupported transport type") {
|
||||
it("throws the expected error") {
|
||||
let connectionString = "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTTOKEN;EntityPath=TESTPATH;TransportType=amqps"
|
||||
expect { _ = try IomtFhirClient.CreateFromConnectionString(connectionString: connectionString) }.to(throwError(IomtFhirClientError.unsupportedTransportType(transportType: TransportType.amqps)))
|
||||
}
|
||||
}
|
||||
context("with a valid connection string") {
|
||||
context("using http transport") {
|
||||
let client = try! IomtFhirClient.CreateFromConnectionString(connectionString: "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTTOKEN;EntityPath=TESTPATH")
|
||||
it("creates a client of the expected type") {
|
||||
expect(client).to(beAKindOf(HttpIomtFhirClient.self))
|
||||
}
|
||||
it("creates a token provider of the expected type") {
|
||||
expect(client.tokenProvider).to(beAKindOf(SharedAccessSignatureTokenProvider.self))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context("send event data is called") {
|
||||
context("the call to the sender is successful") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((nil, true, nil))
|
||||
let eventData = EventData(data: Data())
|
||||
waitUntil { completed in
|
||||
try! test.client.send(eventData: eventData, completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("does not return an error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls the sender with the event data") {
|
||||
expect(test.sender.onSendParams.count) == 1
|
||||
expect(test.sender.onSendParams[0][0]).to(be(eventData))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the call to the sender fails") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((nil, false, MockError.onSendError))
|
||||
let eventData = EventData(data: Data())
|
||||
waitUntil { completed in
|
||||
try! test.client.send(eventData: eventData, completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("returns the expected error") {
|
||||
expect(error).to(matchError(MockError.onSendError))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the call to the sender throws") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((MockError.onSendThrow, true, nil))
|
||||
let eventData = EventData(data: Data())
|
||||
expect { try test.client.send(eventData: eventData, completion: { (success, error) in }) }.to(throwError(MockError.onSendThrow))
|
||||
}
|
||||
}
|
||||
context("send event datas is called") {
|
||||
context("the call to the sender is successful") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((nil, true, nil))
|
||||
let eventData1 = EventData(data: Data())
|
||||
let eventData2 = EventData(data: Data())
|
||||
let eventData3 = EventData(data: Data())
|
||||
waitUntil { completed in
|
||||
try! test.client.send(eventDatas: [eventData1, eventData2, eventData3], completion: { (success, error) in
|
||||
it("completes successfully") {
|
||||
expect(success).to(beTrue())
|
||||
}
|
||||
it("does not return an error") {
|
||||
expect(error).to(beNil())
|
||||
}
|
||||
it("calls the sender with the event data objects") {
|
||||
expect(test.sender.onSendParams.count) == 1
|
||||
expect(test.sender.onSendParams[0].count) == 3
|
||||
expect(test.sender.onSendParams[0]).to(containElementSatisfying({ (eventData) -> Bool in
|
||||
for data in [eventData1, eventData2, eventData3] {
|
||||
if data.data == eventData.data {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the call to the sender fails") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((nil, false, MockError.onSendError))
|
||||
let eventData1 = EventData(data: Data())
|
||||
let eventData2 = EventData(data: Data())
|
||||
let eventData3 = EventData(data: Data())
|
||||
waitUntil { completed in
|
||||
try! test.client.send(eventDatas: [eventData1, eventData2, eventData3], completion: { (success, error) in
|
||||
it("completes unsuccessfully") {
|
||||
expect(success).to(beFalse())
|
||||
}
|
||||
it("returns the expected error") {
|
||||
expect(error).to(matchError(MockError.onSendError))
|
||||
}
|
||||
completed()
|
||||
})
|
||||
}
|
||||
}
|
||||
context("the call to the sender throws") {
|
||||
let test = testObjects()
|
||||
test.sender.onSendCompletions.append((MockError.onSendThrow, true, nil))
|
||||
let eventData1 = EventData(data: Data())
|
||||
let eventData2 = EventData(data: Data())
|
||||
let eventData3 = EventData(data: Data())
|
||||
expect { try test.client.send(eventDatas: [eventData1, eventData2, eventData3], completion: { (success, error) in }) }.to(throwError(MockError.onSendThrow))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func testObjects() -> (sender: MockDataSender, client: IomtFhirClient) {
|
||||
let sender = MockDataSender()
|
||||
let client = try! IomtFhirClient.CreateFromConnectionString(connectionString: "Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=TESTKEYNAME;SharedAccessKey=TESTTOKEN;EntityPath=TESTPATH")
|
||||
client.sender = sender
|
||||
return (sender, client)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockConnectionManager : ConnectionManagerProtocol {
|
||||
public var getOrCreateParams = [(iomtFhirClient: IomtFhirClient, partitionId: String?)]()
|
||||
public var getOrCreateReturns = [Any]()
|
||||
|
||||
public func getOrCreate<T>(iomtFhirClient: IomtFhirClient, partitionId: String?) throws -> T where T : EventHubsConnection {
|
||||
getOrCreateParams.append((iomtFhirClient, partitionId))
|
||||
let ret = getOrCreateReturns.removeFirst()
|
||||
if let error = ret as? Error {
|
||||
throw error
|
||||
}
|
||||
return ret as! T
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockDataSender : EventDataSenderProtocol {
|
||||
public var sendParams = [[EventData]]()
|
||||
public var sendCompletions = [(throw: Error?, success: Bool, error: Error?)]()
|
||||
public var onSendParams = [[EventData]]()
|
||||
public var onSendCompletions = [(throw: Error?, success: Bool, error: Error?)]()
|
||||
|
||||
public func send(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws {
|
||||
sendParams.append(eventDatas)
|
||||
let comp = sendCompletions.removeFirst()
|
||||
if let error = comp.throw {
|
||||
throw error
|
||||
}
|
||||
completion(comp.success, comp.error)
|
||||
}
|
||||
|
||||
public func onSend(eventDatas: [EventData], completion: @escaping (Bool, Error?) -> Void) throws {
|
||||
onSendParams.append(eventDatas)
|
||||
let comp = onSendCompletions.removeFirst()
|
||||
if let error = comp.throw {
|
||||
throw error
|
||||
}
|
||||
completion(comp.success, comp.error)
|
||||
}
|
||||
|
||||
public static func validateEvents(eventDatas: [EventData]) throws -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockDateFactory : DateFactoryProtocol {
|
||||
public var nowReturns = [Date]()
|
||||
|
||||
public func now() -> Date {
|
||||
return nowReturns.removeFirst()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum MockError : Error {
|
||||
case onSendError
|
||||
case onSendThrow
|
||||
case getOrCreateThrow
|
||||
case getTokenThrows
|
||||
case uploadTaskError
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
internal class MockHttpEventHubsConnection : HttpEventHubsConnection {
|
||||
public var sendParams = [HttpMessage]()
|
||||
public var sendCompletions = [(success: Bool, error: Error?)]()
|
||||
|
||||
open override func send(httpMessage: HttpMessage, completion: ((Bool, Error?) -> Void)?) throws {
|
||||
sendParams.append(httpMessage)
|
||||
let comp = sendCompletions.removeFirst()
|
||||
completion?(comp.success, comp.error)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockTokenProvider : TokenProviderProtocol {
|
||||
public var getTokenParams = [(appliesTo: URL, timeToLive: TimeInterval?)]()
|
||||
public var getTokenReturns = [Any]()
|
||||
|
||||
public func getToken(appliesTo: URL, timeToLive: TimeInterval?) throws -> SecurityToken {
|
||||
getTokenParams.append((appliesTo, timeToLive))
|
||||
let ret = getTokenReturns.removeFirst()
|
||||
if let error = ret as? Error {
|
||||
throw error
|
||||
}
|
||||
return ret as! SecurityToken
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockURLSession : URLSession {
|
||||
public var uploadTaskParams = [(request: URLRequest, bodyData: Data?)]()
|
||||
public var uploadTaskCompletions = [(response: URLResponse?, error: Error?)]()
|
||||
|
||||
open override func uploadTask(with request: URLRequest, from bodyData: Data?, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
|
||||
uploadTaskParams.append((request, bodyData))
|
||||
let comp = uploadTaskCompletions.removeFirst()
|
||||
completionHandler(nil, comp.response, comp.error)
|
||||
return MockURLSessionUploadTask()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MockURLSessionUploadTask : URLSessionUploadTask {
|
||||
|
||||
open override func resume() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class SharedAccessSignatureTokenProviderSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("SharedAccessSignatureTokenProvider") {
|
||||
context("init with shared access signature is called") {
|
||||
context("with an invalid shared access signature") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureTokenProvider(sharedAccessSignature: "INVALID_TOKEN") }.to(throwError(EventHubsTokenError.invalidTokenString(tokenString: "INVALID_TOKEN")))
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signed key name") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&sig=signature&se=0") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "skn")))
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signed expiry") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&sig=signature&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "se")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context ("is initialized with key name and shared access key") {
|
||||
var provider = SharedAccessSignatureTokenProvider(keyName: "keyName", sharedAccessKey: "sharedAccessKey")
|
||||
context("getToken is called") {
|
||||
context("with a valid url") {
|
||||
let dateFactory = MockDateFactory()
|
||||
provider.dateFactory = dateFactory
|
||||
let date = Date(timeIntervalSince1970: TimeInterval(1567000000))
|
||||
dateFactory.nowReturns.append(date)
|
||||
let token = try? provider.getToken(appliesTo: URL(string: "https://test.com/test")!, timeToLive: nil)
|
||||
it ("provides a token with the expected tokenValue") {
|
||||
expect(token?.tokenValue) == "SharedAccessSignature sr=test.com%2ftest%2f&sig=5YJxMk3FHCFoymb4CCeRv9AMfrFen3XV0353eiIyw5I%3D&se=1567003600&skn=keyName"
|
||||
}
|
||||
it ("provides a token with the expected tokenType") {
|
||||
expect(token?.tokenType) == "servicebus.windows.net:sastoken"
|
||||
}
|
||||
it ("provides a token with the expected expiresAt") {
|
||||
expect(token?.expiresAt).to(equal(date.addingTimeInterval(TimeInterval(3600))))
|
||||
}
|
||||
it ("provides a token with the expected audience") {
|
||||
expect(token?.audience) == "test.com/test/"
|
||||
}
|
||||
}
|
||||
context ("providing a timeToLive parameter") {
|
||||
provider = SharedAccessSignatureTokenProvider(keyName: "keyName", sharedAccessKey: "sharedAccessKey")
|
||||
let dateFactory = MockDateFactory()
|
||||
provider.dateFactory = dateFactory
|
||||
let date = Date(timeIntervalSince1970: TimeInterval(1567000000))
|
||||
dateFactory.nowReturns.append(date)
|
||||
let token = try? provider.getToken(appliesTo: URL(string: "https://test.com/test")!, timeToLive: TimeInterval(2400))
|
||||
it ("provides a token with the expected expiresAt") {
|
||||
expect(token?.expiresAt).to(equal(date.addingTimeInterval(TimeInterval(2400))))
|
||||
}
|
||||
}
|
||||
}
|
||||
context ("with the tokenTimeToLive parameter not the default value") {
|
||||
context("getToken is called") {
|
||||
provider = SharedAccessSignatureTokenProvider(keyName: "keyName", sharedAccessKey: "sharedAccessKey", tokenTimeToLive: TimeInterval(4800))
|
||||
let dateFactory = MockDateFactory()
|
||||
provider.dateFactory = dateFactory
|
||||
let date = Date(timeIntervalSince1970: TimeInterval(1567000000))
|
||||
dateFactory.nowReturns.append(date)
|
||||
let token = try? provider.getToken(appliesTo: URL(string: "https://test.com/test")!, timeToLive: nil)
|
||||
it ("provides a token with the expected expiresAt") {
|
||||
expect(token?.expiresAt).to(equal(date.addingTimeInterval(TimeInterval(4800))))
|
||||
}
|
||||
}
|
||||
}
|
||||
context ("with the tokenScope parameter not the default value") {
|
||||
context("getToken is called") {
|
||||
provider = SharedAccessSignatureTokenProvider(keyName: "keyName", sharedAccessKey: "sharedAccessKey", tokenTimeToLive: nil, tokenScope: .namespace)
|
||||
let dateFactory = MockDateFactory()
|
||||
provider.dateFactory = dateFactory
|
||||
let date = Date(timeIntervalSince1970: TimeInterval(1567000000))
|
||||
dateFactory.nowReturns.append(date)
|
||||
let token = try? provider.getToken(appliesTo: URL(string: "https://test.com/test")!, timeToLive: nil)
|
||||
it ("provides a token with the expected audience") {
|
||||
expect(token?.audience) == "test.com/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Foundation
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
class SharedAccessSignatureTokenSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("SharedAccessSignatureToken") {
|
||||
context("init is called") {
|
||||
context("with an invalid token string") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken(tokenString: "INVALID_TOKEN") }.to(throwError(EventHubsTokenError.invalidTokenString(tokenString: "INVALID_TOKEN")))
|
||||
}
|
||||
}
|
||||
context("with a valid token string") {
|
||||
var token: SharedAccessSignatureToken?
|
||||
it("does not throw an error") {
|
||||
expect { token = try SharedAccessSignatureToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName") }.toNot(throwError())
|
||||
}
|
||||
it("sets the tokenValue property") {
|
||||
expect(token?.tokenValue) == "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName"
|
||||
}
|
||||
it("sets the expiresAt property") {
|
||||
expect(token?.expiresAt).to(equal(Date.init(timeIntervalSince1970: 0)))
|
||||
}
|
||||
it("sets the audience property") {
|
||||
expect(token?.audience) == "signedResource"
|
||||
}
|
||||
it("sets the tokenType property") {
|
||||
expect(token?.tokenType) == "servicebus.windows.net:sastoken"
|
||||
}
|
||||
}
|
||||
}
|
||||
context("validate is called") {
|
||||
context("with a valid shared access signature") {
|
||||
it("does not throw an error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName") }.toNot(throwError())
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signed resource") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sig=signature&se=0&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "sr")))
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signature") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&se=0&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "sig")))
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signed expiry") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&sig=signature&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "se")))
|
||||
}
|
||||
}
|
||||
context("with a shared access signature missing the signed key name") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.validate(sharedAccessSignature: "SharedAccessSignature sr=signedResource&sig=signature&se=0") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "skn")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("getAudienceFromToken is called") {
|
||||
context("with a valid token string") {
|
||||
it("returns the expected audience") {
|
||||
expect { return try SharedAccessSignatureToken.getAudienceFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName") }.to(equal("signedResource"))
|
||||
}
|
||||
}
|
||||
context("with a token string missing the signed resource") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.getAudienceFromToken(tokenString: "SharedAccessSignature sig=signature&se=0skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "sr")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("getExpirationDateFromToken is called") {
|
||||
context("with a valid token string") {
|
||||
it("returns the expected date") {
|
||||
expect { return try SharedAccessSignatureToken.getExpirationDateFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName") }.to(equal(Date.init(timeIntervalSince1970: 0)))
|
||||
}
|
||||
}
|
||||
context("with a token string missing the signed expiry") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.getExpirationDateFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "se")))
|
||||
}
|
||||
}
|
||||
context("with a token string where the signed expiry value is not numerical") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.getExpirationDateFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=Zero&skn=signedKeyName") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "se")))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("getKeyNameFromToken is called") {
|
||||
context("with a valid token string") {
|
||||
it("returns the expected signed key name") {
|
||||
expect { return try SharedAccessSignatureToken.getKeyNameFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=0&skn=signedKeyName") }.to(equal("signedKeyName"))
|
||||
}
|
||||
}
|
||||
context("with a token string missing the signed key name") {
|
||||
it("throws the expected error") {
|
||||
expect { try SharedAccessSignatureToken.getKeyNameFromToken(tokenString: "SharedAccessSignature sr=signedResource&sig=signature&se=0") }.to(throwError(EventHubsTokenError.missingTokenField(tokenField: "skn")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
import Foundation
|
||||
|
||||
class UrlExtensionsSpec: QuickSpec {
|
||||
override func spec() {
|
||||
describe("UrlExtensions") {
|
||||
let url = URL(string: "https://test.com/entity")
|
||||
context("appliesToUriString is called") {
|
||||
context("with an entity token scope") {
|
||||
context("ensuring a trailng slash") {
|
||||
it("returns the expected uri string") {
|
||||
expect(url?.appliesToUriString(tokenScope: .entity, ensureTrailingSlash: true)) == "test.com/entity/"
|
||||
}
|
||||
}
|
||||
context("not ensuring a trailng slash") {
|
||||
it("returns the expected uri string") {
|
||||
expect(url?.appliesToUriString(tokenScope: .entity, ensureTrailingSlash: false)) == "test.com/entity"
|
||||
}
|
||||
}
|
||||
}
|
||||
context("with a namespace token scope") {
|
||||
context("ensuring a trailng slash") {
|
||||
it("returns the expected uri string") {
|
||||
expect(url?.appliesToUriString(tokenScope: .namespace, ensureTrailingSlash: true)) == "test.com/"
|
||||
}
|
||||
}
|
||||
context("not ensuring a trailng slash") {
|
||||
it("returns the expected uri string") {
|
||||
expect(url?.appliesToUriString(tokenScope: .namespace, ensureTrailingSlash: false)) == "test.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче