feat: add Idempotency-Key` header for status
This commit is contained in:
parent
3570c7108c
commit
d3c77ee6cf
|
@ -70,7 +70,7 @@ extension ComposeStatusPollItem {
|
||||||
hasher.combine(id)
|
hasher.combine(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ExpiresOption: Equatable, Hashable, CaseIterable {
|
enum ExpiresOption: String, Equatable, Hashable, CaseIterable {
|
||||||
case thirtyMinutes
|
case thirtyMinutes
|
||||||
case oneHour
|
case oneHour
|
||||||
case sixHours
|
case sixHours
|
||||||
|
|
|
@ -107,6 +107,8 @@ extension ComposeViewModel.PublishState {
|
||||||
return subscriptions
|
return subscriptions
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let idempotencyKey = viewModel.idempotencyKey.value
|
||||||
|
|
||||||
publishingSubscription = Publishers.MergeMany(updateMediaQuerySubscriptions)
|
publishingSubscription = Publishers.MergeMany(updateMediaQuerySubscriptions)
|
||||||
.collect()
|
.collect()
|
||||||
.flatMap { attachments -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> in
|
.flatMap { attachments -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> in
|
||||||
|
@ -122,6 +124,7 @@ extension ComposeViewModel.PublishState {
|
||||||
)
|
)
|
||||||
return viewModel.context.apiService.publishStatus(
|
return viewModel.context.apiService.publishStatus(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
query: query,
|
query: query,
|
||||||
mastodonAuthenticationBox: mastodonAuthenticationBox
|
mastodonAuthenticationBox: mastodonAuthenticationBox
|
||||||
)
|
)
|
||||||
|
|
|
@ -58,7 +58,10 @@ final class ComposeViewModel: NSObject {
|
||||||
}()
|
}()
|
||||||
private(set) lazy var publishStateMachinePublisher = CurrentValueSubject<PublishState?, Never>(nil)
|
private(set) lazy var publishStateMachinePublisher = CurrentValueSubject<PublishState?, Never>(nil)
|
||||||
private(set) var publishDate = Date() // update it when enter Publishing state
|
private(set) var publishDate = Date() // update it when enter Publishing state
|
||||||
|
|
||||||
|
// TODO: group post material into Hashable class
|
||||||
|
var idempotencyKey = CurrentValueSubject<String, Never>(UUID().uuidString)
|
||||||
|
|
||||||
// UI & UX
|
// UI & UX
|
||||||
let title: CurrentValueSubject<String, Never>
|
let title: CurrentValueSubject<String, Never>
|
||||||
let shouldDismiss = CurrentValueSubject<Bool, Never>(true)
|
let shouldDismiss = CurrentValueSubject<Bool, Never>(true)
|
||||||
|
@ -383,6 +386,56 @@ final class ComposeViewModel: NSObject {
|
||||||
self.isPollToolbarButtonEnabled.value = !shouldPollDisable
|
self.isPollToolbarButtonEnabled.value = !shouldPollDisable
|
||||||
})
|
})
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
// calculate `Idempotency-Key`
|
||||||
|
let content = Publishers.CombineLatest3(
|
||||||
|
composeStatusAttribute.isContentWarningComposing,
|
||||||
|
composeStatusAttribute.contentWarningContent,
|
||||||
|
composeStatusAttribute.composeContent
|
||||||
|
)
|
||||||
|
.map { isContentWarningComposing, contentWarningContent, composeContent -> String in
|
||||||
|
if isContentWarningComposing {
|
||||||
|
return contentWarningContent + (composeContent ?? "")
|
||||||
|
} else {
|
||||||
|
return composeContent ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let attachmentIDs = attachmentServices.map { attachments -> String in
|
||||||
|
let attachmentIDs = attachments.compactMap { $0.attachment.value?.id }
|
||||||
|
return attachmentIDs.joined(separator: ",")
|
||||||
|
}
|
||||||
|
let pollOptionsAndDuration = Publishers.CombineLatest3(
|
||||||
|
isPollComposing,
|
||||||
|
pollOptionAttributes,
|
||||||
|
pollExpiresOptionAttribute.expiresOption
|
||||||
|
)
|
||||||
|
.map { isPollComposing, pollOptionAttributes, expiresOption -> String in
|
||||||
|
guard isPollComposing else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let pollOptions = pollOptionAttributes.map { $0.option.value }.joined(separator: ",")
|
||||||
|
return pollOptions + expiresOption.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
Publishers.CombineLatest4(
|
||||||
|
content,
|
||||||
|
attachmentIDs,
|
||||||
|
pollOptionsAndDuration,
|
||||||
|
selectedStatusVisibility
|
||||||
|
)
|
||||||
|
.map { content, attachmentIDs, pollOptionsAndDuration, selectedStatusVisibility -> String in
|
||||||
|
var hasher = Hasher()
|
||||||
|
hasher.combine(content)
|
||||||
|
hasher.combine(attachmentIDs)
|
||||||
|
hasher.combine(pollOptionsAndDuration)
|
||||||
|
hasher.combine(selectedStatusVisibility.visibility.rawValue)
|
||||||
|
let hashValue = hasher.finalize()
|
||||||
|
return "\(hashValue)"
|
||||||
|
}
|
||||||
|
.assign(to: \.value, on: idempotencyKey)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
|
@ -16,6 +16,7 @@ extension APIService {
|
||||||
|
|
||||||
func publishStatus(
|
func publishStatus(
|
||||||
domain: String,
|
domain: String,
|
||||||
|
idempotencyKey: String?,
|
||||||
query: Mastodon.API.Statuses.PublishStatusQuery,
|
query: Mastodon.API.Statuses.PublishStatusQuery,
|
||||||
mastodonAuthenticationBox: MastodonAuthenticationBox
|
mastodonAuthenticationBox: MastodonAuthenticationBox
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> {
|
||||||
|
@ -24,6 +25,7 @@ extension APIService {
|
||||||
return Mastodon.API.Statuses.publishStatus(
|
return Mastodon.API.Statuses.publishStatus(
|
||||||
session: session,
|
session: session,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
query: query,
|
query: query,
|
||||||
authorization: authorization
|
authorization: authorization
|
||||||
)
|
)
|
||||||
|
|
|
@ -55,9 +55,12 @@ final class SendPostIntentHandler: NSObject, SendPostIntentHandling {
|
||||||
spoilerText: nil,
|
spoilerText: nil,
|
||||||
visibility: visibility
|
visibility: visibility
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let idempotencyKey = UUID().uuidString
|
||||||
|
|
||||||
APIService.shared.publishStatus(
|
APIService.shared.publishStatus(
|
||||||
domain: box.domain,
|
domain: box.domain,
|
||||||
|
idempotencyKey: idempotencyKey,
|
||||||
query: query,
|
query: query,
|
||||||
mastodonAuthenticationBox: box
|
mastodonAuthenticationBox: box
|
||||||
)
|
)
|
||||||
|
|
|
@ -77,14 +77,18 @@ extension Mastodon.API.Statuses {
|
||||||
public static func publishStatus(
|
public static func publishStatus(
|
||||||
session: URLSession,
|
session: URLSession,
|
||||||
domain: String,
|
domain: String,
|
||||||
|
idempotencyKey: String?,
|
||||||
query: PublishStatusQuery,
|
query: PublishStatusQuery,
|
||||||
authorization: Mastodon.API.OAuth.Authorization?
|
authorization: Mastodon.API.OAuth.Authorization?
|
||||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> {
|
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Status>, Error> {
|
||||||
let request = Mastodon.API.post(
|
var request = Mastodon.API.post(
|
||||||
url: publishNewStatusEndpointURL(domain: domain),
|
url: publishNewStatusEndpointURL(domain: domain),
|
||||||
query: query,
|
query: query,
|
||||||
authorization: authorization
|
authorization: authorization
|
||||||
)
|
)
|
||||||
|
if let idempotencyKey = idempotencyKey {
|
||||||
|
request.setValue(idempotencyKey, forHTTPHeaderField: "Idempotency-Key")
|
||||||
|
}
|
||||||
return session.dataTaskPublisher(for: request)
|
return session.dataTaskPublisher(for: request)
|
||||||
.tryMap { data, response in
|
.tryMap { data, response in
|
||||||
let value = try Mastodon.API.decode(type: Mastodon.Entity.Status.self, from: data, response: response)
|
let value = try Mastodon.API.decode(type: Mastodon.Entity.Status.self, from: data, response: response)
|
||||||
|
|
|
@ -346,6 +346,7 @@ extension ShareViewModel {
|
||||||
)
|
)
|
||||||
return APIService.shared.publishStatus(
|
return APIService.shared.publishStatus(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
idempotencyKey: nil, // FIXME:
|
||||||
query: query,
|
query: query,
|
||||||
mastodonAuthenticationBox: mastodonAuthenticationBox
|
mastodonAuthenticationBox: mastodonAuthenticationBox
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue