Skip to content

Commit 7ff3273

Browse files
committed
create an API endpoint for querying the doc build status of a package
1 parent fa71324 commit 7ff3273

10 files changed

+125
-53
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import Media
2+
13
public
2-
protocol ServerResponseFactory<Request>
4+
protocol ServerResponseFactory
35
{
4-
associatedtype Request
5-
6-
func response(for requested:Request) throws -> ServerResponse
6+
func response(as type:AcceptType?) throws -> ServerResponse
77
}

Sources/Media/AcceptType.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@frozen public
2+
enum AcceptType:Equatable, Hashable, Sendable
3+
{
4+
case application(MediaSubtype?)
5+
case audio (MediaSubtype?)
6+
case font (MediaSubtype?)
7+
case image (MediaSubtype?)
8+
case model (MediaSubtype?)
9+
case text (MediaSubtype?)
10+
case video (MediaSubtype?)
11+
}

Sources/UnidocPages/API/Site.API.Get.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extension Site.API
33
@frozen public
44
enum Get:String
55
{
6+
case build
67
case github
78
case register
89
}
Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,58 @@
11
import HTTP
2+
import JSON
3+
import Media
4+
import UnidocDB
25
import UnidocQueries
36
import URI
47

58
extension PackageEditionsQuery.Output:ServerResponseFactory
69
{
710
public
8-
func response(for _:URI) throws -> ServerResponse
11+
func response(as type:AcceptType?) throws -> ServerResponse
912
{
10-
let list:Site.Tags.List = .init(from: self)
11-
return .ok(list.resource())
13+
switch type
14+
{
15+
case .application(.json):
16+
let json:JSON = .object
17+
{
18+
guard
19+
let repo:PackageRepo = self.record.repo,
20+
let release:PackageEditionsQuery.Facet = self.releases.first
21+
else
22+
{
23+
return
24+
}
25+
26+
$0["repo"] = "https://\(repo.origin)"
27+
28+
$0["release"]
29+
{
30+
$0["graphs"] = release.graphs?.count ?? 0
31+
$0["tag"] = release.edition.name
32+
}
33+
34+
guard
35+
let prerelease:PackageEditionsQuery.Facet = self.prereleases.first,
36+
prerelease.edition.patch > release.edition.patch
37+
else
38+
{
39+
return
40+
}
41+
42+
$0["prerelease"]
43+
{
44+
$0["graphs"] = prerelease.graphs?.count ?? 0
45+
$0["tag"] = prerelease.edition.name
46+
}
47+
}
48+
49+
return .ok(.init(
50+
content: .binary(json.utf8),
51+
type: .application(.json, charset: .utf8)))
52+
53+
case _:
54+
let list:Site.Tags.List = .init(from: self)
55+
return .ok(list.resource())
56+
}
1257
}
1358
}

Sources/UnidocPages/Responses/SearchIndexQuery.Output (ext).swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import HTTP
2+
import Media
23
import UnidocQueries
34
import URI
45

