Skip to content

Commit 3636f1e

Browse files
committed
enable updating repo state from webhook events
1 parent 7145294 commit 3636f1e

15 files changed

+383
-111
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import JSON
2+
3+
extension GitHub
4+
{
5+
@frozen public
6+
struct Installation:Equatable, Sendable
7+
{
8+
public
9+
let id:Int32
10+
11+
@inlinable public
12+
init(id:Int32)
13+
{
14+
self.id = id
15+
}
16+
}
17+
}
18+
extension GitHub.Installation
19+
{
20+
/// There are a lot more fields in the API response, but we only need the ID.
21+
@frozen public
22+
enum CodingKey:String, Sendable
23+
{
24+
case id
25+
}
26+
}
27+
extension GitHub.Installation:JSONObjectDecodable
28+
{
29+
public
30+
init(json:JSON.ObjectDecoder<CodingKey>) throws
31+
{
32+
self.init(id: try json[.id].decode())
33+
}
34+
}

Sources/GitHubAPI/GitHub.WebhookCreate.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ extension GitHub
1515
let refType:RefType
1616

1717
public
18-
let installation:UInt?
18+
let installation:Int32?
1919

2020
@inlinable public
21-
init(repo:Repo, ref:String, refType:RefType, installation:UInt?)
21+
init(repo:Repo, ref:String, refType:RefType, installation:Int32?)
2222
{
2323
self.repo = repo
2424
self.ref = ref
@@ -37,10 +37,6 @@ extension GitHub.WebhookCreate:JSONObjectDecodable
3737
case ref_type
3838

3939
case installation
40-
enum Installation:String, Sendable
41-
{
42-
case id
43-
}
4440
}
4541

