Skip to content

Commit

Permalink
keep the original URL when decoding Download/ExternalLocationReference (
Browse files Browse the repository at this point in the history
#621)

rdar://110536969
  • Loading branch information
QuietMisdreavus authored Jun 12, 2023
1 parent 55f84fa commit cd87793
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public struct ExternalLocationReference: RenderReference, URLReference {

public private(set) var type: RenderReferenceType = .externalLocation

public var identifier: RenderReferenceIdentifier
public let identifier: RenderReferenceIdentifier

let url: String

enum CodingKeys: String, CodingKey {
case type
Expand All @@ -34,21 +36,21 @@ public struct ExternalLocationReference: RenderReference, URLReference {

public init(identifier: RenderReferenceIdentifier) {
self.identifier = identifier
self.url = identifier.identifier
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.identifier = try container.decode(RenderReferenceIdentifier.self, forKey: .identifier)
self.url = try container.decode(String.self, forKey: .url)
self.type = try container.decode(RenderReferenceType.self, forKey: .type)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type.rawValue, forKey: .type)
try container.encode(identifier, forKey: .identifier)

// Enter the given URL verbatim into the Render JSON
try container.encode(identifier.identifier, forKey: .url)
try container.encode(url, forKey: .url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ public struct DownloadReference: RenderReference, URLReference, Equatable {

/// The location of the downloadable resource.
public var url: URL


/// Indicates whether the ``url`` property was loaded from the regular initializer or from the
/// `Decodable` initializer.
///
/// This is used during encoding to determine whether to filter ``url`` through the
/// `renderURL(for:)` method. In case the URL was loaded from JSON, we don't want to modify it
/// further after a round-trip.
private var urlWasDecoded = false

/// The SHA512 hash value for the resource.
public var checksum: String?

Expand Down Expand Up @@ -60,15 +68,41 @@ public struct DownloadReference: RenderReference, URLReference, Equatable {
public init(identifier: RenderReferenceIdentifier, renderURL url: URL, sha512Checksum: String) {
self.init(identifier: identifier, renderURL: url, checksum: sha512Checksum)
}


enum CodingKeys: CodingKey {
case type
case identifier
case url
case checksum
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.type = try container.decode(RenderReferenceType.self, forKey: .type)
self.identifier = try container.decode(RenderReferenceIdentifier.self, forKey: .identifier)
self.url = try container.decode(URL.self, forKey: .url)
self.urlWasDecoded = true
self.checksum = try container.decodeIfPresent(String.self, forKey: .checksum)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type.rawValue, forKey: .type)
try container.encode(identifier, forKey: .identifier)
try container.encodeIfPresent(checksum, forKey: .checksum)

// Render URL
try container.encode(renderURL(for: url), forKey: .url)
if !urlWasDecoded {
try container.encode(renderURL(for: url), forKey: .url)
} else {
try container.encode(url, forKey: .url)
}
}

static public func ==(lhs: DownloadReference, rhs: DownloadReference) -> Bool {
lhs.identifier == rhs.identifier
&& lhs.url == rhs.url
&& lhs.checksum == rhs.checksum
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"schemaVersion" : {
"major" : 1,
"minor" : 0,
"patch" : 0
},
"seeAlsoSections" : [ ],
"metadata" : {
"platforms" : [
{
"name" : "macOS",
"introducedAt" : "10.15"
}
],
"modules" : [
{ "name" : "MyKit" }
],
"title" : "Wifi Access",
"roleHeading" : "Plist Key"
},
"abstract" : [
{
"type" : "text",
"text" : "A "
},
{
"type" : "codeVoice",
"code" : "WiFi access"
},
{
"type" : "text",
"text" : " abstract description."
}
],
"sections" : [
],
"identifier" : {
"url" : "doc:\/\/org.swift.docc.example\/plist\/wifiaccess",
"interfaceLanguage": "swift"
},
"hierarchy" : {
"paths" : [["doc:\/\/org.swift.docc.example\/plist\/wifiaccess"]]
},
"topicSections" : [
],
"kind" : "symbol",
"references" : {
"doc:\/\/org.swift.docc.example\/downloads\/sample.zip": {
"identifier": "ExternalLocation.zip",
"url": "https://example.com/ExternalLocation.zip",
"type": "externalLocation"
},
"doc:\/\/org.swift.docc.example\/plist\/wifiaccess": {
"abstract" : [
{
"text" : "WiFi access",
"type" : "text"
}
],
"identifier" : "doc:\/\/org.swift.docc.example\/plist\/wifiaccess",
"kind" : "symbol",
"title" : "WiFi Access",
"type" : "topic",
"url" : "\/documentation\/mykit"
}
},
"sampleCodeDownload": {
"action": {
"identifier": "doc:\/\/org.swift.docc.example\/downloads\/sample.zip",
"isActive": true,
"overridingTitle": "Download",
"type": "reference"
}
},
"primaryContentSections" : [
{
"kind" : "content",
"content" : [
{
"anchor" : "discussion",
"level" : 2,
"type" : "heading",
"text" : "Discussion"
},
{
"type" : "paragraph",
"inlineContent" : [
{
"type" : "text",
"text" : "Use "
},
{
"type" : "codeVoice",
"code" : "Wifi access"
},
{
"type" : "text",
"text" : " to secure wifi access for your app."
}
]
}
]
}
],
"variants": [{
"paths" : ["\/plist\/wifiaccess"],
"traits" : []
}]
}

51 changes: 51 additions & 0 deletions Tests/SwiftDocCTests/Rendering/SampleDownloadTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,55 @@ class SampleDownloadTests: XCTestCase {
let reference = try XCTUnwrap(renderNode.references[identifier.identifier])
XCTAssert(reference is ExternalLocationReference)
}

/// Ensure that a DownloadReference where the URL is different from the reference identifier
/// can still round-trip through an ExternalLocationReference with the URL and reference identifier intact.
func testRoundTripWithDifferentUrl() throws {
let baseReference = DownloadReference(identifier: .init("DownloadReference.zip"), renderURL: .init(string: "https://example.com/DownloadReference.zip")!, checksum: nil)

let encoder = JSONEncoder()
let decoder = JSONDecoder()

let encodedReference = try encoder.encode(baseReference)

let interimReference = try decoder.decode(ExternalLocationReference.self, from: encodedReference)
let interimEncodedReference = try encoder.encode(interimReference)

let roundTripReference = try decoder.decode(DownloadReference.self, from: interimEncodedReference)

XCTAssertEqual(baseReference, roundTripReference)
}

/// Ensure that an ExternalLocationReference loaded from JSON continues to encode the same
/// information after being decoded and re-encoded.
func testRoundTripExternalLocationFromFixture() throws {
let downloadSymbolURL = Bundle.module.url(
forResource: "external-location-custom-url", withExtension: "json",
subdirectory: "Rendering Fixtures")!

let originalData = try Data(contentsOf: downloadSymbolURL)
let originalRenderNode = try RenderNode.decode(fromJSON: originalData)

let encodedRenderNode = try JSONEncoder().encode(originalRenderNode)
let symbol = try RenderNode.decode(fromJSON: encodedRenderNode)

//
// Sample Download Details
//

guard let section = symbol.sampleDownload else {
XCTFail("Download section not decoded")
return
}

guard case RenderInlineContent.reference(let identifier, _, _, _) = section.action else {
XCTFail("Could not decode action reference")
return
}

XCTAssertEqual(identifier.identifier, "doc://org.swift.docc.example/downloads/sample.zip")

let externalReference = try XCTUnwrap(symbol.references[identifier.identifier] as? ExternalLocationReference)
XCTAssertEqual(externalReference.url, "https://example.com/ExternalLocation.zip")
}
}

0 comments on commit cd87793

Please sign in to comment.