56
extension SearchIndexQuery.Output:ServerResponseFactory
67
{
78
public
8-
func response(for _:URI) throws -> ServerResponse
9+
func response(as _:AcceptType?) throws -> ServerResponse
910
{
1011
let content:ServerResource.Content
1112
switch self.json

Sources/UnidocPages/Responses/ThinQuery.Output (ext).swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import HTTP
2+
import Media
23
import UnidocQueries
34
import UnidocRecords
45
import URI
56

67
extension ThinQuery.Output:ServerResponseFactory
78
{
89
public
9-
func response(for _:URI) throws -> ServerResponse
10+
func response(as _:AcceptType?) throws -> ServerResponse
1011
{
1112
if LookupPredicate.self is Volume.Range.Type
1213
{

Sources/UnidocPages/Responses/WideQuery.Output (ext).swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import HTTP
2+
import Media
23
import UnidocQueries
34
import UnidocRecords
45
import URI
56

67
extension WideQuery.Output:ServerResponseFactory
78
{
89
public
9-
func response(for _:URI) throws -> ServerResponse
10+
func response(as _:AcceptType?) throws -> ServerResponse
1011
{
1112
guard let principal:WideQuery.Output.Principal = self.principal
1213
else

Sources/UnidocServer/Operations/Server.Endpoint.swift

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,6 @@ extension Server.Endpoint
7373
return nil
7474
}
7575

76-
case Site.API.root:
77-
switch Site.API.Get.init(trunk)
78-
{
79-
case nil:
80-
return nil
81-
82-
case .github?:
83-
if let parameters:[(String, String)] = uri.query?.parameters,
84-
let operation:Server.Operation.Login = .init(parameters: parameters)
85-
{
86-
return .interactive(operation)
87-
}
88-
89-
case .register?:
90-
if let parameters:[(String, String)] = uri.query?.parameters,
91-
let operation:Server.Operation.Register = .init(parameters: parameters)
92-
{
93-
return .interactive(operation)
94-
}
95-
}
96-
9776
case Site.Asset.root:
9877
let asset:Site.Asset.Get? = .init(trunk)
9978
return asset.map { .static(.init($0, tag: tag)) }
@@ -130,16 +109,47 @@ extension Server.Endpoint
130109

131110
switch root
132111
{
112+
case Site.API.root:
113+
switch Site.API.Get.init(trunk)
114+
{
115+
case nil:
116+
return nil
117+
118+
case .build?:
119+
if let package:String = stem.first
120+
{
121+
return .interactive(Server.Operation.Pipeline<PackageEditionsQuery>.init(
122+
output: explain ? nil : .application(.json),
123+
query: .init(package: .init(package), limit: 1),
124+
uri: uri,
125+
tag: tag))
126+
}
127+
128+
case .github?:
129+
if let parameters:[(String, String)] = uri.query?.parameters,
130+
let operation:Server.Operation.Login = .init(parameters: parameters)
131+
{
132+
return .interactive(operation)
133+
}
134+
135+
case .register?:
136+
if let parameters:[(String, String)] = uri.query?.parameters,
137+
let operation:Server.Operation.Register = .init(parameters: parameters)
138+
{
139+
return .interactive(operation)
140+
}
141+
}
142+
133143
case Site.Tags.root:
134144
return .interactive(Server.Operation.Pipeline<PackageEditionsQuery>.init(
135-
explain: explain,
145+
output: explain ? nil : .text(.html),
136146
query: .init(package: .init(trunk)),
137147
uri: uri,
138148
tag: tag))
139149

140150
case Site.Docs.root:
141151
return .interactive(Server.Operation.Pipeline<WideQuery>.init(
142-
explain: explain,
152+
output: explain ? nil : .text(.html),
143153
query: .init(
144154
volume: .init(trunk),
145155
lookup: .init(stem: stem, hash: hash)),
@@ -148,7 +158,7 @@ extension Server.Endpoint
148158

149159
case Site.Guides.root:
150160
return .interactive(Server.Operation.Pipeline<ThinQuery<Volume.Range>>.init(
151-
explain: explain,
161+
output: explain ? nil : .text(.html),
152162
query: .init(
153163
volume: .init(trunk),
154164
lookup: .articles),
@@ -157,7 +167,7 @@ extension Server.Endpoint
157167

158168
case "articles":
159169
return .interactive(Server.Operation.Pipeline<WideQuery>.init(
160-
explain: explain,
170+
output: explain ? nil : .text(.html),
161171
query: .init(
162172
volume: .init(package: "__swiftinit", version: "0.0.0"),
163173
lookup: .init(stem: ["Articles", trunk], hash: nil)),
@@ -169,7 +179,7 @@ extension Server.Endpoint
169179
{
170180
return .interactive(
171181
Server.Operation.Pipeline<SearchIndexQuery<VolumeIdentifier>>.init(
172-
explain: explain,
182+
output: explain ? nil : .application(.json),
173183
query: .init(
174184
from: UnidocDatabase.Search.name,
175185
tag: tag,
@@ -181,22 +191,20 @@ extension Server.Endpoint
181191
{
182192
return .interactive(
183193
Server.Operation.Pipeline<SearchIndexQuery<Int32>>.init(
184-
explain: explain,
194+
output: explain ? nil : .application(.json),
185195
query: .init(
186196
from: UnidocDatabase.Meta.name,
187197
tag: tag,
188198
id: 0),
189199
uri: uri,
190200
tag: tag))
191201
}
192-
else
193-
{
194-
return nil
195-
}
196202

197203
case _:
198-
return nil
204+
break
199205
}
206+
207+
return nil
200208
}
201209

202210
private static
@@ -223,14 +231,14 @@ extension Server.Endpoint
223231
if let overload:Symbol.Decl
224232
{
225233
return .interactive(Server.Operation.Pipeline<ThinQuery<Symbol.Decl>>.init(
226-
explain: false,
234+
output: .text(.html),
227235
query: .init(volume: query.volume, lookup: overload),
228236
uri: uri))
229237
}
230238
else
231239
{
232240
return .interactive(Server.Operation.Pipeline<ThinQuery<Volume.Shoot>>.init(
233-
explain: false,
241+
output: .text(.html),
234242
query: query,
235243
uri: uri))
236244
}

Sources/UnidocServer/Operations/Server.Operation.Pipeline.swift

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import HTTP
2+
import Media
23
import MD5
34
import MongoDB
45
import UnidocDB
@@ -8,21 +9,22 @@ import URI
89
extension Server.Operation
910
{
1011
struct Pipeline<Query>:Sendable
11-
where Query:DatabaseQuery,
12-
Query.Output:ServerResponseFactory<URI>
12+
where Query:DatabaseQuery, Query.Output:ServerResponseFactory
1313
{
14-
let explain:Bool
14+
/// If nil, the query will be explained instead of executed. If non-nil, this field
15+
/// will be passed to the query’s output type, which may influence the response it
16+
/// produces.
17+
let output:AcceptType?
1518
let query:Query
1619
let uri:URI
1720
let tag:MD5?
1821

19-
init(
20-
explain:Bool,
22+
init(output:AcceptType?,
2123
query:Query,
2224
uri:URI,
2325
tag:MD5? = nil)
2426
{
25-
self.explain = explain
27+
self.output = output
2628
self.query = query
2729
self.uri = uri
2830
self.tag = tag
@@ -49,7 +51,9 @@ extension Server.Operation.Pipeline:UnrestrictedOperation
4951
func load(from db:UnidocDatabase,
5052
with session:Mongo.Session) async throws -> ServerResponse?
5153
{
52-
if self.explain
54+
guard
55+
let accept:AcceptType = self.output
56+
else
5357
{
5458
let explanation:String = try await db.explain(
5559
query: self.query,
@@ -67,7 +71,7 @@ extension Server.Operation.Pipeline:UnrestrictedOperation
6771
return nil
6872
}
6973

70-
switch try output.response(for: self.uri)
74+
switch try output.response(as: accept)
7175
{
7276
case .redirect(let redirect, cookies: let cookies):
7377
return .redirect(redirect, cookies: cookies)

Sources/UnidocServer/Operations/Server.Operation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ extension Server.Operation:HTTPServerOperation
5454
// Hilariously, we don’t have a home page yet. So we just redirect to the docs
5555
// for the standard library.
5656
let get:Server.Endpoint = .interactive(Server.Operation.Pipeline<WideQuery>.init(
57-
explain: false,
57+
output: .text(.html),
5858
query: .init(
5959
volume: .init(package: .swift, version: nil),
6060
lookup: .init(stem: [])),

0 commit comments

Comments
 (0)