This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement tbDEX protocol parsing test vectors
- Loading branch information
Showing
20 changed files
with
494 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "Tests/tbDEXTestVectors/tbdex-spec"] | ||
path = Tests/tbDEXTestVectors/tbdex-spec | ||
url = https://github.com/TBD54566975/tbdex |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,12 @@ | ||
bootstrap: | ||
# Initialize submodules | ||
git submodule update --init | ||
# Initialize sparse checkout in the `tbdex-spec` submodule | ||
git -C Tests/tbDEXTestVectors/tbdex-spec config core.sparseCheckout true | ||
# Sparse checkout only the `hosted/test-vectors` directory from `tbdex-spec` | ||
git -C Tests/tbDEXTestVectors/tbdex-spec sparse-checkout set hosted/test-vectors | ||
# Update submodules so they sparse checkout takes effect | ||
git submodule update | ||
|
||
format: | ||
swift format --in-place --recursive . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import Foundation | ||
|
||
/// A date formatter that can be used to encode and decode dates in the ISO8601 format, | ||
/// compatible with the larger tbDEX ecosystem. | ||
let tbDEXDateFormatter: ISO8601DateFormatter = { | ||
let dateFormatter = ISO8601DateFormatter() | ||
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] | ||
return dateFormatter | ||
}() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import Foundation | ||
|
||
public class tbDEXJSONDecoder: JSONDecoder { | ||
|
||
public override init() { | ||
super.init() | ||
|
||
dateDecodingStrategy = .custom { decoder in | ||
let container = try decoder.singleValueContainer() | ||
let dateString = try container.decode(String.self) | ||
|
||
if let date = tbDEXDateFormatter.date(from: dateString) { | ||
return date | ||
} else { | ||
throw DecodingError.dataCorruptedError( | ||
in: container, | ||
debugDescription: "Invalid date: \(dateString)" | ||
) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Foundation | ||
|
||
public class tbDEXJSONEncoder: JSONEncoder { | ||
|
||
public override init() { | ||
super.init() | ||
|
||
outputFormatting = .sortedKeys | ||
dateEncodingStrategy = .custom { date, encoder in | ||
var container = encoder.singleValueContainer() | ||
try container.encode(tbDEXDateFormatter.string(from: date)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import AnyCodable | ||
import Foundation | ||
|
||
/// Enumeration that can represent any `Message` type. | ||
/// | ||
/// `AnyMessage` should be used in contexts when given a `Message`, but the exact type | ||
/// of the `Message` is unknown until runtime. | ||
/// | ||
/// Example: When calling an endpoint that returns `Message`s, but it's impossible to know exactly | ||
/// what kind of `Message` it is until the JSON response is parsed. | ||
public enum AnyMessage { | ||
case close(Close) | ||
case order(Order) | ||
case orderStatus(OrderStatus) | ||
case quote(Quote) | ||
case rfq(RFQ) | ||
|
||
/// Parse a JSON string into an `AnyMessage` object, which can represent any message type. | ||
/// - Parameter jsonString: A string containing a JSON representation of a `Message` | ||
/// - Returns: An `AnyMessage` object, representing the parsed JSON string | ||
public static func parse(_ jsonString: String) throws -> AnyMessage { | ||
guard let data = jsonString.data(using: .utf8) else { | ||
throw Error.invalidJSONString | ||
} | ||
|
||
return try tbDEXJSONDecoder().decode(AnyMessage.self, from: data) | ||
} | ||
} | ||
|
||
// MARK: - Decodable | ||
|
||
extension AnyMessage: Decodable { | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
|
||
// Read the JSON payload into a dictionary representation | ||
let messageJSONObject = try container.decode([String: AnyCodable].self) | ||
|
||
// Ensure that a metadata object is present within the JSON payload | ||
guard let metadataJSONObject = messageJSONObject["metadata"]?.value as? [String: Any] else { | ||
throw DecodingError.valueNotFound( | ||
AnyMessage.self, | ||
DecodingError.Context( | ||
codingPath: decoder.codingPath, | ||
debugDescription: "metadata not found" | ||
) | ||
) | ||
} | ||
|
||
// Decode the metadata into a strongly-typed `MessageMetadata` object | ||
let metadataData = try JSONSerialization.data(withJSONObject: metadataJSONObject) | ||
let metadata = try tbDEXJSONDecoder().decode(MessageMetadata.self, from: metadataData) | ||
|
||
// Decode the message itself into it's strongly-typed representation, indicated by the `metadata.kind` field | ||
switch metadata.kind { | ||
case .close: | ||
self = .close(try container.decode(Close.self)) | ||
case .order: | ||
self = .order(try container.decode(Order.self)) | ||
case .orderStatus: | ||
self = .orderStatus(try container.decode(OrderStatus.self)) | ||
case .quote: | ||
self = .quote(try container.decode(Quote.self)) | ||
case .rfq: | ||
self = .rfq(try container.decode(RFQ.self)) | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Errors | ||
|
||
extension AnyMessage { | ||
|
||
public enum Error: Swift.Error { | ||
/// The provided JSON string is invalid | ||
case invalidJSONString | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import AnyCodable | ||
import Foundation | ||
|
||
/// Enumeration that can represent any `Resource` type. | ||
/// | ||
/// `AnyResource` should be used in contexts when given a `Resource`, but the exact type | ||
/// of the `Resource` is unknown until runtime. | ||
/// | ||
/// Example: When calling an endpoint that returns `Resource`s, but it's impossible to know exactly | ||
/// what kind of `Resource` it is until the JSON response is parsed. | ||
public enum AnyResource { | ||
case offering(Offering) | ||
|
||
public static func parse(_ jsonString: String) throws -> AnyResource { | ||
guard let data = jsonString.data(using: .utf8) else { | ||
throw Error.invalidJSONString | ||
} | ||
|
||
return try tbDEXJSONDecoder().decode(AnyResource.self, from: data) | ||
} | ||
} | ||
|
||
// MARK: - Decodable | ||
|
||
extension AnyResource: Decodable { | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
|
||
// Read the JSON payload into a dictionary representation | ||
let resourceJSONObject = try container.decode([String: AnyCodable].self) | ||
|
||
// Ensure that a metadata object is present within the JSON payload | ||
guard let metadataJSONObject = resourceJSONObject["metadata"]?.value as? [String: Any] else { | ||
throw DecodingError.valueNotFound( | ||
AnyResource.self, | ||
DecodingError.Context( | ||
codingPath: decoder.codingPath, | ||
debugDescription: "metadata not found" | ||
) | ||
) | ||
} | ||
|
||
// Decode the metadata into a strongly-typed `ResourceMetadata` object | ||
let metadataData = try JSONSerialization.data(withJSONObject: metadataJSONObject) | ||
let metadata = try tbDEXJSONDecoder().decode(ResourceMetadata.self, from: metadataData) | ||
|
||
// Decode the resource itself into it's strongly-typed representation, indicated by the `metadata.kind` field | ||
switch metadata.kind { | ||
case .offering: | ||
self = .offering(try container.decode(Offering.self)) | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Errors | ||
|
||
extension AnyResource { | ||
|
||
enum Error: Swift.Error { | ||
/// The provided JSON string is invalid | ||
case invalidJSONString | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.