4642
public
@@ -50,7 +46,8 @@ extension GitHub.WebhookCreate:JSONObjectDecodable
5046
repo: try json[.repository].decode(),
5147
ref: try json[.ref].decode(),
5248
refType: try json[.ref_type].decode(),
53-
installation: try json[.installation]?.decode(using: CodingKey.Installation.self)
49+
installation: try json[.installation]?.decode(
50+
using: GitHub.Installation.CodingKey.self)
5451
{
5552
try $0[.id].decode()
5653
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import JSON
2+
3+
@available(*, unavailable, message: "unimplemented")
4+
extension GitHub.WebhookInstallationRepositories
5+
{
6+
@frozen public
7+
enum Selection:String, JSONEncodable, JSONDecodable, Equatable, Sendable
8+
{
9+
case all
10+
case selected
11+
}
12+
}
13+
extension GitHub
14+
{
15+
@available(*, unavailable, message: "unimplemented")
16+
@frozen public
17+
struct WebhookInstallationRepositories:Sendable
18+
{
19+
public
20+
let installation:Installation
21+
public
22+
let selection:Selection
23+
public
24+
let added:[Never]
25+
public
26+
let removed:[Never]
27+
}
28+
}

Sources/UnidocDB/Mongo.CollectionModel.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,24 @@ extension Mongo.CollectionModel
463463

464464
extension Mongo.CollectionModel
465465
{
466+
@inlinable package
467+
func modify(upserting id:Element.ID,
468+
returning phase:Mongo.UpdatePhase = .new,
469+
with session:Mongo.Session,
470+
do encode:(inout Mongo.UpdateEncoder) -> ()) async throws -> (state:Element, new:Bool)
471+
where Element:BSONDecodable, Element.ID:BSONEncodable
472+
{
473+
let (element, upserted):(Element, Element.ID?) = try await session.run(
474+
command: Mongo.FindAndModify<Mongo.Upserting<Element, Element.ID>>.init(Self.name,
475+
returning: phase)
476+
{
477+
$0[.query] { $0["_id"] = id }
478+
$0[.update] { encode(&$0) }
479+
},
480+
against: self.database)
481+
return (element, upserted != nil)
482+
}
483+
466484
@inlinable package
467485
func modify(existing id:Element.ID,
468486
returning phase:Mongo.UpdatePhase = .new,
@@ -483,6 +501,27 @@ extension Mongo.CollectionModel
483501
against: self.database)
484502
return element
485503
}
504+
505+
@inlinable package
506+
func modify(existing predicate:some Mongo.PredicateEncodable,
507+
returning phase:Mongo.UpdatePhase = .new,
508+
with session:Mongo.Session,
509+
do encode:(inout Mongo.UpdateEncoder) -> ()) async throws -> Element?
510+
where Element:BSONDecodable, Element.ID:BSONEncodable
511+
{
512+
let (element, _):(Element?, Never?) = try await session.run(
513+
command: Mongo.FindAndModify<Mongo.Existing<Element>>.init(Self.name,
514+
returning: phase)
515+
{
516+
$0[.query] { predicate.encode(to: &$0) }
517+
$0[.update]
518+
{
519+
encode(&$0)
520+
}
521+
},
522+
against: self.database)
523+
return element
524+
}
486525
}
487526
extension Mongo.CollectionModel
488527
{

Sources/UnidocDB/Packages/Unidoc.Autoincrement.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import UnidocRecords
44

55
extension Unidoc
66
{
7+
// TODO: refactor use sites to stop using document postimages
78
enum Autoincrement<Document>:Sendable
89
where Document:Identifiable,
910
Document:BSONDecodable,

Sources/UnidocDB/Packages/Unidoc.DB.Packages.swift

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -155,29 +155,6 @@ extension Unidoc.DB.Packages
155155
}
156156
extension Unidoc.DB.Packages
157157
{
158-
public
159-
func findGitHub(repo id:Int32,
160-
with session:Mongo.Session) async throws -> Unidoc.PackageMetadata?
161-
{
162-
let command:Mongo.Find<Mongo.Single<Unidoc.PackageMetadata>> = .init(Self.name,
163-
limit: 1)
164-
{
165-
$0[.filter]
166-
{
167-
// We need this to use the partial index, for some reason.
168-
$0[ Unidoc.PackageMetadata[.repo] /
169-
Unidoc.PackageRepo[.github]] { $0[.exists] = true }
170-
171-
$0[ Unidoc.PackageMetadata[.repo] /
172-
Unidoc.PackageRepo[.github] /
173-
Unidoc.GitHubOrigin[.id]] = id
174-
}
175-
$0[.hint] = Self.indexRepoGitHub.id
176-
}
177-
178-
return try await session.run(command: command, against: self.database)
179-
}
180-
181158
public
182159
func update(metadata:Unidoc.PackageMetadata,
183160
with session:Mongo.Session) async throws -> Bool?
@@ -234,6 +211,41 @@ extension Unidoc.DB.Packages
234211
}
235212
}
236213
}
214+
215+
public
216+
func updateWebhook(configurationURL:String,
217+
repo:Unidoc.PackageRepo,
218+
with session:Mongo.Session) async throws -> Unidoc.PackageMetadata?
219+
{
220+
let package:Unidoc.PackageByGitHubID
221+
222+
switch repo.origin
223+
{
224+
case .github(let origin): package = .init(id: origin.id)
225+
}
226+
227+
return try await self.modify(existing: package, with: session)
228+
{
229+
$0[.set]
230+
{
231+
$0[Element[.repo]] = repo
232+
$0[Element[.repoWebhook]] = configurationURL
233+
}
234+
}
235+
}
236+
237+
public
238+
func detachWebhook(package:Unidoc.Package,
239+
with session:Mongo.Session) async throws -> Unidoc.PackageMetadata?
240+
{
241+
try await self.modify(existing: package, with: session)
242+
{
243+
$0[.unset]
244+
{
245+
$0[Unidoc.PackageMetadata[.repoWebhook]] = ()
246+
}
247+
}
248+
}
237249
}
238250
extension Unidoc.DB.Packages
239251
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import MongoQL
2+
3+
extension Unidoc
4+
{
5+
struct PackageByGitHubID
6+
{
7+
let id:Int32
8+
}
9+
}
10+
extension Unidoc.PackageByGitHubID:Mongo.PredicateEncodable
11+
{
12+
func encode(to predicate:inout Mongo.PredicateEncoder)
13+
{
14+
predicate[Unidoc.PackageMetadata[.repo]
15+
/ Unidoc.PackageRepo[.github]
16+
/ Unidoc.GitHubOrigin[.id]] = self.id
17+
18+
predicate[Unidoc.PackageMetadata[.repo] / Unidoc.PackageRepo[.github]]
19+
{
20+
$0[.exists] = true
21+
}
22+
}
23+
}

Sources/UnidocDB/Packages/Unidoc.PackageMetadata (ext).swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ extension Unidoc.PackageMetadata
1616
public
1717
func nextTagsFetch() -> UnixMillisecond?
1818
{
19+
if case _? = self.repoWebhook
20+
{
21+
return nil
22+
}
23+
1924
guard
2025
let repo:Unidoc.PackageRepo = self.repo,
2126
let interval:Milliseconds = repo.crawlingIntervalTarget(

Sources/UnidocDB/Packages/Unidoc.PackageRepo (ext).swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ extension Unidoc.PackageRepo:Mongo.MasterCodingModel
1111
extension Unidoc.PackageRepo
1212
{
1313
@inlinable public static
14-
func github(_ repo:GitHub.Repo, crawled:UnixMillisecond) throws -> Self
14+
func github(_ repo:GitHub.Repo,
15+
crawled:UnixMillisecond,
16+
installation:Int32? = nil) throws -> Self
1517
{
1618
/// We clip this to Midnights because we use this as a shard key, and also because
1719
/// Midnights are Swifty.
@@ -57,7 +59,8 @@ extension Unidoc.PackageRepo
5759
size: repo.size,
5860
archived: repo.archived,
5961
disabled: repo.disabled,
60-
fork: repo.fork)),
62+
fork: repo.fork,
63+
installation: installation)),
6164
forks: repo.forks,
6265
stars: repo.stars)
6366
}

0 commit comments

Comments
 (0)