Skip to content

Commit

Permalink
Make the ISO8601 date transcoder configurable (#94)
Browse files Browse the repository at this point in the history
Make the ISO8601 date transcoder configurable

### Motivation

Fixes apple/swift-openapi-generator#389.

### Modifications

Add a way to configure the existing `ISO8601DateTranscoder`.

### Result

Adopters can more easily use e.g. fractional seconds-based transcoder.

### Test Plan

Added unit tests.


Reviewed by: simonjbeaumont

Builds:
     ✔︎ pull request validation (5.10) - Build finished. 
     ✔︎ pull request validation (5.9) - Build finished. 
     ✔︎ pull request validation (5.9.0) - Build finished. 
     ✔︎ pull request validation (api breakage) - Build finished. 
     ✔︎ pull request validation (docc test) - Build finished. 
     ✔︎ pull request validation (integration test) - Build finished. 
     ✔︎ pull request validation (nightly) - Build finished. 
     ✔︎ pull request validation (soundness) - Build finished. 

#94
  • Loading branch information
czechboy0 authored Jan 16, 2024
1 parent 6be221f commit 7f86e4a
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
34 changes: 31 additions & 3 deletions Sources/OpenAPIRuntime/Conversion/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,37 @@ public protocol DateTranscoder: Sendable {
}

/// A transcoder for dates encoded as an ISO-8601 string (in RFC 3339 format).
public struct ISO8601DateTranscoder: DateTranscoder {
public struct ISO8601DateTranscoder: DateTranscoder, @unchecked Sendable {

/// The lock protecting the formatter.
private let lock: NSLock

/// The underlying date formatter.
private let locked_formatter: ISO8601DateFormatter

/// Creates a new transcoder with the provided options.
/// - Parameter options: Options to override the default ones. If you provide nil here, the default options
/// are used.
public init(options: ISO8601DateFormatter.Options? = nil) {
let formatter = ISO8601DateFormatter()
if let options { formatter.formatOptions = options }
lock = NSLock()
lock.name = "com.apple.swift-openapi-generator.runtime.ISO8601DateTranscoder"
locked_formatter = formatter
}

/// Creates and returns an ISO 8601 formatted string representation of the specified date.
public func encode(_ date: Date) throws -> String { ISO8601DateFormatter().string(from: date) }
public func encode(_ date: Date) throws -> String {
lock.lock()
defer { lock.unlock() }
return locked_formatter.string(from: date)
}

/// Creates and returns a date object from the specified ISO 8601 formatted string representation.
public func decode(_ dateString: String) throws -> Date {
guard let date = ISO8601DateFormatter().date(from: dateString) else {
lock.lock()
defer { lock.unlock() }
guard let date = locked_formatter.date(from: dateString) else {
throw DecodingError.dataCorrupted(
.init(codingPath: [], debugDescription: "Expected date string to be ISO8601-formatted.")
)
Expand All @@ -44,6 +67,11 @@ public struct ISO8601DateTranscoder: DateTranscoder {
extension DateTranscoder where Self == ISO8601DateTranscoder {
/// A transcoder that transcodes dates as ISO-8601–formatted string (in RFC 3339 format).
public static var iso8601: Self { ISO8601DateTranscoder() }

/// A transcoder that transcodes dates as ISO-8601–formatted string (in RFC 3339 format) with fractional seconds.
public static var iso8601WithFractionalSeconds: Self {
ISO8601DateTranscoder(options: [.withInternetDateTime, .withFractionalSeconds])
}
}

extension JSONEncoder.DateEncodingStrategy {
Expand Down
30 changes: 30 additions & 0 deletions Tests/OpenAPIRuntimeTests/Conversion/Test_Configuration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftOpenAPIGenerator open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import XCTest
@_spi(Generated) import OpenAPIRuntime

final class Test_Configuration: Test_Runtime {

func testDateTranscoder_iso8601() throws {
let transcoder: any DateTranscoder = .iso8601
XCTAssertEqual(try transcoder.encode(testDate), testDateString)
XCTAssertEqual(testDate, try transcoder.decode(testDateString))
}

func testDateTranscoder_iso8601WithFractionalSeconds() throws {
let transcoder: any DateTranscoder = .iso8601WithFractionalSeconds
XCTAssertEqual(try transcoder.encode(testDateWithFractionalSeconds), testDateWithFractionalSecondsString)
XCTAssertEqual(testDateWithFractionalSeconds, try transcoder.decode(testDateWithFractionalSecondsString))
}
}
4 changes: 4 additions & 0 deletions Tests/OpenAPIRuntimeTests/Test_Runtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class Test_Runtime: XCTestCase {

var testDateString: String { "2023-01-18T10:04:11Z" }

var testDateWithFractionalSeconds: Date { Date(timeIntervalSince1970: 1_674_036_251.123) }

var testDateWithFractionalSecondsString: String { "2023-01-18T10:04:11.123Z" }

var testDateEscapedString: String { "2023-01-18T10%3A04%3A11Z" }

var testDateStringData: Data { Data(testDateString.utf8) }
Expand Down

0 comments on commit 7f86e4a

Please sign in to comment.