Skip to content

Commit

Permalink
Merge pull request #6 from mattpolzin/openapikit-3
Browse files Browse the repository at this point in the history
Support OpenAPIKit 3.0.0
  • Loading branch information
mattpolzin authored Nov 6, 2023
2 parents c92c687 + 30105ea commit d2ac032
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 115 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ on: [pull_request]
jobs:
codecov:
container:
image: swift:5.3-bionic
image: swift:5.8-focal
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: swift test --enable-test-discovery --enable-code-coverage
- uses: mattpolzin/[email protected].3
- uses: mattpolzin/[email protected].5
31 changes: 12 additions & 19 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,27 @@ jobs:
fail-fast: false
matrix:
image:
- swift:5.2-xenial
- swift:5.2-bionic
- swift:5.2-focal
- swift:5.2-centos8
- swift:5.2-amazonlinux2
- swift:5.3-xenial
- swift:5.3-bionic
- swift:5.3-focal
- swift:5.3-centos8
- swift:5.3-amazonlinux2
- swift:5.5-xenial
- swift:5.5-bionic
- swift:5.5-focal
- swift:5.5-centos8
- swift:5.5-amazonlinux2
- swift:5.8-bionic
- swift:5.8-focal
- swift:5.8-jammy
- swift:5.8-amazonlinux2
- swift:5.9-focal
- swift:5.9-jammy
- swift:5.9-amazonlinux2
container: ${{ matrix.image }}
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run tests
run: swift test --enable-test-discovery
run: swift test
osx:
runs-on: macOS-latest
runs-on: macOS-13
steps:
- name: Select latest available Xcode
uses: maxim-lobanov/setup-xcode@v1
with: { 'xcode-version': 'latest' }
with:
xcode-version: 'latest'
- name: Checkout code
uses: actions/checkout@v3
- name: Run tests
run: swift test --enable-test-discovery
run: swift test
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM swift:5.2 as build
FROM swift:5.9 as build

WORKDIR /build

Expand All @@ -12,7 +12,7 @@ RUN swift build -c release --enable-test-discovery

# ----------------

FROM swift:5.2-slim
FROM swift:5.9-slim

WORKDIR /api

Expand Down
87 changes: 29 additions & 58 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,61 +1,32 @@
{
"object": {
"pins": [
{
"package": "FineJSON",
"repositoryURL": "https://github.com/omochi/FineJSON.git",
"state": {
"branch": null,
"revision": "05101709243cb66d80c92e645210a3b80cf4e17f",
"version": "1.14.0"
}
},
{
"package": "OpenAPIKit",
"repositoryURL": "https://github.com/mattpolzin/OpenAPIKit.git",
"state": {
"branch": null,
"revision": "dea99e8bb5d8241c5c9f815d0fb0ad487b9491b0",
"version": "2.0.0"
}
},
{
"package": "pure-swift-json",
"repositoryURL": "https://github.com/fabianfett/pure-swift-json.git",
"state": {
"branch": null,
"revision": "5dc8ec1d857f3b56e3a718a01165ae80c7677d48",
"version": "0.4.0"
}
},
{
"package": "RichJSONParser",
"repositoryURL": "https://github.com/omochi/RichJSONParser.git",
"state": {
"branch": null,
"revision": "263e2ecfe88d0500fa99e4cbc8c948529d335534",
"version": "3.0.0"
}
},
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser.git",
"state": {
"branch": null,
"revision": "92646c0cdbaca076c8d3d0207891785b3379cbff",
"version": "0.3.1"
}
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "88caa2e6fffdbef2e91c2022d038576062042907",
"version": "4.0.0"
}
"pins" : [
{
"identity" : "openapikit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mattpolzin/OpenAPIKit.git",
"state" : {
"revision" : "ae98338a8e660ae547b058ebb69c010e70b64e31",
"version" : "3.0.0"
}
]
},
"version": 1
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.3"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3",
"version" : "5.0.6"
}
}
],
"version" : 2
}
13 changes: 6 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.8

import PackageDescription

