From 5b300e9f0c5c3603d5aeb1563b58674459f9111a Mon Sep 17 00:00:00 2001 From: Andy Lin Date: Wed, 27 Nov 2024 13:33:19 +0000 Subject: [PATCH 1/3] adding api to do reposts --- .../RepostRecord/CreateRepostRecord.swift | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/CreateRepostRecord.swift b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/CreateRepostRecord.swift index b5a1cdb239..7995203ebb 100644 --- a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/CreateRepostRecord.swift +++ b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/CreateRepostRecord.swift @@ -9,4 +9,45 @@ import Foundation extension ATProtoBluesky { + /// Create a repost record to a user's post. + /// + /// - Parameters: + /// - strongReference: The URI of the record, which contains the `recordURI` and `cidHash`. + /// - createdAt: The date and time the like record was created. Defaults to `Date.now`. + /// - shouldValidate: Indicates whether the record should be validated. Optional. + /// Defaults to `true`. + /// - Returns: A + /// ``ComAtprotoLexicon/Repository/StrongReference`` + /// structure which represents the record that was successfully created. + public func createRepostRecord( + _ strongReference: ComAtprotoLexicon.Repository.StrongReference, + createdAt: Date = Date(), + shouldValidate: Bool? = true + ) async throws -> ComAtprotoLexicon.Repository.StrongReference { + guard let session else { throw ATRequestPrepareError.missingActiveSession } + + let repostRecord = AppBskyLexicon.Feed.RepostRecord( + subject: strongReference, + createdAt: createdAt + ) + + return try await atProtoKitInstance.createRecord( + repositoryDID: session.sessionDID, + collection: "app.bsky.feed.repost", + shouldValidate: shouldValidate, + record: UnknownType.record(repostRecord) + ) + } + + /// The request body for a repost record. + struct RepostRecordRequestBody: Encodable { + /// The repository for the repost record. + let repo: String + /// The Namespaced Identifiers (NSID) of the request body. + /// + /// - Warning: The value must not change. + let collection: String = "app.bsky.feed.repost" + /// The like record itself. + let record: AppBskyLexicon.Feed.RepostRecord + } } From 31a2680cfd4a80d71481fc418283909077b82d72 Mon Sep 17 00:00:00 2001 From: Andy Lin Date: Wed, 27 Nov 2024 15:09:51 +0000 Subject: [PATCH 2/3] adding convienience delete repost method --- .../RepostRecord/DeleteRepostRecord.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift index 5336762a47..961d302133 100644 --- a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift +++ b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift @@ -9,4 +9,14 @@ import Foundation extension ATProtoBluesky { + /// Deletes a repost record. + /// + /// This can also be used to validate if a repost record has been deleted. + /// - Parameter record: The record that needs to be deleted. + /// + /// This can be either the URI of the record, or the full record object itself. + public func deleteRepostRecord(_ record: RecordIdentifier) async throws { + // in testing, I have found that I can use the preexisting deleteLikeRecord function to delete a repost without any issues. + try await deleteLikeRecord(record) + } } From 04738f1b11621deeb091b6a8aa82365f4d29ca34 Mon Sep 17 00:00:00 2001 From: Andy Lin Date: Wed, 27 Nov 2024 23:49:01 +0000 Subject: [PATCH 3/3] delete like record function is now in a seperate function to make it clearer that two functions are sharing the same underpinning --- .../HelperFunctions/DeleteActionRecord.swift | 102 ++++++++++++++++++ .../LikeRecord/DeleteLikeRecord.swift | 83 +------------- .../RepostRecord/DeleteRepostRecord.swift | 3 +- 3 files changed, 104 insertions(+), 84 deletions(-) create mode 100644 Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/HelperFunctions/DeleteActionRecord.swift diff --git a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/HelperFunctions/DeleteActionRecord.swift b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/HelperFunctions/DeleteActionRecord.swift new file mode 100644 index 0000000000..67f35a4e5d --- /dev/null +++ b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/HelperFunctions/DeleteActionRecord.swift @@ -0,0 +1,102 @@ +// +// DeleteActionRecord.swift +// ATProtoKit +// +// Created by Andy Lin on 11/27/24. +// + +import Foundation + +extension ATProtoBluesky { + + /// Deletes a record. + /// + /// This can also be used to validate if a record has been deleted. + /// - Parameter record: The record that needs to be deleted. + /// + /// This can be either the URI of the record, or the full record object itself. + internal func deleteActionRecord(_ record: RecordIdentifier) async throws { + guard let session else { + throw ATRequestPrepareError.missingActiveSession + } + + guard let sessionURL = session.pdsURL else { + throw ATRequestPrepareError.invalidRequestURL + } + + var actionRecord: ATProtoTools.RecordQuery? = nil + try await resolveRecordIdentifierToQuery(record, sessionURL, &actionRecord) + + let requestBody = actionRecord + + guard let repositoryDID = requestBody?.repository, + let actionCollection = requestBody?.collection, + let actionRecordKey = requestBody?.recordKey else { + throw ATRequestPrepareError.invalidRecord + } + + try await atProtoKitInstance.deleteRecord( + repositoryDID: repositoryDID, + collection: actionCollection, + recordKey: actionRecordKey, + swapRecord: requestBody?.recordCID + ) + } + + fileprivate func resolveRecordIdentifierToQuery(_ record: RecordIdentifier, _ sessionURL: String, + _ actionRecord: inout ATProtoTools.RecordQuery?) async throws { + switch record { + case .recordQuery(let recordQuery): + do { + // Perform the fetch and validation based on recordQuery. + let output = try await atProtoKitInstance.getRepositoryRecord(from: recordQuery.repository, + collection: recordQuery.collection, + recordKey: recordQuery.recordKey, + recordCID: recordQuery.recordCID, + pdsURL: sessionURL + ) + + let recordURI = "at://\(recordQuery.repository)/\(recordQuery.collection)/\(recordQuery.recordKey)" + + guard output.recordURI == recordURI else { + throw ATRequestPrepareError.invalidRecord + } + } catch { + throw error + } + + case .recordURI(let recordURI): + do { + // Perform the fetch and validation based on the parsed URI. + let parsedURI = try ATProtoTools().parseURI(recordURI) + let output = try await atProtoKitInstance.getRepositoryRecord(from: parsedURI.repository, + collection: parsedURI.collection, + recordKey: parsedURI.recordKey, + recordCID: parsedURI.recordCID, + pdsURL: sessionURL + ) + + guard recordURI == output.recordURI else { + throw ATRequestPrepareError.invalidRecord + } + + actionRecord = parsedURI + } catch { + throw error + } + } + } + + /// Identifies the record based on the specific information provided. + /// + /// `RecordIdentifier` provides a unified interface for specifying how the record is defined. + /// This allows methods like ``deleteActionRecord(_:)`` to handle the backend of how to grab the + /// details of the record so it can delete it. + public enum RecordIdentifier { + /// The record object itself. + /// - Parameter recordQuery: the record object. + case recordQuery(recordQuery: ATProtoTools.RecordQuery) + /// The URI of the record. + case recordURI(atURI: String) + } +} diff --git a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/LikeRecord/DeleteLikeRecord.swift b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/LikeRecord/DeleteLikeRecord.swift index 842b7557d0..d39b16eaec 100644 --- a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/LikeRecord/DeleteLikeRecord.swift +++ b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/LikeRecord/DeleteLikeRecord.swift @@ -16,87 +16,6 @@ extension ATProtoBluesky { /// /// This can be either the URI of the record, or the full record object itself. public func deleteLikeRecord(_ record: RecordIdentifier) async throws { - guard let session else { - throw ATRequestPrepareError.missingActiveSession - } - - guard let sessionURL = session.pdsURL else { - throw ATRequestPrepareError.invalidRequestURL - } - - var likeRecord: ATProtoTools.RecordQuery? = nil - try await resolveRecordIdentifierToQuery(record, sessionURL, &likeRecord) - - let requestBody = likeRecord - - guard let repositoryDID = requestBody?.repository, - let likeCollection = requestBody?.collection, - let likeRecordKey = requestBody?.recordKey else { - throw ATRequestPrepareError.invalidRecord - } - - try await atProtoKitInstance.deleteRecord( - repositoryDID: repositoryDID, - collection: likeCollection, - recordKey: likeRecordKey, - swapRecord: requestBody?.recordCID - ) - } - - fileprivate func resolveRecordIdentifierToQuery(_ record: RecordIdentifier, _ sessionURL: String, - _ likeRecord: inout ATProtoTools.RecordQuery?) async throws { - switch record { - case .recordQuery(let recordQuery): - do { - // Perform the fetch and validation based on recordQuery. - let output = try await atProtoKitInstance.getRepositoryRecord(from: recordQuery.repository, - collection: recordQuery.collection, - recordKey: recordQuery.recordKey, - recordCID: recordQuery.recordCID, - pdsURL: sessionURL - ) - - let recordURI = "at://\(recordQuery.repository)/\(recordQuery.collection)/\(recordQuery.recordKey)" - - guard output.recordURI == recordURI else { - throw ATRequestPrepareError.invalidRecord - } - } catch { - throw error - } - - case .recordURI(let recordURI): - do { - // Perform the fetch and validation based on the parsed URI. - let parsedURI = try ATProtoTools().parseURI(recordURI) - let output = try await atProtoKitInstance.getRepositoryRecord(from: parsedURI.repository, - collection: parsedURI.collection, - recordKey: parsedURI.recordKey, - recordCID: parsedURI.recordCID, - pdsURL: sessionURL - ) - - guard recordURI == output.recordURI else { - throw ATRequestPrepareError.invalidRecord - } - - likeRecord = parsedURI - } catch { - throw error - } - } - } - - /// Identifies the record based on the specific information provided. - /// - /// `RecordIdentifier` provides a unified interface for specifying how the record is defined. - /// This allows methods like ``deleteLikeRecord(_:)`` to handle the backend of how to grab the - /// details of the record so it can delete it. - public enum RecordIdentifier { - /// The record object itself. - /// - Parameter recordQuery: the record object. - case recordQuery(recordQuery: ATProtoTools.RecordQuery) - /// The URI of the record. - case recordURI(atURI: String) + return try await deleteActionRecord(record) } } diff --git a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift index 961d302133..3b52729012 100644 --- a/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift +++ b/Sources/ATProtoKit/APIReference/ATProtoBlueskyAPI/RepostRecord/DeleteRepostRecord.swift @@ -16,7 +16,6 @@ extension ATProtoBluesky { /// /// This can be either the URI of the record, or the full record object itself. public func deleteRepostRecord(_ record: RecordIdentifier) async throws { - // in testing, I have found that I can use the preexisting deleteLikeRecord function to delete a repost without any issues. - try await deleteLikeRecord(record) + try await deleteActionRecord(record) } }