Skip to content

Commit efc1ed9

Browse files
authored
Fix duplicate App ID registration (#90)
We pull the list of App IDs in `upsertApp` to see if we already created the App ID. This broke when the user had too many App IDs, since DS returned a paginated response and we only looked at the first page. We could paginate the whole list (as seen in #89) but that's slow. Instead, just filter the query to the expected App ID. This is a prefix search so we might still receive multiple responses, but it's much less likely that the user has like 10(? I'm not sure what the default/max pagination limit is) apps registered with the XTL-ABC.com.foo.bar prefix (they could have PlugIns but usually just a small number of them.) This is a tentative fix for #87.
1 parent 693e60f commit efc1ed9

2 files changed

Lines changed: 19 additions & 25 deletions

File tree

Sources/XKit/DeveloperServices/App IDs/DeveloperServicesAddAppOperation.swift

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,22 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation {
4141
private func upsertApp(
4242
bundleID: String,
4343
entitlements: Entitlements,
44-
isFreeTeam: Bool,
45-
appIDs: [String: Components.Schemas.BundleId]
44+
isFreeTeam: Bool
4645
) async throws -> Components.Schemas.BundleId {
46+
let newBundleID = ProvisioningIdentifiers.identifier(fromSanitized: bundleID, context: self.context)
47+
48+
let existing = try await context.developerAPIClient
49+
.bundleIdsGetCollection(query: .init(
50+
filter_lbrack_identifier_rbrack_: [bundleID]
51+
))
52+
.ok.body.json.data
53+
// filter[identifier] is a prefix filter so we need to manually upgrade to equality
54+
.first(where: { $0.attributes?.identifier == newBundleID })
55+
4756
let appID: Components.Schemas.BundleId
48-
if let existing = appIDs[bundleID] {
57+
if let existing {
4958
appID = existing
5059
} else {
51-
let newBundleID = ProvisioningIdentifiers.identifier(fromSanitized: bundleID, context: self.context)
5260
let name = ProvisioningIdentifiers.appName(fromSanitized: bundleID)
5361
let createResponse = try await context.developerAPIClient.bundleIdsCreateInstance(
5462
body: .json(
@@ -150,8 +158,7 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation {
150158
/// that the app has).
151159
private func addApp(
152160
_ app: URL,
153-
isFreeTeam: Bool,
154-
appIDs: [String: Components.Schemas.BundleId]
161+
isFreeTeam: Bool
155162
) async throws -> ProvisioningInfo {
156163
let infoURL = app.appendingPathComponent("Info.plist")
157164
guard let data = try? Data(contentsOf: infoURL),
@@ -179,7 +186,7 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation {
179186
entitlements = try Entitlements(entitlements: [])
180187
}
181188

182-
let appID = try await upsertApp(bundleID: bundleID, entitlements: entitlements, isFreeTeam: isFreeTeam, appIDs: appIDs)
189+
let appID = try await upsertApp(bundleID: bundleID, entitlements: entitlements, isFreeTeam: isFreeTeam)
183190
let newBundleID = appID.attributes!.identifier!
184191

185192
let teamID = try signingInfo.certificate.teamID()
@@ -225,17 +232,6 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation {
225232
)
226233
}
227234

228-
// keyed by sanitized bundle ID
229-
private func getCurrentAppIDs() async throws -> [String: Components.Schemas.BundleId] {
230-
let bundleIDs = try await context.developerAPIClient.bundleIdsGetCollection().ok.body.json.data
231-
let keyedIDs = bundleIDs
232-
.compactMap { id -> (String, Components.Schemas.BundleId)? in
233-
guard let bundleID = id.attributes?.identifier else { return nil }
234-
return (ProvisioningIdentifiers.sanitize(identifier: bundleID), id)
235-
}
236-
return Dictionary(keyedIDs, uniquingKeysWith: { $1 })
237-
}
238-
239235
/// Registers the app + its extensions, returning the profile and entitlements of each
240236
public func perform() async throws -> [URL: ProvisioningInfo] {
241237
var apps: [URL] = [root]
@@ -244,17 +240,15 @@ public struct DeveloperServicesAddAppOperation: DeveloperServicesOperation {
244240
apps += plugins.implicitContents.filter { $0.pathExtension.lowercased() == "appex" }
245241
}
246242

247-
async let isFreeTeamTask = context.auth.team()?.isFree == true
248-
async let appIDsTask = getCurrentAppIDs()
249-
let (isFreeTeam, appIDs) = try await (isFreeTeamTask, appIDsTask)
243+
let isFreeTeam = try await context.auth.team()?.isFree == true
250244

251245
return try await withThrowingTaskGroup(
252246
of: (URL, ProvisioningInfo).self,
253247
returning: [URL: ProvisioningInfo].self
254248
) { group in
255249
for app in apps {
256250
group.addTask {
257-
let info = try await addApp(app, isFreeTeam: isFreeTeam, appIDs: appIDs)
251+
let info = try await addApp(app, isFreeTeam: isFreeTeam)
258252
return (app, info)
259253
}
260254
}

Sources/XKit/Utilities/ProvisioningIdentifiers.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ extension ProvisioningIdentifiers {
2121
return identifier.split(separator: ".").dropFirst().joined(separator: ".")
2222
}
2323

24-
static func identifier(fromSanitized sanitized: String, context: SigningContext? = nil) -> String {
25-
let uuid = (context?.auth.identityID ?? UUID().uuidString).split(separator: "-")[0].uppercased()
24+
static func identifier(fromSanitized sanitized: String, context: SigningContext) -> String {
25+
let uuid = context.auth.identityID.split(separator: "-")[0].uppercased()
2626
return "\(Self.idPrefix)\(uuid).\(sanitized)"
2727
}
2828

@@ -36,7 +36,7 @@ extension ProvisioningIdentifiers {
3636
return sanitize(identifier: id)
3737
}
3838

39-
static func groupID(fromSanitized sanitized: String, context: SigningContext? = nil) -> DeveloperServicesAppGroup.GroupID {
39+
static func groupID(fromSanitized sanitized: String, context: SigningContext) -> DeveloperServicesAppGroup.GroupID {
4040
.init(rawValue: "\(Self.groupPrefix)\(identifier(fromSanitized: sanitized, context: context))")
4141
}
4242

0 commit comments

Comments
 (0)