Skip to content

Commit c2aa4ec

Browse files
committed
add the Installation ID to the user info, and return the user info for the package owner as part of EditionState
1 parent b48b741 commit c2aa4ec

11 files changed

+121
-26
lines changed

Sources/UnidocDB/Users/Unidoc.User.swift

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ extension Unidoc
2323

2424
public
2525
var github:GitHub.User.Profile?
26+
public
27+
var githubInstallation:Int32?
2628

2729
/// Additional accounts that this user has access to.
2830
public
@@ -35,6 +37,7 @@ extension Unidoc
3537
apiKey:Int64? = nil,
3638
symbol:String? = nil,
3739
github:GitHub.User.Profile? = nil,
40+
githubInstallation:Int32? = nil,
3841
access:[Account] = [])
3942
{
4043
self.id = id
@@ -43,30 +46,42 @@ extension Unidoc
4346
self.apiKey = apiKey
4447
self.symbol = symbol
4548
self.github = github
49+
self.githubInstallation = githubInstallation
4650
self.access = access
4751
}
4852
}
4953
}
5054
extension Unidoc.User
5155
{
52-
@inlinable public static
53-
func github(_ user:GitHub.User, initialLimit:Int) -> Self
56+
@inlinable public
57+
init(githubInstallation appInstallation:GitHub.Installation, initialLimit:Int)
58+
{
59+
self.init(github: appInstallation.account.id, symbol: appInstallation.account.login)
60+
self.apiLimitLeft = initialLimit
61+
self.githubInstallation = appInstallation.id
62+
}
63+
64+
@inlinable public
65+
init(github user:GitHub.User, initialLimit:Int)
66+
{
67+
self.init(github: user.id, symbol: user.profile.login)
68+
self.apiLimitLeft = initialLimit
69+
self.github = user.profile
70+
}
71+
72+
@inlinable
73+
init(github id:UInt32, symbol:String)
5474
{
5575
/// r u taylor swift?
56-
let level:Unidoc.User.Level = user.id == 2556986 ? .administratrix : .human
57-
let id:Unidoc.Account = .init(type: .github, user: user.id)
58-
return .init(id: id,
59-
level: level,
60-
// This will only be written to the database if the user is new.
61-
apiLimitLeft: initialLimit,
62-
symbol: user.profile.login,
63-
github: user.profile)
76+
let level:Unidoc.User.Level = id == 2556986 ? .administratrix : .human
77+
let id:Unidoc.Account = .init(type: .github, user: id)
78+
self.init(id: id, level: level, symbol: symbol)
6479
}
6580

66-
@inlinable public static
67-
func machine(_ number:UInt32 = 0) -> Self
81+
@inlinable public
82+
init(machine:UInt32)
6883
{
69-
.init(id: .init(type: .unidoc, user: number), level: .machine)
84+
self.init(id: .init(type: .unidoc, user: machine), level: .machine)
7085
}
7186
}
7287
extension Unidoc.User
@@ -101,6 +116,7 @@ extension Unidoc.User:Mongo.MasterCodingModel
101116
case symbol = "Y"
102117

103118
case github = "github"
119+
case githubInstallation = "github_I"
104120

105121
case access = "a"
106122
}
@@ -118,6 +134,7 @@ extension Unidoc.User:BSONDocumentEncodable
118134
bson[.symbol] = self.symbol
119135

120136
bson[.github] = self.github
137+
bson[.githubInstallation] = self.githubInstallation
121138

122139
bson[.access] = self.access.isEmpty ? nil : self.access
123140
}
@@ -133,6 +150,7 @@ extension Unidoc.User:BSONDocumentDecodable
133150
apiKey: try bson[.apiKey]?.decode(),
134151
symbol: try bson[.symbol]?.decode(),
135152
github: try bson[.github]?.decode(),
153+
githubInstallation: try bson[.githubInstallation]?.decode(),
136154
access: try bson[.access]?.decode() ?? [])
137155
}
138156
}
@@ -149,6 +167,7 @@ extension Unidoc.User
149167
$0[Self[.level]] = self.level
150168
$0[Self[.symbol]] = self.symbol
151169
$0[Self[.github]] = self.github
170+
$0[Self[.githubInstallation]] = self.githubInstallation
152171
}
153172
u[.setOnInsert]
154173
{

Sources/UnidocQueries/Mongo.PipelineEncoder (ext).swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ extension Mongo.PipelineEncoder
2424
self[stage: .set] { $0[output] { $0[.first] = output } }
2525
}
2626

