Skip to content

Commit

Permalink
Refine type parsing, use separate object for query
Browse files Browse the repository at this point in the history
  • Loading branch information
ffried committed Oct 18, 2022
1 parent bd661e0 commit f351e8b
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 66 deletions.
41 changes: 22 additions & 19 deletions Sources/RouteDocs/DefaultDocsView/doc_documentation.leaf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
<div id="endpoint-header-#import("groupID")-#(index)" class="card-header d-flex justify-content-between">
<h3 class="mb-0 mr-5 flex-fill">
<!-- 'w-100 text-left' => would stretch the button to full width, extending the clickable space -->
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="\#endpoint-content-#import("groupID")-#(index)" aria-expanded="false" aria-controls="endpoint-content-#import("groupID")-#(index)">
<button
class="btn btn-link collapsed"
data-toggle="collapse"
data-target="\#endpoint-content-#import("groupID")-#(index)"
aria-expanded="false"
aria-controls="endpoint-content-#import("groupID")-#(index)">
<strong>#(doc.method)</strong> <em>#(doc.path)</em>
</button>
</h3>
Expand All @@ -14,36 +19,34 @@
</div>
#endif
</div>
<div id="endpoint-content-#import("groupID")-#(index)" class="collapse" aria-labelledby="endpoint-header-#import("groupID")-#(index)" data-parent="\##import("accordionID")-#import("groupID")">
<div
id="endpoint-content-#import("groupID")-#(index)"
class="collapse"
aria-labelledby="endpoint-header-#import("groupID")-#(index)"
data-parent="\##import("accordionID")-#import("groupID")">
<div class="card-body">
#if(!doc.query || doc.query.body.isEmpty):
#if(!doc.query):
#if(!doc.request):
<!-- if(!doc.request || count(doc.request.objects) <= 0): -->
#if(!doc.response):
<!-- if(!doc.response || count(doc.response.objects) <= 0): -->
<p class="card-text text-body">
<em>This request has no query or body and returns no response (usually a HTTP 204).</em>
</p>
#endif
#endif
#endif
#if(doc.query && !doc.query.body.isEmpty):
#if(doc.query):
<div class="card">
<h4 class="card-header">Query (<em>#(doc.query.name)</em>) parameters</h4>
#if(doc.query.body.fields):
<ul class="list-group list-group-flush">
#for(field in doc.query.body.fields):
#extend("doc_field"):
#export("nonOptionalBadgeStyle", "badge-danger")
#export("optionalBadgeStyle", "badge-secondary")
#endextend
#endfor
</ul>
#endif
<h4 class="card-header">Query parameters</h4>
#for(object in doc.query.objects):
#extend("doc_object"):
#export("nonOptionalBadgeStyle", "badge-danger")
#export("optionalBadgeStyle", "badge-secondary")
#endextend
#endfor
</div>
#endif
#if(doc.request):
<div class="card#if(doc.query && !doc.query.body.isEmpty): mt-3#endif">
<div class="card#if(doc.query): mt-3#endif">
<h4 class="card-header">
Request Body (<em>#(doc.request.mediaType.type)/#(doc.request.mediaType.subtype)</em>)
</h4>
Expand All @@ -56,7 +59,7 @@
</div>
#endif
#if(doc.response):
<div class="card #if((doc.query && !doc.query.body.isEmpty) || doc.response): mt-3#endif">
<div class="card #if(doc.query || doc.response): mt-3#endif">
<h4 class="card-header">
Response Body (<em>#(doc.response.mediaType.type)/#(doc.response.mediaType.subtype)</em>)
</h4>
Expand Down
14 changes: 10 additions & 4 deletions Sources/RouteDocs/DefaultDocsView/docs.leaf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Expand All @@ -11,7 +11,11 @@
<meta name="description" content="API Docs" />
<title>API Docs</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<style>.content-container { margin-bottom: 76px; }</style>
<style>
.content-container {
margin-bottom: 76px;
}
</style>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
Expand All @@ -29,7 +33,9 @@
<div class="jumbotron pt-5 pb-4 mb-0">
<h3 class="display-4"><small class="text-muted mr-3">📚</small>API Documentation</h3>
<hr class="my-4">
<p class="lead">Each endpoint can be expanded to list the query parameters as well as request and response bodies.</p>
<p class="lead">
Each endpoint can be expanded to list the query parameters as well as request and response bodies.
</p>
</div>
#extend("doc_list"):
#endextend
Expand All @@ -38,7 +44,7 @@
<div class="container footer-container">
<div class="footer rounded-top bg-dark fixed-bottom text-white text-center py-2">
<p class="my-1">A <a href="https://www.sersoft.de" class="text-info">ser.soft GmbH</a> creation.</p>
<p class="my-1">&copy; <span id="docs-current-year">2020</span> - All rights reserved.</p>
<p class="my-1">&copy; <span id="docs-current-year">2022</span> - All rights reserved.</p>
</div>
</div>
</body>
Expand Down
17 changes: 14 additions & 3 deletions Sources/RouteDocs/DocsViewContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,18 @@ public struct DocsViewContext: Encodable, Sendable {
public let body: Body
}

public struct Query: Encodable, Sendable {
public let objects: Array<Object>
}

public struct Payload: Encodable, @unchecked Sendable { // unchecked because of HTTPMediaType
public let mediaType: HTTPMediaType
public let objects: Array<Object>
}

public let method: HTTPMethod
public let path: String
public let query: Object?
public let query: Query?
public let request: Payload?
public let response: Payload?
public let requiredAuthorization: Array<String>
Expand Down Expand Up @@ -83,7 +87,7 @@ fileprivate extension HTTPMethod {

fileprivate extension DocumentationType {
func docsTypeName(using namePath: KeyPath<DocumentationType, String>?) -> String {
customName ?? namePath.map { self[keyPath: $0] } ?? typeDescription.typeName(includingModule: false)
customName ?? namePath.map { self[keyPath: $0] } ?? typeDescription.typeName(with: [.withParents])
}
}

Expand Down Expand Up @@ -126,6 +130,13 @@ extension DocsViewContext.Documentation.Object {
}
}

extension DocsViewContext.Documentation.Query {
public init(query: EndpointDocumentation.Query,
usingName namePath: KeyPath<DocumentationType, String>? = nil) {
self.init(objects: query.objects.map { .init(object: $0, usingName: namePath) })
}
}

extension DocsViewContext.Documentation.Payload {
public init(payload: EndpointDocumentation.Payload,
usingName namePath: KeyPath<DocumentationType, String>? = nil) {
Expand All @@ -139,7 +150,7 @@ extension DocsViewContext.Documentation {
usingName namePath: KeyPath<DocumentationType, String>? = nil) {
self.init(method: documentation.method,
path: documentation.path,
query: documentation.query.map { .init(object: $0, usingName: namePath) },
query: documentation.query.map { .init(query: $0, usingName: namePath) },
request: documentation.request.map { .init(payload: $0, usingName: namePath) },
response: documentation.response.map { .init(payload: $0, usingName: namePath) },
requiredAuthorization: documentation.requiredAuthorization)
Expand Down
2 changes: 1 addition & 1 deletion Sources/RouteDocs/EndpointDocumentable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension Route: EndpointDocumentable {

@inlinable
public func addDocumentation(groupedAs groupName: String? = nil,
query: EndpointDocumentation.Object? = nil,
query: EndpointDocumentation.Query? = nil,
request: EndpointDocumentation.Payload? = nil,
response: EndpointDocumentation.Payload? = nil,
requiredAuthorization: Array<String> = .init()) {
Expand Down
43 changes: 32 additions & 11 deletions Sources/RouteDocs/EndpointDocumentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public struct DocumentationType: Codable, Equatable, CustomStringConvertible, Se
public let customName: String?

public var defaultName: String {
customName ?? typeDescription.typeName(includingModule: true)
customName ?? typeDescription.typeName(with: [.withModule, .withParents])
}

public var description: String { defaultName }
Expand Down Expand Up @@ -159,6 +159,24 @@ public struct EndpointDocumentation: Codable, Equatable, CustomStringConvertible
}
}

public struct Query: Codable, Equatable, CustomStringConvertible, Sendable {
public let objects: Array<Object>

public var description: String {
"""
\(objects.map { String(describing: $0) }.joined(separator: "\n\n"))
"""
}

public func filterObjects(with closure: (Object) throws -> Object?) rethrows -> Self {
try .init(objects: objects.compactMap(closure))
}

public func filterObjects(with closure: (Object) throws -> Bool) rethrows -> Self {
try .init(objects: objects.filter(closure))
}
}

public struct Payload: Codable, Equatable, CustomStringConvertible, @unchecked Sendable { // unchecked because of HTTPMediaType
public let mediaType: HTTPMediaType
public let objects: Array<Object>
Expand All @@ -182,7 +200,7 @@ public struct EndpointDocumentation: Codable, Equatable, CustomStringConvertible
public let groupName: String?
public let method: HTTPMethod
public let path: String
public let query: Object?
public let query: Query?
public let request: Payload?
public let response: Payload?
public let requiredAuthorization: Array<String>
Expand All @@ -203,7 +221,7 @@ extension EndpointDocumentation {
public init(method: HTTPMethod,
path: Array<PathComponent>,
groupName: String? = nil,
query: Object? = nil,
query: Query? = nil,
request: Payload? = nil,
response: Payload? = nil,
requiredAuthorization: Array<String> = .init()) {
Expand Down Expand Up @@ -237,6 +255,17 @@ extension EndpointDocumentation.Payload {
}
}

extension EndpointDocumentation.Query {
public init<T: Decodable>(object: T.Type,
customUserInfo: Dictionary<CodingUserInfoKey, Any> = .init()) throws {
try self.init(objects: EndpointDocumentation.Object.objects(from: object.reflectedDocumentation(withCustomUserInfo: customUserInfo)))
}

public init<T: CustomDocumentable>(object: T.Type) throws {
self.init(objects: EndpointDocumentation.Object.objects(from: object.object(with: object)))
}
}

extension EndpointDocumentation.Object {
fileprivate static func addObjects(from documentation: DocumentationObject,
to list: inout Array<EndpointDocumentation.Object>) {
Expand All @@ -256,14 +285,6 @@ extension EndpointDocumentation.Object {
let actualType = (documentation.type as? AnyTypeWrapping.Type)?.leafType ?? documentation.type
self.init(type: DocumentationType(actualType), body: .init(documentation: documentation.body))
}

public init<T: Decodable>(object: T.Type, customUserInfo: Dictionary<CodingUserInfoKey, Any> = .init()) throws {
try self.init(documentation: object.reflectedDocumentation(withCustomUserInfo: customUserInfo))
}

public init<T: CustomDocumentable>(object: T.Type) {
self.init(documentation: object.object(with: object))
}
}

extension EndpointDocumentation.Object.Body {
Expand Down
Loading

0 comments on commit f351e8b

Please sign in to comment.