Skip to content

corrects schema version property name in RenderIndex.spec.json #1224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ let package = Package(
dependencies: [
.target(name: "SwiftDocC"),
.target(name: "SwiftDocCTestUtilities"),
.product(name: "_OpenAPIGeneratorCore", package: "swift-openapi-generator")
],
resources: [
.copy("Test Resources"),
Expand Down Expand Up @@ -140,6 +141,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
.package(url: "https://github.com/swiftlang/swift-docc-symbolkit.git", branch: "main"),
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
.package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.2.0"),
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.8.0"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: If we add this dependency, it—and all its transient dependencies—will be used when building DocC for toolchains builds. This significantly increases the amount of work necessary to merge this PR because we need to check if anything else in the toolchain depend on any version of these dependencies and ensure that those versions are compatible, now and in the future. If we can skip this dependency it would make it easier to merge this PR. Otherwise someone will write access to various Swift repositories will need to spend some time making toolchain builds to verify that these new dependencies don't break the toolchain builds.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I was afraid of that.

Since the project chose to provide OpenAPI schema files as a description, what would be the best way to verify them? The dependency isn't the only problem - going from them as resources to something you can write a test to verify is another awkward part here.

I wanted to explore what might be possible in testing, and this PR reflects a baseline of it, but it adds a huge amount of overhead, as you're mentioning.

]
} else {
// Building in the Swift.org CI system, so rely on local versions of dependencies.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"interfaceLanguages"
],
"properties": {
"identifier": {
"schemaVersion": {
"$ref": "#/components/schemas/SchemaVersion"
},
"interfaceLanguages": {
Expand Down
26 changes: 16 additions & 10 deletions Sources/SwiftDocC/Utility/ValidatedURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,23 +179,29 @@ private extension StringProtocol {
func addingPercentEncodingIfNeeded(withAllowedCharacters allowedCharacters: CharacterSet) -> String? {
var needsPercentEncoding: Bool {
for (index, character) in unicodeScalars.indexed() where !allowedCharacters.contains(character) {
// Check if the character "%" represents a percent encoded URL.
// Any other disallowed character is an indication that this substring needs percent encoding.
if character == "%" {
// % isn't allowed in a URL fragment but it is also the escape character for percent encoding.
let firstFollowingIndex = unicodeScalars.index(after: index)
let secondFollowingIndex = unicodeScalars.index(after: firstFollowingIndex)

guard secondFollowingIndex < unicodeScalars.endIndex else {
guard self.distance(from: index, to: self.endIndex) >= 2 else {
// There's not two characters after the "%". This "%" can't represent a percent encoded character.
return true
}
// If either of the two following characters aren't hex digits, the "%" doesn't represent a
return !Character(unicodeScalars[firstFollowingIndex]).isHexDigit
|| !Character(unicodeScalars[secondFollowingIndex]).isHexDigit
let firstFollowingIndex = self.index(after: index)
let secondFollowingIndex = self.index(after: firstFollowingIndex)

} else {
// Any other disallowed character is an indication that this substring needs percent encoding.
return true
// Check if the next two characthers represent a percent encoded
// URL.
// If either of the two following characters aren't hex digits,
// the "%" doesn't represent a percent encoded character.
if Character(unicodeScalars[firstFollowingIndex]).isHexDigit,
Character(unicodeScalars[secondFollowingIndex]).isHexDigit
{
// Later characters in the string might require percentage encoding.
continue
}
}
return true
}
return false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation
import XCTest
import _OpenAPIGeneratorCore
import OpenAPIKit
@testable import SwiftDocC

class ArchiveModelSpecValidationTests: XCTestCase {

struct ThrowingDiagnosticCollector: DiagnosticCollector, Sendable {
public init() {}
public func emit(_ diagnostic: _OpenAPIGeneratorCore.Diagnostic) throws {
if diagnostic.severity == .warning || diagnostic.severity == .error {
throw diagnostic
}
}
}

func testOpenAPISpecIsValid() throws {
let renderIndexspecURL = Bundle.module.url(
forResource: "RenderIndex.spec", withExtension: "json", subdirectory: "Test Resources")!
let data = try Data(contentsOf: renderIndexspecURL)

let diagCollector = ThrowingDiagnosticCollector()

let _ = try _OpenAPIGeneratorCore.runGenerator(input: .init(absolutePath: renderIndexspecURL, contents: data), config: Config(
mode: .types,
access: Config.defaultAccessModifier,
namingStrategy: Config.defaultNamingStrategy
), diagnostics: diagCollector)
}
}
199 changes: 199 additions & 0 deletions Tests/SwiftDocCTests/Test Resources/RenderIndex.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
{
"openapi": "3.0.0",
"info": {
"description": "Specification of the Swift-DocC Index.json file.",
"version": "0.1.2",
"title": "RenderIndex"
},
"paths": {},
"components": {
"schemas": {
"RenderIndex": {
"type": "object",
"required": [
"identifier",
"interfaceLanguages",
],
"properties": {
"schemaVersion": {
"$ref": "#/components/schemas/SchemaVersion"
},
"interfaceLanguages": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Node"
}
}
},
"references": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/ImageRenderReference"
}
},
"includedArchiveIdentifiers": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"Node": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"article",
"associatedtype",
"buildSetting",
"case",
"collection",
"class",
"container",
"dictionarySymbol",
"enum",
"extension",
"func",
"groupMarker",
"httpRequest",
"init",
"languageGroup",
"learn",
"macro",
"method",
"module",
"op",
"overview",
"project",
"property",
"propertyListKey",
"propertyListKeyReference",
"protocol",
"resources",
"root",
"sampleCode",
"section",
"struct",
"subscript",
"symbol",
"typealias",
"union",
"var"
]
},
"path": {
"type": "string"
},
"deprecated": {
"type": "boolean",
"default": "false"
},
"external": {
"type": "boolean",
"default": "false"
},
"beta": {
"type": "boolean",
"default": "false"
},
"icon": {
"type": "string",
"format": "reference(ImageRenderReference)"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Node"
}
}
}
},
"SchemaVersion": {
"type": "object",
"required": [
"major",
"minor",
"patch"
],
"properties": {
"major": {
"type": "integer"
},
"minor": {
"type": "integer"
},
"patch": {
"type": "integer"
}
}
},
"ImageRenderReference": {
"type": "object",
"required": [
"type",
"identifier",
"variants"
],
"properties": {
"type": {
"type": "string",
"enum": ["image"]
},
"alt": {
"type": "string",
"nullable": true
},
"identifier": {
"type": "string"
},
"variants": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RenderReferenceVariant"
}
}
}
},
"RenderReferenceVariant": {
"type": "object",
"required": [
"url",
"traits"
],
"properties": {
"url": {
"type": "string"
},
"svgID": {
"type": "string",
"description": "The ID attribute for the image that should be rendered in the SVG file represented by this variant."
},
"traits": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RenderReferenceVariantTrait"
}
}
}
},
"RenderReferenceVariantTrait": {
"type": "string",
"enum": ["1x", "2x", "3x", "light", "dark"]
}
},
"requestBodies": {},
"securitySchemes": {},
"links": {},
"callbacks": {}
}
}
Loading