Skip to content

Commit 4906de2

Browse files
authored
add get_presence_state to presence API (#171)
1 parent 4d6c07f commit 4906de2

File tree

6 files changed

+465
-44
lines changed

6 files changed

+465
-44
lines changed

examples/presence_state.rs

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
use pubnub::{Keyset, PubNubClientBuilder};
2+
use serde::Serialize;
23
use std::env;
34

5+
#[derive(Debug, Serialize)]
6+
struct State {
7+
is_doing: String,
8+
}
9+
410
#[tokio::main]
511
async fn main() -> Result<(), Box<dyn snafu::Error>> {
612
let publish_key = env::var("SDK_PUB_KEY")?;
713
let subscribe_key = env::var("SDK_SUB_KEY")?;
814

9-
let _client = PubNubClientBuilder::with_reqwest_transport()
15+
let client = PubNubClientBuilder::with_reqwest_transport()
1016
.with_keyset(Keyset {
1117
subscribe_key,
1218
publish_key: Some(publish_key),
@@ -17,27 +23,31 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
1723

1824
println!("running!");
1925

20-
// client
21-
// .set_presence_state()
22-
// .channels(["my_channel".into(), "other_channel".into()].to_vec())
23-
// .state("{\"What you're doing\": \"Me? Nothing... Just hanging around\"}")
24-
// .user_id("user_id")
25-
// .execute()
26-
// .await?;
27-
//
28-
// let states = client
29-
// .get_presence_state()
30-
// .channels(["my_channel".into(), "other_channel".into()].to_vec())
31-
// .user_id("user_id")
32-
// .execute()
33-
// .await?;
34-
//
35-
// println!("All channels state: {:?}", states);
36-
//
37-
// states.iter().for_each(|channel| {
38-
// println!("Channel: {}", channel.channel);
39-
// println!("State: {:?}", channel.state);
40-
// });
41-
//
26+
client
27+
.set_presence_state(State {
28+
is_doing: "Nothing... Just hanging around...".into(),
29+
})
30+
.channels(["my_channel".into(), "other_channel".into()].to_vec())
31+
.user_id("user_id")
32+
.execute()
33+
.await?;
34+
35+
println!("State set!");
36+
println!();
37+
38+
let states = client
39+
.get_presence_state()
40+
.channels(["my_channel".into(), "other_channel".into()].to_vec())
41+
.user_id("user_id")
42+
.execute()
43+
.await?;
44+
45+
println!("All channels state: {:?}", states);
46+
47+
states.iter().for_each(|channel| {
48+
println!("Channel: {}", channel.channel);
49+
println!("State: {:?}", channel.state);
50+
});
51+
4252
Ok(())
4353
}
Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
use pubnub::{Keyset, PubNubClientBuilder};
2+
use serde::Serialize;
23
use std::env;
34

5+
#[derive(Debug, Serialize)]
6+
struct State {
7+
is_doing: String,
8+
}
9+
410
fn main() -> Result<(), Box<dyn snafu::Error>> {
511
let publish_key = env::var("SDK_PUB_KEY")?;
612
let subscribe_key = env::var("SDK_SUB_KEY")?;
713

8-
let _client = PubNubClientBuilder::with_reqwest_blocking_transport()
14+
let client = PubNubClientBuilder::with_reqwest_blocking_transport()
915
.with_keyset(Keyset {
1016
subscribe_key,
1117
publish_key: Some(publish_key),
@@ -16,25 +22,29 @@ fn main() -> Result<(), Box<dyn snafu::Error>> {
1622

1723
println!("running!");
1824

19-
// client
20-
// .set_presence_state()
21-
// .channels(["my_channel".into(), "other_channel".into()].to_vec())
22-
// .state("{\"What you're doing\": \"Me? Nothing... Just hanging around\"}")
23-
// .user_id("user_id")
24-
// .execute_blocking()?;
25-
//
26-
// let states = client
27-
// .get_presence_state()
28-
// .channels(["my_channel".into(), "other_channel".into()].to_vec())
29-
// .user_id("user_id")
30-
// .execute_blocking()?;
31-
//
32-
// println!("All channels state: {:?}", states);
33-
//
34-
// states.iter().for_each(|channel| {
35-
// println!("Channel: {}", channel.channel);
36-
// println!("State: {:?}", channel.state);
37-
// });
38-
//
25+
client
26+
.set_presence_state(State {
27+
is_doing: "Nothing... Just hanging around...".into(),
28+
})
29+
.channels(["my_channel".into(), "other_channel".into()].to_vec())
30+
.user_id("user_id")
31+
.execute_blocking()?;
32+
33+
println!("State set!");
34+
println!();
35+
36+
let states = client
37+
.get_presence_state()
38+
.channels(["my_channel".into(), "other_channel".into()].to_vec())
39+
.user_id("user_id")
40+
.execute_blocking()?;
41+
42+
println!("All channels state: {:?}", states);
43+
44+
states.iter().for_each(|channel| {
45+
println!("Channel: {}", channel.channel);
46+
println!("State: {:?}", channel.state);
47+
});
48+
3949
Ok(())
4050
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//! # PubNub set state module.
2+
//!
3+
//! The [`GetStateRequestBuilder`] lets you make and execute requests that will
4+
//! associate the provided `state` with `user_id` on the provided list of
5+
//! channels and channels in channel groups.
6+
7+
use derive_builder::Builder;
8+
9+
use crate::{
10+
core::{
11+
utils::{
12+
encoding::{
13+
url_encode_extended, url_encoded_channel_groups, url_encoded_channels,
14+
UrlEncodeExtension,
15+
},
16+
headers::{APPLICATION_JSON, CONTENT_TYPE},
17+
},
18+
Deserializer, PubNubError, Transport, TransportMethod, TransportRequest,
19+
},
20+
dx::{
21+
presence::{
22+
builders,
23+
result::{GetStateResponseBody, GetStateResult},
24+
},
25+
pubnub_client::PubNubClientInstance,
26+
},
27+
lib::{
28+
alloc::{
29+
string::{String, ToString},
30+
vec,
31+
},
32+
collections::HashMap,
33+
},
34+
};
35+
36+
/// The [`GetStateRequestBuilder`] is used to build `user_id` associated state
37+
/// update request that is sent to the [`PubNub`] network.
38+
///
39+
/// This struct is used by the [`set_state`] method of the [`PubNubClient`].
40+
/// The [`set_state`] method is used to update state associated with `user_id`
41+
/// on the provided channels and groups.
42+
///
43+
/// [`PubNub`]:https://www.pubnub.com/
44+
#[derive(Builder)]
45+
#[builder(
46+
pattern = "owned",
47+
build_fn(vis = "pub(in crate::dx::presence)", validate = "Self::validate"),
48+
no_std
49+
)]
50+
pub struct GetStateRequest<T, D> {
51+
/// Current client which can provide transportation to perform the request.
52+
///
53+
/// This field is used to get [`Transport`] to perform the request.
54+
#[builder(field(vis = "pub(in crate::dx::presence)"), setter(custom))]
55+
pub(in crate::dx::presence) pubnub_client: PubNubClientInstance<T, D>,
56+
57+
/// Channels with which state will be associated.
58+
#[builder(
59+
field(vis = "pub(in crate::dx::presence)"),
60+
setter(strip_option, into),
61+
default = "vec![]"
62+
)]
63+
pub(in crate::dx::presence) channels: Vec<String>,
64+
65+
/// Channel groups with which state will be associated.
66+
///
67+
/// The specified state will be associated with channels that have been
68+
/// included in the specified target channel groups.
69+
#[builder(
70+
field(vis = "pub(in crate::dx::presence)"),
71+
setter(into, strip_option),
72+
default = "vec![]"
73+
)]
74+
pub(in crate::dx::presence) channel_groups: Vec<String>,
75+
76+
#[builder(field(vis = "pub(in crate::dx::presence)"), setter(strip_option, into))]
77+
/// Identifier for which `state` should be associated for provided list of
78+
/// channels and groups.
79+
pub(in crate::dx::presence) user_id: String,
80+
}
81+
82+
impl<T, D> GetStateRequestBuilder<T, D> {
83+
/// Validate user-provided data for request builder.
84+
///
85+
/// Validator ensure that list of provided data is enough to build valid
86+
/// set state request instance.
87+
fn validate(&self) -> Result<(), String> {
88+
let groups_len = self.channel_groups.as_ref().map_or_else(|| 0, |v| v.len());
89+
let channels_len = self.channels.as_ref().map_or_else(|| 0, |v| v.len());
90+
91+
builders::validate_configuration(&self.pubnub_client).and_then(|_| {
92+
if channels_len == groups_len && channels_len == 0 {
93+
Err("Either channels or channel groups should be provided".into())
94+
} else if self.user_id.is_none() {
95+
Err("User id is missing".into())
96+
} else {
97+
Ok(())
98+
}
99+
})
100+
}
101+
102+
/// Build [`GetStateRequest`] from builder.
103+
fn request(self) -> Result<GetStateRequest<T, D>, PubNubError> {
104+
self.build()
105+
.map_err(|err| PubNubError::general_api_error(err.to_string(), None, None))
106+
}
107+
}
108+
109+
impl<T, D> GetStateRequest<T, D> {
110+
/// Create transport request from the request builder.
111+
pub(in crate::dx::presence) fn transport_request(
112+
&self,
113+
) -> Result<TransportRequest, PubNubError> {
114+
let sub_key = &self.pubnub_client.config.subscribe_key;
115+
let mut query: HashMap<String, String> = HashMap::new();
116+
117+
// Serialize list of channel groups and add into query parameters list.
118+
url_encoded_channel_groups(&self.channel_groups)
119+
.and_then(|channel_groups| query.insert("channel-group".into(), channel_groups));
120+
121+
Ok(TransportRequest {
122+
path: format!(
123+
"/v2/presence/sub-key/{sub_key}/channel/{}/uuid/{}",
124+
url_encoded_channels(&self.channels),
125+
url_encode_extended(self.user_id.as_bytes(), UrlEncodeExtension::NonChannelPath)
126+
),
127+
query_parameters: query,
128+
method: TransportMethod::Get,
129+
headers: [(CONTENT_TYPE.into(), APPLICATION_JSON.into())].into(),
130+
body: None,
131+
})
132+
}
133+
}
134+
135+
impl<T, D> GetStateRequestBuilder<T, D>
136+
where
137+
T: Transport,
138+
D: Deserializer + 'static,
139+
{
140+
/// Build and call asynchronous request.
141+
pub async fn execute(self) -> Result<GetStateResult, PubNubError> {
142+
let request = self.request()?;
143+
let transport_request = request.transport_request()?;
144+
let client = request.pubnub_client.clone();
145+
let deserializer = client.deserializer.clone();
146+
transport_request
147+
.send::<GetStateResponseBody, _, _, _>(&client.transport, deserializer)
148+
.await
149+
}
150+
}
151+
152+
#[allow(dead_code)]
153+
#[cfg(feature = "blocking")]
154+
impl<T, D> GetStateRequestBuilder<T, D>
155+
where
156+
T: crate::core::blocking::Transport,
157+
D: Deserializer + 'static,
158+
{
159+
/// Build and call synchronous request.
160+
pub fn execute_blocking(self) -> Result<GetStateResult, PubNubError> {
161+
let request = self.request()?;
162+
let transport_request = request.transport_request()?;
163+
let client = request.pubnub_client.clone();
164+
let deserializer = client.deserializer.clone();
165+
transport_request
166+
.send_blocking::<GetStateResponseBody, _, _, _>(&client.transport, deserializer)
167+
}
168+
}

src/dx/presence/builders/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ pub(crate) mod here_now;
2525
pub(crate) use where_now::WhereNowRequestBuilder;
2626
pub(crate) mod where_now;
2727

28+
#[doc(inline)]
29+
pub(crate) use get_presence_state::GetStateRequestBuilder;
30+
pub(crate) mod get_presence_state;
31+
2832
use crate::{dx::pubnub_client::PubNubClientInstance, lib::alloc::string::String};
2933

3034
/// Validate [`PubNubClient`] configuration.

src/dx/presence/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,48 @@ impl<T, D> PubNubClientInstance<T, D> {
170170
self.heartbeat().state(state)
171171
}
172172

173+
/// Create a get state request builder.
174+
///
175+
/// This method is used to get state associated with `user_id` on
176+
/// channels and and channels registered with channel groups.
177+
///
178+
/// Instance of [`SetStateRequestBuilder`] returned.
179+
///
180+
/// # Example
181+
/// ```rust
182+
/// use pubnub::presence::*;
183+
/// # use pubnub::{Keyset, PubNubClientBuilder};
184+
/// # use std::collections::HashMap;
185+
///
186+
/// #[tokio::main]
187+
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
188+
/// # use std::sync::Arc;
189+
/// let mut pubnub = // PubNubClient
190+
/// # PubNubClientBuilder::with_reqwest_transport()
191+
/// # .with_keyset(Keyset {
192+
/// # subscribe_key: "demo",
193+
/// # publish_key: None,
194+
/// # secret_key: None
195+
/// # })
196+
/// # .with_user_id("uuid")
197+
/// # .build()?;
198+
/// pubnub
199+
/// .get_presence_state()
200+
/// .channels(["lobby".into(), "announce".into()])
201+
/// .channel_groups(["area-51".into()])
202+
/// .execute()
203+
/// .await?;
204+
/// # Ok(())
205+
/// # }
206+
/// ```
207+
pub fn get_presence_state(&self) -> GetStateRequestBuilder<T, D> {
208+
GetStateRequestBuilder {
209+
pubnub_client: Some(self.clone()),
210+
user_id: Some(self.config.user_id.clone().to_string()),
211+
..Default::default()
212+
}
213+
}
214+
173215
/// Create a here now request builder.
174216
///
175217
/// This method is used to get information about current occupancy of

0 commit comments

Comments
 (0)