27+
mutating
28+
func loadUser(owning package:Mongo.AnyKeyPath, as output:Mongo.AnyKeyPath)
29+
{
30+
self[stage: .set]
31+
{
32+
$0[output] = package / Unidoc.PackageMetadata[.repo] / Unidoc.PackageRepo[.account]
33+
}
34+
self[stage: .lookup]
35+
{
36+
$0[.from] = Unidoc.DB.Users.name
37+
$0[.localField] = output
38+
$0[.foreignField] = Unidoc.User[.id]
39+
$0[.as] = output
40+
}
41+
// Unbox single-element array.
42+
self[stage: .set] { $0[output] { $0[.first] = output } }
43+
}
44+
2745
mutating
2846
func loadEdition(matching predicate:Unidoc.VersionPredicate,
2947
from package:Mongo.AnyKeyPath,

Sources/UnidocQueries/Versions/Unidoc.EditionState.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ extension Unidoc
1212
let package:PackageMetadata
1313
public
1414
let version:VersionState
15-
1615
public
1716
let build:BuildMetadata?
17+
public
18+
let owner:User?
1819

19-
init(package:PackageMetadata, version:VersionState, build:BuildMetadata?)
20+
init(package:PackageMetadata, version:VersionState, build:BuildMetadata?, owner:User?)
2021
{
2122
self.package = package
2223
self.version = version
2324
self.build = build
25+
self.owner = owner
2426
}
2527
}
2628
}
@@ -32,6 +34,7 @@ extension Unidoc.EditionState:Mongo.MasterCodingModel
3234
case package
3335
case version
3436
case build
37+
case owner
3538
}
3639
}
3740
extension Unidoc.EditionState:BSONDocumentDecodable
@@ -42,6 +45,7 @@ extension Unidoc.EditionState:BSONDocumentDecodable
4245
self.init(
4346
package: try bson[.package].decode(),
4447
version: try bson[.version].decode(),
45-
build: try bson[.build]?.decode())
48+
build: try bson[.build]?.decode(),
49+
owner: try bson[.owner]?.decode())
4650
}
4751
}

Sources/UnidocQueries/Versions/Unidoc.EditionStateDirectQuery.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,9 @@ extension Unidoc.EditionStateDirectQuery:Mongo.PipelineQuery
7272

7373
// Unbox single-element array.
7474
pipeline[stage: .unwind] = Unidoc.EditionState[.version]
75+
76+
pipeline.loadUser(
77+
owning: Unidoc.EditionState[.package],
78+
as: Unidoc.EditionState[.owner])
7579
}
7680
}

Sources/UnidocQueries/Versions/Unidoc.EditionStateSymbolicQuery.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ extension Unidoc.EditionStateSymbolicQuery:Unidoc.AliasingQuery
4040
// Unbox single-element array.
4141
pipeline[stage: .unwind] = Unidoc.EditionState[.version]
4242

43+
pipeline.loadUser(
44+
owning: Unidoc.EditionState[.package],
45+
as: Unidoc.EditionState[.owner])
46+
4347
pipeline[stage: .lookup]
4448
{
4549
$0[.from] = Unidoc.DB.PackageBuilds.name

Sources/UnidocServer/Operations/Interactions/Unidoc.UpdatePackageRuleOperation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ extension Unidoc.UpdatePackageRuleOperation:Unidoc.RestrictedOperation
7777
}
7878

7979
let registered:Unidoc.UserSecrets = try await server.db.users.update(
80-
user: .github(user, initialLimit: server.db.policy.apiLimitPerReset),
80+
user: .init(github: user, initialLimit: server.db.policy.apiLimitPerReset),
8181
with: session)
8282

8383
package = try await server.db.packages.insert(editor: registered.account,

Sources/UnidocServer/Operations/Interactions/Unidoc.UserIndexOperation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension Unidoc.UserIndexOperation
5656
let user:Unidoc.User = try await restAPI.connect
5757
{
5858
let user:GitHub.User = try await $0.get(from: "/user", with: .token(self.token))
59-
return .github(user, initialLimit: server.db.policy.apiLimitPerReset)
59+
return .init(github: user, initialLimit: server.db.policy.apiLimitPerReset)
6060
}
6161

6262
let session:Mongo.Session = try await .init(from: server.db.sessions)

Sources/UnidocServer/Server/Unidoc.Server.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ extension Unidoc.Server
101101

102102
// Create the machine user, if it doesn’t exist. Don’t store the cookie, since we
103103
// want to be able to change it without restarting the server.
104-
let _:Unidoc.UserSecrets = try await self.db.users.update(user: .machine(0),
104+
let _:Unidoc.UserSecrets = try await self.db.users.update(user: .init(machine: 0),
105105
with: session)
106106
}
107107

Sources/UnidocUI/Endpoints/User/Unidoc.User (ext).swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,12 @@ extension Unidoc.User
99
self.id.github.map { "https://avatars.githubusercontent.com/u/\($0)?s=\(size)" }
1010
}
1111

12-
func card() -> Unidoc.UserCard<Never>
13-
{
14-
self.card(some: nil)
15-
}
16-
1712
func card(
1813
tools:Unidoc.RulesPage.EditorTools) -> Unidoc.UserCard<Unidoc.RulesPage.EditorTools>
1914
{
2015
self.card(some: tools)
2116
}
2217

23-
private
2418
func card<Tools>(some tools:Tools?) -> Unidoc.UserCard<Tools>
2519
{
2620
.init(id: self.id,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import HTML
2+
3+
extension Unidoc.UserSettingsPage
4+
{
5+
struct Installation
6+
{
7+
let id:Int32?
8+
9+
init(id:Int32?)
10+
{
11+
self.id = id
12+
}
13+
}
14+
}
15+
extension Unidoc.UserSettingsPage.Installation:HTML.OutputStreamable
16+
{
17+
static
18+
func += (div:inout HTML.ContentEncoder, self:Self)
19+
{
20+
if let id:Int32 = self.id
21+
{
22+
div[.a]
23+
{
24+
$0.target = "_blank"
25+
$0.href = "https://github.com/settings/installations/\(id)"
26+
$0.rel = .external
27+
} = "installation settings"
28+
}
29+
else
30+
{
31+
div[.span] { $0.class = "placeholder" } = "no installation"
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)