diff --git a/README.md b/README.md index f64a1c4..8ba25d0 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ OPTIONS: Path to the openssl executable, this is used to generate CSR signing artifacts that are required when creating certificates --intermediary-apple-certificates Intermediary Apple Certificates that should also be added to the keychain (https://www.apple.com/certificateauthority/) + --profile-name + The name that you would like to assign to the created provisioning profile (optional) --certificate-signing-request-subject Subject for the Certificate Signing Request when creating certificates. diff --git a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift index ad8b25b..eae3027 100644 --- a/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift +++ b/Sources/SignHereLibrary/Commands/CreateProvisioningProfileCommand.swift @@ -121,6 +121,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { case opensslPath = "opensslPath" case intermediaryAppleCertificates = "intermediaryAppleCertificates" case certificateSigningRequestSubject = "certificateSigningRequestSubject" + case profileName = "profileName" } @Option(help: "The key identifier of the private key (https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)") @@ -165,6 +166,9 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { @Option(help: "Intermediary Apple Certificates that should also be added to the keychain (https://www.apple.com/certificateauthority/)") internal var intermediaryAppleCertificates: [String] = [] + @Option(help: "The name that you would like to assign to the created provisioning profile (optional)") + internal var profileName: String? + @Option(help: """ Subject for the Certificate Signing Request when creating certificates. @@ -223,7 +227,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { intermediaryAppleCertificates: [String], certificateSigningRequestSubject: String, bundleIdentifierName: String?, - platform: String + platform: String, + profileName: String? ) { self.files = files self.log = log @@ -246,6 +251,7 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { self.certificateSigningRequestSubject = certificateSigningRequestSubject self.bundleIdentifierName = bundleIdentifierName self.platform = platform + self.profileName = profileName } internal init(from decoder: Decoder) throws { @@ -279,7 +285,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { intermediaryAppleCertificates: try container.decodeIfPresent([String].self, forKey: .intermediaryAppleCertificates) ?? [], certificateSigningRequestSubject: try container.decode(String.self, forKey: .certificateSigningRequestSubject), bundleIdentifierName: try container.decodeIfPresent(String.self, forKey: .bundleIdentifierName), - platform: try container.decode(String.self, forKey: .platform) + platform: try container.decode(String.self, forKey: .platform), + profileName: try container.decodeIfPresent(String.self, forKey: .profileName) ) } @@ -315,7 +322,8 @@ internal struct CreateProvisioningProfileCommand: ParsableCommand { ), certificateId: certificateId, deviceIDs: deviceIDs, - profileType: profileType + profileType: profileType, + profileName: profileName ) guard let profileData: Data = .init(base64Encoded: profileResponse.data.attributes.profileContent) else { diff --git a/Sources/SignHereLibrary/Services/iTunesConnectService.swift b/Sources/SignHereLibrary/Services/iTunesConnectService.swift index 68e3337..4180f1b 100644 --- a/Sources/SignHereLibrary/Services/iTunesConnectService.swift +++ b/Sources/SignHereLibrary/Services/iTunesConnectService.swift @@ -35,7 +35,8 @@ internal protocol iTunesConnectService { bundleId: String, certificateId: String, deviceIDs: Set, - profileType: String + profileType: String, + profileName: String? ) throws -> CreateProfileResponse func deleteProvisioningProfile( jsonWebToken: String, @@ -352,7 +353,8 @@ internal class iTunesConnectServiceImp: iTunesConnectService { bundleId: String, certificateId: String, deviceIDs: Set, - profileType: String + profileType: String, + profileName: String? = nil ) throws -> CreateProfileResponse { let urlString: String = "https://api.appstoreconnect.apple.com/v1/profiles" guard let url: URL = .init(string: urlString) @@ -364,7 +366,7 @@ internal class iTunesConnectServiceImp: iTunesConnectService { request.setValue(Constants.applicationJSONHeaderValue, forHTTPHeaderField: Constants.contentTypeHeaderName) request.setValue("Bearer \(jsonWebToken)", forHTTPHeaderField: "Authorization") request.httpMethod = "POST" - let profileName: String = "\(certificateId)_\(profileType)_\(clock.now().timeIntervalSince1970)" + let profileName = profileName ?? "\(certificateId)_\(profileType)_\(clock.now().timeIntervalSince1970)" var devices: CreateProfileRequest.CreateProfileRequestData.Relationships.Devices? = nil // ME: App Store profiles cannot use UDIDs if !["IOS_APP_STORE", "MAC_APP_STORE", "TVOS_APP_STORE", "MAC_CATALYST_APP_STORE"].contains(profileType) { diff --git a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift index 0974396..de10048 100644 --- a/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift +++ b/Tests/SignHereLibraryTests/CreateProvisioningProfileCommandTests.swift @@ -56,7 +56,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { intermediaryAppleCertificates: ["/intermediaryAppleCertificate"], certificateSigningRequestSubject: "certificateSigningRequestSubject", bundleIdentifierName: "bundleIdentifierName", - platform: "platform" + platform: "platform", + profileName: "profileName" ) isRecording = false } @@ -160,7 +161,8 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { "opensslPath": "/opensslPath", "certificateSigningRequestSubject": "certificateSigningRequestSubject", "bundleIdentifierName": "bundleIdentifierName", - "platform": "platform" + "platform": "platform", + "profileName": "profileName" } """.utf8) @@ -180,6 +182,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { XCTAssertEqual(subject.outputPath, "/outputPath") XCTAssertEqual(subject.bundleIdentifierName, "bundleIdentifierName") XCTAssertEqual(subject.platform, "platform") + XCTAssertEqual(subject.profileName, "profileName") } func test_execute_alreadyActiveCertificate() throws { @@ -210,7 +213,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createCertificateHandler = { _, _, _ in self.createCreateCertificateResponse() } - iTunesConnectService.createProfileHandler = { _, _, _, _, _ in + iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } @@ -253,7 +256,7 @@ final class CreateProvisioningProfileCommandTests: XCTestCase { iTunesConnectService.createCertificateHandler = { _, _, _ in self.createCreateCertificateResponse() } - iTunesConnectService.createProfileHandler = { _, _, _, _, _ in + iTunesConnectService.createProfileHandler = { _, _, _, _, _, _ in self.createCreateProfileResponse() } diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.1.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.1.txt new file mode 100644 index 0000000..4840d90 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.1.txt @@ -0,0 +1,7 @@ +curl \ + --request POST \ + --header "Accept: application/json" \ + --header "Authorization: Bearer jsonWebToken" \ + --header "Content-Type: application/json" \ + --data "{\"data\":{\"attributes\":{\"name\":\"mySpecialProfile\",\"profileType\":\"profileType\"},\"type\":\"profiles\",\"relationships\":{\"devices\":{\"data\":[{\"id\":\"deviceId\",\"type\":\"devices\"}]},\"certificates\":{\"data\":[{\"id\":\"certificateId\",\"type\":\"certificates\"}]},\"bundleId\":{\"data\":{\"id\":\"bundleId\",\"type\":\"bundleIds\"}}}}}" \ + "https://api.appstoreconnect.apple.com/v1/profiles" \ No newline at end of file diff --git a/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt new file mode 100644 index 0000000..f9a01d1 --- /dev/null +++ b/Tests/SignHereLibraryTests/__Snapshots__/iTunesConnectServiceTests/iTunesConnectServiceTests_test_createProfile_withProfileName.2.txt @@ -0,0 +1,13 @@ +▿ CreateProfileResponse + ▿ data: CreateProfileResponseData + ▿ attributes: Attributes + - createdDate: 1970-01-01T00:00:00Z + - expirationDate: 1970-01-01T00:01:40Z + - name: "createdProfileName" + - platform: "platform" + - profileContent: "dGVzdAo=" + - profileState: "profileState" + - profileType: "profileType" + - uuid: "uuid" + - id: "createdProfileITCID" + - type: "type" diff --git a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift index 08a10da..47fc880 100644 --- a/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift +++ b/Tests/SignHereLibraryTests/iTunesConnectServiceTests.swift @@ -567,7 +567,38 @@ final class iTunesConnectServiceTests: XCTestCase { bundleId: "bundleId", certificateId: "certificateId", deviceIDs: .init(["deviceId"]), - profileType: "profileType" + profileType: "profileType", + profileName: nil + ) + + // THEN + for argValue in network.executeArgValues { + assertSnapshot(matching: argValue, as: .curl) + } + assertSnapshot( + matching: value, + as: .dump + ) + } + + func test_createProfile_withProfileName() throws { + // GIVEN + let jsonEncoder: JSONEncoder = createJSONEncoder() + var networkExecutes: [Data] = [ + try jsonEncoder.encode(createCreateProfileResponse()), + ] + network.executeHandler = { _ in + return networkExecutes.removeFirst() + } + + // WHEN + let value: CreateProfileResponse = try subject.createProfile( + jsonWebToken: "jsonWebToken", + bundleId: "bundleId", + certificateId: "certificateId", + deviceIDs: .init(["deviceId"]), + profileType: "profileType", + profileName: "mySpecialProfile" ) // THEN @@ -596,7 +627,8 @@ final class iTunesConnectServiceTests: XCTestCase { bundleId: "bundleId", certificateId: "certificateId", deviceIDs: .init(["deviceId"]), - profileType: "IOS_APP_STORE" + profileType: "IOS_APP_STORE", + profileName: nil ) // THEN @@ -624,7 +656,8 @@ final class iTunesConnectServiceTests: XCTestCase { bundleId: "bundleId", certificateId: "certificateId", deviceIDs: .init(["deviceId"]), - profileType: "profileType" + profileType: "profileType", + profileName: nil )) { if case iTunesConnectServiceImp.Error.unableToDecodeResponse = $0 { return