Expand All @@ -9,10 +9,9 @@ let package = Package(
.executable(name: "openapi-diff", targets: ["openapi-diff"])
],
dependencies: [
.package(url: "https://github.com/mattpolzin/OpenAPIKit.git", from: "2.0.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "4.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/fabianfett/pure-swift-json.git", .upToNextMinor(from: "0.4.0"))
.package(url: "https://github.com/mattpolzin/OpenAPIKit.git", from: "3.0.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"),
],
targets: [
.target(
Expand All @@ -21,12 +20,12 @@ let package = Package(
.testTarget(
name: "OpenAPIDiffTests",
dependencies: ["OpenAPIDiff"]),
.target(
.executableTarget(
name: "openapi-diff",
dependencies: [
"OpenAPIDiff",
.product(name: "OpenAPIKitCompat", package: "OpenAPIKit"),
.product(name: "Yams", package: "Yams"),
.product(name: "PureSwiftJSON", package: "pure-swift-json"),
.product(name: "ArgumentParser", package: "swift-argument-parser")
]
)
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

This is a WIP diffing library for OpenAPI documentation. It produces a hierarchical list of changes between two versions of an API. Only OpenAPI v3.x is supported.

By default, it uses the Foundation JSONDecoder which battle-tested but notably slow on Linux. You can save a lot of time by passing the `--fast` flag or running diffs on YAML files if the Yams parser does an adequate job of parsing your OpenAPI document.

## Example
To see an example of what this library produces when run against two YAML OpenAPI documents with the markdown option, take a look at the `example` folder.

Expand All @@ -18,8 +16,6 @@ docker run --rm -v "$(pwd)/old.json:/api/old.json" -v "$(pwd)/new.json:/api/new.

You can pass `mattpolzin2/openapi-diff` the `--markdown` flag in addition to the two API document files to produce a markdown diff instead of the default plaintext diff.

You can pass `mattpolzin2/openapi-diff` the `--fast` flag to use faster (though less battle-tested) JSON parsing. The flag currently has no effect on YAML parsing.

For all options, see the `--help`.

### Library
Expand All @@ -28,7 +24,7 @@ As a library, you first reference this package from your manifest or pull it in
```swift
...
dependencies: [
.package(url: "https://github.com/mattpolzin/OpenAPIDiff.git", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/mattpolzin/OpenAPIDiff.git", .upToNextMinor(from: "0.7.0")),
...
],
...
Expand Down Expand Up @@ -59,6 +55,8 @@ let markdownDifferences = comparison.markdownDescription { !$0.isSame }

The structure you are producing is `ApiDiff`. You can take a look at `ApiDiff.swift` if you want to operate on the diff in some way different than the `description()` and `markdownDescription()` methods do.

If you need to diff an OAS 3.0 document, you can decode it and then convert it to an OAS 3.1 document in order to semantically diff it. You can see an example of this in the executable target for this project (see `main.swift`).

## Building From Source
The library target (`OpenAPIDiff`) and executable target (`openapi-diff`) can both be built quite easily with either `swift build` or by opening the repository root folder in Xcode 11.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,27 @@ extension OpenAPI.Document.Info.License: ApiComparable {
context: context,
changes: [
name.compare(to: other.name, in: "name"),
url.compare(to: other.url, in: "URL")
identifier.compare(to: other.identifier)
]
)
}
}

extension OpenAPI.Document.Info.License.Identifier: ApiComparable {
public func compare(to other: OpenAPI.Document.Info.License.Identifier, in context: String?) -> ApiDiff {
switch(self, other) {
case (.spdx(let id1), .spdx(let id2)):
return id1.compare(to: id2, in: "identifier")
case (.url(let url1), .url(let url2)):
return url1.compare(to: url2, in: "url")
case (.spdx(let id), .url(let url)):
return .changed(context: "identifier", from: "the identifier \(id)", to: "the url \(url)")
case (.url(let url), .spdx(let id)):
return .changed(context: "url", from: "the url \(url)", to: "the identifier \(id)")
}
}
}

extension OpenAPI.Document.Info: ApiComparable {
public func compare(to other: OpenAPI.Document.Info, in context: String?) -> ApiDiff {
return .init(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// OpenAPIReference+ApiComparable.swift
//
//
// Created by Mathew Polzin on 12/25/22.
//

import OpenAPIKit

extension OpenAPI.Reference: ApiComparable {
public func compare(to other: OpenAPIKit.OpenAPI.Reference<ReferenceType>, in context: String?) -> ApiDiff {
return .init(
context: context,
changes: [
jsonReference.compare(to: other.jsonReference),
summary.compare(to: other.summary),
description.compare(to: other.description)
]
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ extension OpenAPI.SecurityScheme.SecurityType {
return "oauth2"
case .openIdConnect:
return "openIdConnect"
case .mutualTLS:
return "mutualTLS"
}
}
}
Expand Down
39 changes: 18 additions & 21 deletions Sources/openapi-diff/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

import Foundation
import OpenAPIKit
import OpenAPIKit30
import OpenAPIKitCompat
import OpenAPIDiff
import Yams
import PureSwiftJSON
import ArgumentParser

struct OpenAPIDiff: ParsableCommand {
Expand Down Expand Up @@ -39,15 +40,6 @@ struct OpenAPIDiff: ParsableCommand {
)
var printStyle: PrintStyle = .plaintext

@Flag(
name: .customLong("fast"),
help: .init(
"Parse the input OpenAPI more quickly when possible.",
discussion: "This option currently only affects JSON parsing. Especially in Linux environments, the speed boost can be very substantial.\n\nThere is no inherent quality/speed tradeoff in play, but the parser this option uses is less battle tested so if stability is more important than speed, do not use this option."
)
)
var fastParsing: Bool = false

@Flag(
name: [.customLong("skip-schemas")],
help: .init(
Expand All @@ -61,26 +53,31 @@ struct OpenAPIDiff: ParsableCommand {
let left = URL(fileURLWithPath: firstFilePath)
let right = URL(fileURLWithPath: secondFilePath)

let api1: OpenAPI.Document
let api2: OpenAPI.Document
let api1V3: OpenAPIKit30.OpenAPI.Document?
let api2V3: OpenAPIKit30.OpenAPI.Document?
let api1: OpenAPIKit.OpenAPI.Document
let api2: OpenAPIKit.OpenAPI.Document

if left.pathExtension.lowercased() == "json" {
let file1 = try! Data(contentsOf: left)
let file2 = try! Data(contentsOf: right)

if (fastParsing) {
api1 = try! PSJSONDecoder().decode(OpenAPI.Document.self, from: file1)
api2 = try! PSJSONDecoder().decode(OpenAPI.Document.self, from: file2)
} else {
api1 = try! JSONDecoder().decode(OpenAPI.Document.self, from: file1)
api2 = try! JSONDecoder().decode(OpenAPI.Document.self, from: file2)
}
api1V3 = try? JSONDecoder().decode(OpenAPI.Document.self, from: file1)
api1 = api1V3?.convert(to: .v3_1_0) ??
(try! JSONDecoder().decode(OpenAPI.Document.self, from: file1))
api2V3 = try? JSONDecoder().decode(OpenAPI.Document.self, from: file2)
api2 = api2V3?.convert(to: .v3_1_0) ??
(try! JSONDecoder().decode(OpenAPI.Document.self, from: file2))
} else {
let file1 = try! String(contentsOf: left)
let file2 = try! String(contentsOf: right)

api1 = try! YAMLDecoder().decode(OpenAPI.Document.self, from: file1)
api2 = try! YAMLDecoder().decode(OpenAPI.Document.self, from: file2)
api1V3 = try? YAMLDecoder().decode(OpenAPI.Document.self, from: file1)
api1 = api1V3?.convert(to: .v3_1_0) ??
(try! YAMLDecoder().decode(OpenAPI.Document.self, from: file1))
api2V3 = try? YAMLDecoder().decode(OpenAPI.Document.self, from: file2)
api2 = api2V3?.convert(to: .v3_1_0) ??
(try! YAMLDecoder().decode(OpenAPI.Document.self, from: file2))
}

let filter: (ApiDiff) -> Bool = { apiDiff in
Expand Down

0 comments on commit d2ac032

Please sign in to comment.