@ -26,13 +26,13 @@ public enum OpenGroupAPI {
// / - M e s s a g e s ( i n c l u d e s a d d i t i o n s a n d d e l e t i o n s )
// / - I n b o x f o r t h e s e r v e r
// / - O u t b o x f o r t h e s e r v e r
public static func p oll(
public static func p reparedP oll(
_ db : Database ,
server : String ,
hasPerformedInitialPoll : Bool ,
timeSinceLastPoll : TimeInterval ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( info : ResponseInfoType , data : [ Endpoint : Codable ] ) , Error > {
) throws -> PreparedSendData < BatchResponse > {
let lastInboxMessageId : Int64 = ( try ? OpenGroup
. select ( . inboxLatestMessageId )
. filter ( OpenGroup . Columns . server = = server )
@ -51,26 +51,23 @@ public enum OpenGroupAPI {
. asRequest ( of : Capability . Variant . self )
. fetchSet ( db ) )
. defaulting ( to : [ ] )
let openGroupRooms : [ OpenGroup ] = ( try ? OpenGroup
. filter ( OpenGroup . Columns . server = = server . lowercased ( ) ) // N o t e : T h e ` O p e n G r o u p ` t y p e c o n v e r t s t o l o w e r c a s e i n i n i t
. filter ( OpenGroup . Columns . isActive = = true )
. filter ( OpenGroup . Columns . roomToken != " " )
. fetchAll ( db ) )
. defaulting ( to : [ ] )
// G e n e r a t e t h e r e q u e s t s
let requestResponseType : [ BatchRequest . Info ] = [
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . capabilities
) ,
responseType : Capabilities . self
let preparedRequests : [ ErasedPreparedSendData ] = [
try preparedCapabilities (
db ,
server : server ,
using : dependencies
)
]
. appending (
] . appending (
// P e r - r o o m r e q u e s t s
contentsOf : ( try ? OpenGroup
. filter ( OpenGroup . Columns . server = = server . lowercased ( ) ) // N o t e : T h e ` O p e n G r o u p ` t y p e c o n v e r t s t o l o w e r c a s e i n i n i t
. filter ( OpenGroup . Columns . isActive = = true )
. filter ( OpenGroup . Columns . roomToken != " " )
. fetchAll ( db ) )
. defaulting ( to : [ ] )
. flatMap { openGroup -> [ BatchRequest . Info ] in
contentsOf : try openGroupRooms
. flatMap { openGroup -> [ ErasedPreparedSendData ] in
let shouldRetrieveRecentMessages : Bool = (
openGroup . sequenceNumber = = 0 || (
// I f i t ' s t h e f i r s t p o l l f o r t h i s l a u n c h a n d i t ' s b e e n l o n g e r t h a n
@ -82,26 +79,27 @@ public enum OpenGroupAPI {
)
return [
BatchRequest . Info(
request: Request < NoBody , Endpoint > (
server : server ,
endpoint : . roomPollInfo ( openGroup . roomToken , openGroup . infoUpdates )
) ,
responseType: RoomPollInfo . self
try preparedRoomPoll Info(
db,
lastUpdated : openGroup . infoUpdates ,
for : openGroup . roomToken ,
on : openGroup . server ,
using: dependencies
) ,
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : ( shouldRetrieveRecentMessages ?
. roomMessagesRecent ( openGroup . roomToken ) :
. roomMessagesSince ( openGroup . roomToken , seqNo : openGroup . sequenceNumber )
) ,
queryParameters : [
. updateTypes : UpdateTypes . reaction . rawValue ,
. reactors : " 5 "
]
) ,
responseType : [ Failable < Message > ] . self
( shouldRetrieveRecentMessages ?
try preparedRecentMessages (
db ,
in : openGroup . roomToken ,
on : openGroup . server ,
using : dependencies
) :
try preparedMessagesSince (
db ,
seqNo : openGroup . sequenceNumber ,
in : openGroup . roomToken ,
on : openGroup . server ,
using : dependencies
)
)
]
}
@ -112,83 +110,73 @@ public enum OpenGroupAPI {
! capabilities . contains ( . blind ) ? [ ] :
[
// I n b o x
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : ( lastInboxMessageId = = 0 ?
. inbox :
. inboxSince ( id : lastInboxMessageId )
)
) ,
responseType : [ DirectMessage ] ? . self // ' i n b o x S i n c e ' w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s
( lastInboxMessageId = = 0 ?
try preparedInbox ( db , on : server , using : dependencies ) :
try preparedInboxSince ( db , id : lastInboxMessageId , on : server , using : dependencies )
) ,
// O u t b o x
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : ( lastOutboxMessageId = = 0 ?
. outbox :
. outboxSince ( id : lastOutboxMessageId )
)
) ,
responseType : [ DirectMessage ] ? . self // ' o u t b o x S i n c e ' w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s
)
( lastOutboxMessageId = = 0 ?
try preparedOutbox ( db , on : server , using : dependencies ) :
try preparedOutboxSince ( db , id : lastOutboxMessageId , on : server , using : dependencies )
) ,
]
)
)
return OpenGroupAPI . batch ( db , server : server , requests : requestResponseType , using : dependencies )
return try OpenGroupAPI . preparedBatch (
db ,
server : server ,
requests : preparedRequests ,
using : dependencies
)
}
// / S u b m i t s m u l t i p l e r e q u e s t s w r a p p e d u p i n a s i n g l e r e q u e s t , r u n s t h e m a l l , t h e n r e t u r n s t h e r e s u l t o f e a c h o n e
// /
// / R e q u e s t s a r e p e r f o r m e d i n d e p e n d e n t l y , t h a t i s , i f o n e f a i l s t h e o t h e r s w i l l s t i l l b e a t t e m p t e d - t h e r e i s n o g u a r a n t e e o n t h e o r d e r i n w h i c h r e q u e s t s w i l l b e
// / ca r r i e d o u t ( f o r s e q u e n t i a l , r e l a t e d r e q u e s t s i n v o k e v i a ` / s e q u e n c e ` i n s t e a d )
// / R e q u e s t s a r e p e r f o r m e d i n d e p e n d e n t l y , t h a t i s , i f o n e f a i l s t h e o t h e r s w i l l s t i l l b e a t t e m p t e d - t h e r e i s n o g u a r a n t e e o n t h e o r d e r i n w h i c h
// / re q u e s t s w i l l b e ca r r i e d o u t ( f o r s e q u e n t i a l , r e l a t e d r e q u e s t s i n v o k e v i a ` / s e q u e n c e ` i n s t e a d )
// /
// / F o r c o n t a i n e d s u b r e q u e s t s t h a t s p e c i f y a b o d y ( i . e . P O S T o r P U T r e q u e s t s ) e x a c t l y o n e o f ` j s o n ` , ` b 6 4 ` , o r ` b y t e s ` m u s t b e p r o v i d e d w i t h t h e r e q u e s t b o d y .
private static func batch (
// / F o r c o n t a i n e d s u b r e q u e s t s t h a t s p e c i f y a b o d y ( i . e . P O S T o r P U T r e q u e s t s ) e x a c t l y o n e o f ` j s o n ` , ` b 6 4 ` , o r ` b y t e s ` m u s t b e p r o v i d e d
// / w i t h t h e r e q u e s t b o d y .
private static func preparedBatch (
_ db : Database ,
server : String ,
requests : [ BatchRequest. Info ] ,
requests : [ ErasedPreparedSendData ] ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( info : ResponseInfoType , data : [ Endpoint : Codable ] ) , Error > {
let responseTypes = requests . map { $0 . responseType }
return OpenGroupAPI
. send (
) throws -> PreparedSendData < BatchResponse > {
return try OpenGroupAPI
. prepareSendData (
db ,
request : Request (
method : . post ,
server : server ,
endpoint : Endpoint . batch ,
endpoint : . batch ,
body : BatchRequest ( requests : requests )
) ,
responseType : BatchResponse . self ,
using : dependencies
)
. decoded ( as : responseTypes , using : dependencies )
. map ( requests : requests , toHashMapFor : Endpoint . self )
}
// / T h i s i s l i k e ` / b a t c h ` , e x c e p t t h a t i t g u a r a n t e e s t o p e r f o r m r e q u e s t s s e q u e n t i a l l y i n t h e o r d e r p r o v i d e d a n d w i l l s t o p p r o c e s s i n g r e q u e s t s i f t h e p r e v i o u s r e q u e s t
// / re t u r n e d a n o n - ` 2 x x ` r e s p o n s e
// / T h i s i s l i k e ` / b a t c h ` , e x c e p t t h a t i t g u a r a n t e e s t o p e r f o r m r e q u e s t s s e q u e n t i a l l y i n t h e o r d e r p r o v i d e d a n d w i l l s t o p p r o c e s s i n g r e q u e s t s
// / if t h e p r e v i o u s r e q u e s t re t u r n e d a n o n - ` 2 x x ` r e s p o n s e
// /
// / F o r e x a m p l e , t h i s c a n b e u s e d t o b a n a n d d e l e t e a l l o f a u s e r ' s m e s s a g e s b y s e q u e n c i n g t h e b a n f o l l o w e d b y t h e ` d e l e t e _ a l l ` : i f t h e b a n f a i l s ( e . g . b e c a u s e
// / pe r m i s s i o n i s d e n i e d ) t h e n t h e ` d e l e t e _ a l l ` w i l l n o t o c c u r . T h e b a t c h b o d y a n d r e s p o n s e a r e i d e n t i c a l t o t h e ` / b a t c h ` e n d p o i n t ; r e q u e s t s t h a t a r e n o t
// / ca r r i e d o u t b e c a u s e o f a n e a r l i e r f a i l u r e w i l l h a v e a r e s p o n s e c o d e o f ` 4 1 2 ` ( P r e c o n d i t i o n F a i l e d ) . "
// / F o r e x a m p l e , t h i s c a n b e u s e d t o b a n a n d d e l e t e a l l o f a u s e r ' s m e s s a g e s b y s e q u e n c i n g t h e b a n f o l l o w e d b y t h e ` d e l e t e _ a l l ` : i f t h e
// / ba n f a i l s ( e . g . b e c a u s e pe r m i s s i o n i s d e n i e d ) t h e n t h e ` d e l e t e _ a l l ` w i l l n o t o c c u r . T h e b a t c h b o d y a n d r e s p o n s e a r e i d e n t i c a l t o t h e
// / `/ b a t c h ` e n d p o i n t ; r e q u e s t s t h a t a r e n o t ca r r i e d o u t b e c a u s e o f a n e a r l i e r f a i l u r e w i l l h a v e a r e s p o n s e c o d e o f ` 4 1 2 ` ( P r e c o n d i t i o n F a i l e d ) . "
// /
// / L i k e ` / b a t c h ` , r e s p o n s e s a r e r e t u r n e d i n t h e s a m e o r d e r a s r e q u e s t s , b u t u n l i k e ` / b a t c h ` t h e r e m a y b e f e w e r e l e m e n t s i n t h e r e s p o n s e l i s t ( i f r e q u e s t s w e r e
// / s t o p p e d b e c a u s e o f a n o n - 2 x x r e s p o n s e ) - I n s u c h a c a s e , t h e f i n a l , n o n - 2 x x r e s p o n s e i s s t i l l i n c l u d e d a s t h e f i n a l r e s p o n s e v a l u e
private static func sequence (
// / L i k e ` / b a t c h ` , r e s p o n s e s a r e r e t u r n e d i n t h e s a m e o r d e r a s r e q u e s t s , b u t u n l i k e ` / b a t c h ` t h e r e m a y b e f e w e r e l e m e n t s i n t h e r e s p o n s e
// / l i s t ( i f r e q u e s t s w e r e s t o p p e d b e c a u s e o f a n o n - 2 x x r e s p o n s e ) - I n s u c h a c a s e , t h e f i n a l , n o n - 2 x x r e s p o n s e i s s t i l l i n c l u d e d a s t h e f i n a l
// / r e s p o n s e v a l u e
private static func preparedSequence (
_ db : Database ,
server : String ,
requests : [ BatchRequest. Info ] ,
requests : [ ErasedPreparedSendData ] ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( info : ResponseInfoType , data : [ Endpoint : Codable ] ) , Error > {
let responseTypes = requests . map { $0 . responseType }
return OpenGroupAPI
. send (
) throws -> PreparedSendData < BatchResponse > {
return try OpenGroupAPI
. prepareSendData (
db ,
request : Request (
method : . post ,
@ -196,18 +184,17 @@ public enum OpenGroupAPI {
endpoint : Endpoint . sequence ,
body : BatchRequest ( requests : requests )
) ,
responseType : BatchResponse . self ,
using : dependencies
)
. decoded ( as : responseTypes , using : dependencies )
. map ( requests : requests , toHashMapFor : Endpoint . self )
}
// MARK: - C a p a b i l i t i e s
// / R e t u r n t h e l i s t o f s e r v e r f e a t u r e s / c a p a b i l i t i e s
// /
// / O p t i o n a l l y t a k e s a ` r e q u i r e d ` p a r a m e t e r c o n t a i n i n g a c o m m a - s e p a r a t e d l i s t o f c a p a b i l i t e s ; i f a n y a r e n o t s a t i s f i e d a 4 1 2 ( P r e c o n d i t i o n F a i l e d ) r e s p o n s e
// / wi l l b e r e t u r n e d w i t h m i s s i n g r e q u e s t e d c a p a b i l i t i e s i n t h e ` m i s s i n g ` k e y
// / O p t i o n a l l y t a k e s a ` r e q u i r e d ` p a r a m e t e r c o n t a i n i n g a c o m m a - s e p a r a t e d l i s t o f c a p a b i l i t e s ; i f a n y a r e n o t s a t i s f i e d a 4 1 2 ( P r e c o n d i t i o n F a i l e d )
// / re s p o n s e wi l l b e r e t u r n e d w i t h m i s s i n g r e q u e s t e d c a p a b i l i t i e s i n t h e ` m i s s i n g ` k e y
// /
// / E g . ` G E T / c a p a b i l i t i e s ` c o u l d r e t u r n ` { " c a p a b i l i t i e s " : [ " s o g s " , " b a t c h " ] } ` ` G E T / c a p a b i l i t i e s ? r e q u i r e d = m a g i c , b a t c h `
// / c o u l d r e t u r n : ` { " c a p a b i l i t i e s " : [ " s o g s " , " b a t c h " ] , " m i s s i n g " : [ " m a g i c " ] } `
@ -253,11 +240,6 @@ public enum OpenGroupAPI {
}
// / R e t u r n s t h e d e t a i l s o f a s i n g l e r o o m
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e a r o o m s o s h o u l d o n l y b e c a l l e d f r o m e i t h e r t h e ` p o l l ( ) ` o r ` j o i n R o o m ( ) ` m e t h o d s , i n o r d e r t o c a l l
// / t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e P o l l I n f o `
// / m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
public static func preparedRoom (
_ db : Database ,
for roomToken : String ,
@ -280,11 +262,6 @@ public enum OpenGroupAPI {
// /
// / T h e e n d p o i n t p o l l s r o o m m e t a d a t a f o r t h i s r o o m , a l w a y s i n c l u d i n g t h e i n s t a n t a n e o u s r o o m d e t a i l s ( s u c h a s t h e u s e r ' s p e r m i s s i o n a n d c u r r e n t
// / n u m b e r o f a c t i v e u s e r s ) , a n d i n c l u d i n g t h e f u l l r o o m m e t a d a t a i f t h e r o o m ' s i n f o _ u p d a t e d c o u n t e r h a s c h a n g e d f r o m t h e p r o v i d e d v a l u e
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e r o o m u p d a t e s s o s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y f r o m t h e ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l
// / t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e P o l l I n f o `
// / m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
public static func preparedRoomPollInfo (
_ db : Database ,
lastUpdated : Int64 ,
@ -305,51 +282,33 @@ public enum OpenGroupAPI {
}
public typealias CapabilitiesAndRoomResponse = (
info : ResponseInfoType ,
data : (
capabilities : ( info : ResponseInfoType , data : Capabilities ) ,
room : ( info : ResponseInfoType , data : Room )
)
capabilities : ( info : ResponseInfoType , data : Capabilities ) ,
room : ( info : ResponseInfoType , data : Room )
)
// / T h i s i s a c o n v e n i e n c e m e t h o d w h i c h c o n s t r u c t s a ` / s e q u e n c e ` o f t h e ` c a p a b i l i t i e s ` a n d ` r o o m ` r e q u e s t s , r e f e r t o t h o s e
// / m e t h o d s f o r t h e d o c u m e n t e d b e h a v i o u r o f e a c h m e t h o d
public static func c apabilitiesAndRoom(
public static func preparedC apabilitiesAndRoom(
_ db : Database ,
for roomToken : String ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < CapabilitiesAndRoomResponse , Error > {
let requestResponseType : [ BatchRequest . Info ] = [
// G e t t h e l a t e s t c a p a b i l i t i e s f o r t h e s e r v e r ( i n c a s e i t ' s a n e w s e r v e r o r t h e c a c h e d o n e s a r e s t a l e )
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . capabilities
) ,
responseType : Capabilities . self
) ,
// A n d t h e r o o m i n f o
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . room ( roomToken )
) ,
responseType : Room . self
)
]
return OpenGroupAPI
. sequence (
) throws -> PreparedSendData < CapabilitiesAndRoomResponse > {
return try OpenGroupAPI
. preparedSequence (
db ,
server : server ,
requests : requestResponseType ,
requests : [
// G e t t h e l a t e s t c a p a b i l i t i e s f o r t h e s e r v e r ( i n c a s e i t ' s a n e w s e r v e r o r t h e
// c a c h e d o n e s a r e s t a l e )
preparedCapabilities ( db , server : server , using : dependencies ) ,
preparedRoom ( db , for : roomToken , on : server , using : dependencies )
] ,
using : dependencies
)
. tryMap { ( info : ResponseInfoType , data : [ Endpoint : Codable ] ) -> CapabilitiesAndRoomResponse in
let maybeCapabilities : HTTP . BatchSubResponse < Capabilities > ? = ( data [ . capabilities ] as ? HTTP . BatchSubResponse < Capabilities > )
let maybeRoomResponse : Codable ? = data
. map { ( info : ResponseInfoType , response : BatchResponse ) -> CapabilitiesAndRoomResponse in
let maybeCapabilities : HTTP . BatchSubResponse < Capabilities > ? = ( response [ . capabilities ] as ? HTTP . BatchSubResponse < Capabilities > )
let maybeRoomResponse : Decodable ? = response . data
. first ( where : { key , _ in
switch key {
case . room : return true
@ -367,53 +326,34 @@ public enum OpenGroupAPI {
else { throw HTTPError . parsingFailed }
return (
info : info ,
data : (
capabilities : ( info : capabilitiesInfo , data : capabilities ) ,
room : ( info : roomInfo , data : room )
)
capabilities : ( info : capabilitiesInfo , data : capabilities ) ,
room : ( info : roomInfo , data : room )
)
}
. eraseToAnyPublisher ( )
}
// / T h i s i s a c o n v e n i e n c e m e t h o d w h i c h c o n s t r u c t s a ` / s e q u e n c e ` o f t h e ` c a p a b i l i t i e s ` a n d ` r o o m s ` r e q u e s t s , r e f e r t o t h o s e
// / m e t h o d s f o r t h e d o c u m e n t e d b e h a v i o u r o f e a c h m e t h o d
public static func c apabilitiesAndRooms(
public static func preparedC apabilitiesAndRooms(
_ db : Database ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( capabilities : ( info : ResponseInfoType , data : Capabilities ) , rooms : ( info : ResponseInfoType , data : [ Room ] ) ) , Error > {
let requestResponseType : [ BatchRequest . Info ] = [
// G e t t h e l a t e s t c a p a b i l i t i e s f o r t h e s e r v e r ( i n c a s e i t ' s a n e w s e r v e r o r t h e c a c h e d o n e s a r e s t a l e )
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . capabilities
) ,
responseType : Capabilities . self
) ,
// A n d t h e r o o m i n f o
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . rooms
) ,
responseType : [ Room ] . self
)
]
return OpenGroupAPI
. sequence (
) throws -> PreparedSendData < ( capabilities : ( info : ResponseInfoType , data : Capabilities ) , rooms : ( info : ResponseInfoType , data : [ Room ] ) ) > {
return try OpenGroupAPI
. preparedSequence (
db ,
server : server ,
requests : requestResponseType ,
requests : [
// G e t t h e l a t e s t c a p a b i l i t i e s f o r t h e s e r v e r ( i n c a s e i t ' s a n e w s e r v e r o r t h e
// c a c h e d o n e s a r e s t a l e )
preparedCapabilities ( db , server : server , using : dependencies ) ,
preparedRooms ( db , server : server , using : dependencies )
] ,
using : dependencies
)
. tryMap { ( info : ResponseInfoType , data : [ Endpoint : Codable ] ) -> ( capabilities : ( info : ResponseInfoType , data : Capabilities ) , rooms : ( info : ResponseInfoType , data : [ Room ] ) ) in
let maybeCapabilities : HTTP . BatchSubResponse < Capabilities > ? = ( data [ . capabilities ] as ? HTTP . BatchSubResponse < Capabilities > )
let maybeRooms : HTTP . BatchSubResponse < [ Room ] >? = data
. map { ( info : ResponseInfoType , response : BatchResponse ) -> ( capabilities : ( info : ResponseInfoType , data : Capabilities ) , rooms : ( info : ResponseInfoType , data : [ Room ] ) ) in
let maybeCapabilities : HTTP . BatchSubResponse < Capabilities > ? = ( response [ . capabilities ] as ? HTTP . BatchSubResponse < Capabilities > )
let maybeRooms : HTTP . BatchSubResponse < [ Room ] >? = response . data
. first ( where : { key , _ in
switch key {
case . rooms : return true
@ -434,7 +374,6 @@ public enum OpenGroupAPI {
rooms : ( info : roomsInfo , data : rooms )
)
}
. eraseToAnyPublisher ( )
}
// MARK: - M e s s a g e s
@ -528,6 +467,7 @@ public enum OpenGroupAPI {
)
}
// / R e m o v e a m e s s a g e b y i t s m e s s a g e i d
public static func preparedMessageDelete (
_ db : Database ,
id : Int64 ,
@ -548,62 +488,75 @@ public enum OpenGroupAPI {
)
}
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e r e c e n t m e s s a g e s s o s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y f r o m t h e ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l
// / t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e M e s s a g e s `
// / m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / R e t r i e v e s r e c e n t m e s s a g e s p o s t e d t o t h i s r o o m
// /
// / R e t u r n s t h e m o s t r e c e n t l i m i t m e s s a g e s ( 1 0 0 i f n o l i m i t i s g i v e n ) . T h i s o n l y r e t u r n s e x t a n t m e s s a g e s , a n d a l w a y s r e t u r n s t h e l a t e s t
// / v e r s i o n s : t h a t i s , d e l e t e d m e s s a g e i n d i c a t o r s a n d p r e - e d i t i n g v e r s i o n s o f m e s s a g e s a r e n o t r e t u r n e d . M e s s a g e s a r e r e t u r n e d i n o r d e r
// / f r o m m o s t r e c e n t t o l e a s t r e c e n t
public static func preparedRecentMessages (
_ db : Database ,
in roomToken : String ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) throws -> PreparedSendData < [ Message] > {
) throws -> PreparedSendData < [ Failable< Message> ] > {
return try OpenGroupAPI
. prepareSendData (
db ,
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . roomMessagesRecent ( roomToken )
endpoint : . roomMessagesRecent ( roomToken ) ,
queryParameters : [
. updateTypes : UpdateTypes . reaction . rawValue ,
. reactors : " 5 "
]
) ,
responseType : [ Message ] . self ,
responseType : [ Failable< Message> ] . self ,
using : dependencies
)
}
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e r e c e n t m e s s a g e s b e f o r e a g i v e n m e s s a g e a n d i s c u r r e n t l y u n u s e d , i n o r d e r t o c a l l t h i s d i r e c t l y
// / r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e M e s s a g e s `
// / m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / R e t r i e v e s m e s s a g e s f r o m t h e r o o m p r e c e d i n g a g i v e n i d .
// /
// / T h i s e n d p o i n t i s i n t e n d e d t o b e u s e d w i t h . . . / r e c e n t t o a l l o w a c l i e n t t o r e t r i e v e t h e m o s t r e c e n t m e s s a g e s a n d t h e n w a l k b a c k w a r d s
// / t h r o u g h b a t c h e s o f e v e r - o l d e r m e s s a g e s . A s w i t h . . . / r e c e n t , m e s s a g e s a r e r e t u r n e d i n o r d e r f r o m m o s t r e c e n t t o l e a s t r e c e n t .
// /
// / A s w i t h . . . / r e c e n t , t h i s e n d p o i n t d o e s n o t i n c l u d e d e l e t e d m e s s a g e s a n d a l w a y s r e t u r n s t h e c u r r e n t v e r s i o n , f o r e d i t e d m e s s a g e s .
public static func preparedMessagesBefore (
_ db : Database ,
messageId : Int64 ,
in roomToken : String ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) throws -> PreparedSendData < [ Message] > {
) throws -> PreparedSendData < [ Failable< Message> ] > {
return try OpenGroupAPI
. prepareSendData (
db ,
request : Request < NoBody , Endpoint > (
server : server ,
endpoint : . roomMessagesBefore ( roomToken , id : messageId )
endpoint : . roomMessagesBefore ( roomToken , id : messageId ) ,
queryParameters : [
. updateTypes : UpdateTypes . reaction . rawValue ,
. reactors : " 5 "
]
) ,
responseType : [ Message ] . self ,
responseType : [ Failable< Message> ] . self ,
using : dependencies
)
}
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e m e s s a g e s s i n c e a g i v e n m e s s a g e ` s e q N o ` s o s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y f r o m t h e
// / ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e
// / ` O p e n G r o u p M a n a g e r . h a n d l e M e s s a g e s ` m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / R e t r i e v e s m e s s a g e u p d a t e s f r o m a r o o m . T h i s i s t h e m a i n m e s s a g e p o l l i n g e n d p o i n t i n S O G S .
// /
// / T h i s e n d p o i n t r e t r i e v e s n e w , e d i t e d , a n d d e l e t e d m e s s a g e s o r m e s s a g e r e a c t i o n s p o s t e d t o t h i s r o o m s i n c e t h e g i v e n m e s s a g e
// / s e q u e n c e c o u n t e r . R e t u r n s l i m i t m e s s a g e s a t a t i m e ( 1 0 0 i f n o l i m i t i s g i v e n ) . R e t u r n e d m e s s a g e s i n c l u d e a n y n e w m e s s a g e s , u p d a t e s
// / t o e x i s t i n g m e s s a g e s ( i . e . e d i t s ) , a n d m e s s a g e d e l e t i o n s m a d e t o t h e r o o m s i n c e t h e g i v e n u p d a t e i d . M e s s a g e s a r e r e t u r n e d i n " u p d a t e "
// / o r d e r , t h a t i s , i n t h e o r d e r i n w h i c h t h e c h a n g e w a s a p p l i e d t o t h e r o o m , f r o m o l d e s t t h e n e w e s t .
public static func preparedMessagesSince (
_ db : Database ,
seqNo : Int64 ,
in roomToken : String ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) throws -> PreparedSendData < [ Message] > {
) throws -> PreparedSendData < [ Failable< Message> ] > {
return try OpenGroupAPI
. prepareSendData (
db ,
@ -612,10 +565,10 @@ public enum OpenGroupAPI {
endpoint : . roomMessagesSince ( roomToken , seqNo : seqNo ) ,
queryParameters : [
. updateTypes : UpdateTypes . reaction . rawValue ,
. reactors : " 20 "
. reactors : " 5 "
]
) ,
responseType : [ Message] . self ,
responseType : [ Failable< Message> ] . self ,
using : dependencies
)
}
@ -655,6 +608,7 @@ public enum OpenGroupAPI {
// MARK: - R e a c t i o n s
// / R e t u r n s t h e l i s t o f a l l r e a c t o r s w h o h a v e a d d e d a p a r t i c u l a r r e a c t i o n t o a p a r t i c u l a r m e s s a g e .
public static func preparedReactors (
_ db : Database ,
emoji : String ,
@ -682,6 +636,10 @@ public enum OpenGroupAPI {
)
}
// / A d d s a r e a c t i o n t o t h e g i v e n m e s s a g e i n t h i s r o o m . T h e u s e r m u s t h a v e r e a d a c c e s s i n t h e r o o m .
// /
// / R e a c t i o n s a r e s h o r t s t r i n g s o f 1 - 1 2 u n i c o d e c o d e p o i n t s , t y p i c a l l y e m o j i ( o r c h a r a c t e r s e q u e n c e s t o p r o d u c e a n e m o j i v a r i a n t ,
// / s u c h a s 👨 🏿 🦰 , w h i c h i s c o m p o s e d o f 4 u n i c o d e " c h a r a c t e r s " b u t u s u a l l y r e n d e r s a s a s i n g l e e m o j i " M a n : D a r k S k i n T o n e , R e d H a i r " ) .
public static func preparedReactionAdd (
_ db : Database ,
emoji : String ,
@ -709,6 +667,8 @@ public enum OpenGroupAPI {
)
}
// / R e m o v e s a r e a c t i o n f r o m a p o s t t h i s r o o m . T h e u s e r m u s t h a v e r e a d a c c e s s i n t h e r o o m . T h i s o n l y r e m o v e s t h e u s e r ' s o w n r e a c t i o n
// / b u t d o e s n o t a f f e c t t h e r e a c t i o n s o f o t h e r u s e r s .
public static func preparedReactionDelete (
_ db : Database ,
emoji : String ,
@ -736,6 +696,9 @@ public enum OpenGroupAPI {
)
}
// / R e m o v e s a l l r e a c t i o n s o f a l l u s e r s f r o m a p o s t i n t h i s r o o m . T h e c a l l i n g m u s t h a v e m o d e r a t o r p e r m i s s i o n s i n t h e r o o m . T h i s e n d p o i n t
// / c a n e i t h e r r e m o v e a s i n g l e r e a c t i o n ( e . g . r e m o v e a l l 🍆 r e a c t i o n s ) b y s p e c i f y i n g i t a f t e r t h e m e s s a g e i d ( f o l l o w i n g a / ) , o r r e m o v e a l l
// / r e a c t i o n s f r o m t h e p o s t b y n o t i n c l u d i n g t h e / < r e a c t i o n > s u f f i x o f t h e U R L .
public static func preparedReactionDeleteAll (
_ db : Database ,
emoji : String ,
@ -842,6 +805,12 @@ public enum OpenGroupAPI {
// MARK: - F i l e s
// / U p l o a d s a f i l e t o a r o o m .
// /
// / T a k e s t h e r e q u e s t a s b i n a r y i n t h e b o d y a n d t a k e s o t h e r p r o p e r t i e s ( s p e c i f i c a l l y t h e s u g g e s t e d f i l e n a m e ) v i a s u b m i t t e d h e a d e r s .
// /
// / T h e u s e r m u s t h a v e u p l o a d a n d p o s t i n g p e r m i s s i o n s f o r t h e r o o m . T h e f i l e w i l l h a v e a d e f a u l t l i f e t i m e o f 1 h o u r , w h i c h i s e x t e n d e d
// / t o 1 5 d a y s ( b y d e f a u l t ) w h e n a p o s t r e f e r e n c i n g t h e u p l o a d e d f i l e i s p o s t e d o r e d i t e d .
public static func preparedUploadFile (
_ db : Database ,
bytes : [ UInt8 ] ,
@ -871,6 +840,10 @@ public enum OpenGroupAPI {
)
}
// / R e t r i e v e s a f i l e u p l o a d e d t o t h e r o o m .
// /
// / R e t r i e v e s a f i l e v i a i t s n u m e r i c i d f r o m t h e r o o m , r e t u r n i n g t h e f i l e c o n t e n t d i r e c t l y a s t h e b i n a r y r e s p o n s e b o d y . T h e f i l e ' s s u g g e s t e d
// / f i l e n a m e ( a s p r o v i d e d b y t h e u p l o a d e r ) i s p r o v i d e d i n t h e C o n t e n t - D i s p o s i t i o n h e a d e r , i f a v a i l a b l e .
public static func preparedDownloadFile (
_ db : Database ,
fileId : String ,
@ -895,10 +868,7 @@ public enum OpenGroupAPI {
// / R e t r i e v e s a l l o f t h e u s e r ' s c u r r e n t D M s ( u p t o l i m i t )
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e D M s f o r a s p e c i f i c O p e n G r o u p s o s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y f r o m t h e ` p o l l ( ) `
// / m e t h o d , i n o r d e r t o c a l l t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e
// / ` O p e n G r o u p M a n a g e r . h a n d l e D i r e c t M e s s a g e s ` m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / * * N o t e : * * ` i n b o x ` w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s ( h e n c e t h e o p t i o n a l r e t u r n t y p e )
public static func preparedInbox (
_ db : Database ,
on server : String ,
@ -918,10 +888,7 @@ public enum OpenGroupAPI {
// / P o l l s f o r a n y D M s r e c e i v e d s i n c e t h e g i v e n i d , t h i s m e t h o d w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f t h e r e a r e n o m e s s a g e s
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e m e s s a g e s r e q u e s t s f o r a s p e c i f i c O p e n G r o u p s i n c e a g i v e n m e s s a g e s s o s h o u l d b e r e t r i e v e d
// / a u t o m a t i c a l l y f r o m t h e ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e
// / o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e D i r e c t M e s s a g e s ` m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / * * N o t e : * * ` i n b o x S i n c e ` w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s ( h e n c e t h e o p t i o n a l r e t u r n t y p e )
public static func preparedInboxSince (
_ db : Database ,
id : Int64 ,
@ -941,23 +908,22 @@ public enum OpenGroupAPI {
}
// / R e m o v e a l l m e s s a g e r e q u e s t s f r o m i n b o x , t h i s m e t h r o d w i l l r e t u r n t h e n u m b e r o f m e s s a g e s d e l e t e d
public static func c learInbox(
public static func preparedC learInbox(
_ db : Database ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( ResponseInfoType , DeleteInboxResponse ) , Error > {
return OpenGroupAPI
. send (
) throws -> PreparedSendData < DeleteInboxResponse > {
return try OpenGroupAPI
. prepareSendData (
db ,
request : Request < NoBody , Endpoint > (
method : . delete ,
server : server ,
endpoint : . inbox
) ,
responseType : DeleteInboxResponse . self ,
using : dependencies
)
. decoded ( as : DeleteInboxResponse . self , using : dependencies )
. eraseToAnyPublisher ( )
}
// / D e l i v e r s a d i r e c t m e s s a g e t o a u s e r v i a t h e i r b l i n d e d S e s s i o n I D
@ -988,10 +954,7 @@ public enum OpenGroupAPI {
// / R e t r i e v e s a l l o f t h e u s e r ' s s e n t D M s ( u p t o l i m i t )
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e D M s s e n t b y t h e u s e r f o r a s p e c i f i c O p e n G r o u p s o s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y
// / f r o m t h e ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e t o r o u t e t h e r e s p o n s e o f
// / t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e D i r e c t M e s s a g e s ` m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / * * N o t e : * * ` o u t b o x ` w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s ( h e n c e t h e o p t i o n a l r e t u r n t y p e )
public static func preparedOutbox (
_ db : Database ,
on server : String ,
@ -1011,10 +974,7 @@ public enum OpenGroupAPI {
// / P o l l s f o r a n y D M s s e n t s i n c e t h e g i v e n i d , t h i s m e t h o d w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f t h e r e a r e n o m e s s a g e s
// /
// / * * N o t e : * * T h i s i s t h e d i r e c t r e q u e s t t o r e t r i e v e m e s s a g e s r e q u e s t s s e n t b y t h e u s e r f o r a s p e c i f i c O p e n G r o u p s i n c e a g i v e n m e s s a g e s s o
// / s h o u l d b e r e t r i e v e d a u t o m a t i c a l l y f r o m t h e ` p o l l ( ) ` m e t h o d , i n o r d e r t o c a l l t h i s d i r e c t l y r e m o v e t h e ` @ a v a i l a b l e ` l i n e a n d m a k e s u r e
// / t o r o u t e t h e r e s p o n s e o f t h i s m e t h o d t o t h e ` O p e n G r o u p M a n a g e r . h a n d l e D i r e c t M e s s a g e s ` m e t h o d t o e n s u r e t h i n g s a r e p r o c e s s e d c o r r e c t l y
@ available ( * , unavailable , message : " Avoid using this directly, use the pre-built `poll()` method instead " )
// / * * N o t e : * * ` o u t b o x S i n c e ` w i l l r e t u r n a ` 3 0 4 ` w i t h a n e m p t y r e s p o n s e i f n o m e s s a g e s ( h e n c e t h e o p t i o n a l r e t u r n t y p e )
public static func preparedOutboxSince (
_ db : Database ,
id : Int64 ,
@ -1227,52 +1187,35 @@ public enum OpenGroupAPI {
// / T h i s i s a c o n v e n i e n c e m e t h o d w h i c h c o n s t r u c t s a ` / s e q u e n c e ` o f t h e ` u s e r B a n ` a n d ` u s e r D e l e t e M e s s a g e s ` r e q u e s t s , r e f e r t o t h o s e
// / m e t h o d s f o r t h e d o c u m e n t e d b e h a v i o u r o f e a c h m e t h o d
public static func u serBanAndDeleteAllMessages(
public static func preparedU serBanAndDeleteAllMessages(
_ db : Database ,
sessionId : String ,
in roomToken : String ,
on server : String ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( info : ResponseInfoType , data : [ Endpoint : ResponseInfoType ] ) , Error > {
let banRequestBody : UserBanRequest = UserBanRequest (
rooms : [ roomToken ] ,
global : nil ,
timeout : nil
)
// G e n e r a t e t h e r e q u e s t s
let requestResponseType : [ BatchRequest . Info ] = [
BatchRequest . Info (
request : Request < UserBanRequest , Endpoint > (
method : . post ,
server : server ,
endpoint : . userBan ( sessionId ) ,
body : banRequestBody
)
) ,
BatchRequest . Info (
request : Request < NoBody , Endpoint > (
method : . delete ,
server : server ,
endpoint : Endpoint . roomDeleteMessages ( roomToken , sessionId : sessionId )
)
)
]
return OpenGroupAPI
. sequence (
) throws -> PreparedSendData < BatchResponse > {
return try OpenGroupAPI
. preparedSequence (
db ,
server : server ,
requests : requestResponseType ,
requests : [
preparedUserBan (
db ,
sessionId : sessionId ,
from : [ roomToken ] ,
on : server ,
using : dependencies
) ,
preparedMessagesDeleteAll (
db ,
sessionId : sessionId ,
in : roomToken ,
on : server ,
using : dependencies
)
] ,
using : dependencies
)
. map { info , data -> ( info : ResponseInfoType , data : [ Endpoint : ResponseInfoType ] ) in
(
info ,
data . compactMapValues { ( $0 as ? BatchSubResponseType ) ? . responseInfo }
)
}
. eraseToAnyPublisher ( )
}
// MARK: - A u t h e n t i c a t i o n
@ -1408,6 +1351,9 @@ public enum OpenGroupAPI {
// MARK: - C o n v e n i e n c e
// / T a k e s t h e r e u q e s t i n f o r m a t i o n a n d g e n e r a t e s a s i g n e d ` P r e p a r e d S e n d D a t a < R > ` p b j e c t w h i c h i s r e a d y f o r s e n d i n g t o t h e A P I , t h i s
// / m e t h o d i s m a i n l y h e r e s o w e c a n s e p a r a t e t h e p r e p a r a t i o n o f a r e q u e s t , w h i c h r e q u i r e s a c c e s s t o t h e d a t a b a s e f o r s i g n i n g , f r o m t h e
// / a c t u a l s e n d i n g o f t h e r e u q e s t t o e n s u r e w e d o n ' t r u n i n t o a n y u n e x p e c t e d b l o c k i n g o f t h e d a t a b a s e w r i t e t h r e a d
private static func prepareSendData < T : Encodable , R : Decodable > (
_ db : Database ,
request : Request < T , Endpoint > ,
@ -1431,56 +1377,15 @@ public enum OpenGroupAPI {
}
return PreparedSendData (
request : signedRequest ,
endpoint : request . endpoint ,
server : request . server ,
request : request ,
urlRequest : signedRequest ,
publicKey : publicKey ,
responseType : responseType ,
timeout : timeout
)
}
private static func send < T : Encodable > (
_ db : Database ,
request : Request < T , Endpoint > ,
forceBlinded : Bool = false ,
timeout : TimeInterval = HTTP . defaultTimeout ,
using dependencies : SMKDependencies = SMKDependencies ( )
) -> AnyPublisher < ( ResponseInfoType , Data ? ) , Error > {
let urlRequest : URLRequest
do {
urlRequest = try request . generateUrlRequest ( )
}
catch {
return Fail ( error : error )
. eraseToAnyPublisher ( )
}
let maybePublicKey : String ? = try ? OpenGroup
. select ( . publicKey )
. filter ( OpenGroup . Columns . server = = request . server . lowercased ( ) )
. asRequest ( of : String . self )
. fetchOne ( db )
guard let publicKey : String = maybePublicKey else {
return Fail ( error : OpenGroupAPIError . noPublicKey )
. eraseToAnyPublisher ( )
}
// A t t e m p t t o s i g n t h e r e q u e s t w i t h t h e n e w a u t h
guard let signedRequest : URLRequest = sign ( db , request : urlRequest , for : request . server , with : publicKey , forceBlinded : forceBlinded , using : dependencies ) else {
return Fail ( error : OpenGroupAPIError . signingFailed )
. eraseToAnyPublisher ( )
}
// W e w a n t t o a v o i d b l o c k i n g t h e d b w r i t e t h r e a d s o w e d i s p a t c h t h e A P I c a l l t o a d i f f e r e n t t h r e a d
return Just ( ( ) )
. setFailureType ( to : Error . self )
. flatMap { dependencies . onionApi . sendOnionRequest ( signedRequest , to : request . server , with : publicKey , timeout : timeout ) }
. eraseToAnyPublisher ( )
}
// / T h i s m e t h o d t a k e s i n t h e ` P r e p a r e d S e n d D a t a < R > ` a n d a c t u a l l y s e n d s i t t o t h e A P I
public static func send < R > (
data : PreparedSendData < R > ? ,
using dependencies : SMKDependencies = SMKDependencies ( )