Skip to content

Commit 1ff2456

Browse files
authored
feat: Add support for the Retention Messaging API (#85)
1 parent 734eb53 commit 1ff2456

32 files changed

+1581
-19
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,5 @@ Cargo.lock
9393

9494
.env
9595
.claude
96+
CLAUDE.md
97+
rustfmt.toml

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ name = "ac_api_client"
6363
path = "tests/ac_api_client.rs"
6464
required-features = ["api-client"]
6565

66+
[[test]]
67+
name = "rm_api_client"
68+
path = "tests/rm_api_client.rs"
69+
required-features = ["api-client"]
70+
6671
[[test]]
6772
name = "chain_verifier_ocsp"
6873
path = "tests/chain_verifier_ocsp.rs"

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Apple App Store Server Rust Library
2-
The Rust server library for the [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi), [App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications) and [Advanced Commerce API](https://developer.apple.com/documentation/AdvancedCommerceAPI).
2+
The Rust server library for the [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi), [App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications), the [Retention Messaging API](https://developer.apple.com/documentation/retentionmessaging), and [Advanced Commerce API](https://developer.apple.com/documentation/AdvancedCommerceAPI).
33

44
## Requirements
55

@@ -211,6 +211,7 @@ let signature: String = creator.create_signature(
211211
* The full documentation is available at [docs.rs](https://docs.rs/app-store-server-library/)
212212
* [App Store Server API Documentation](https://developer.apple.com/documentation/appstoreserverapi)
213213
* [App Store Server Notifications Documentation](https://developer.apple.com/documentation/appstoreservernotifications)
214+
* [Retention Messaging API Documentation](https://developer.apple.com/documentation/retentionmessaging)
214215
* [Advanced Commerce API Documentation](https://developer.apple.com/documentation/advancedcommerceapi)
215216
* [WWDC Video](https://developer.apple.com/videos/play/wwdc2023/10143/)
216217

src/api_client/api/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
pub mod app_store_server_api;
33

44
#[cfg(feature = "api-client")]
5-
pub mod advanced_commerce_api;
5+
pub mod advanced_commerce_api;
6+
7+
#[cfg(feature = "api-client")]
8+
pub mod retention_messaging_api;
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
pub mod api_error_code;
2+
3+
use http::Method;
4+
use uuid::Uuid;
5+
use crate::api_client::api::retention_messaging_api::api_error_code::ApiErrorCode;
6+
use crate::api_client::api_client::ApiClient;
7+
use crate::api_client::error::ApiServiceError;
8+
use crate::api_client::transport::Transport;
9+
use crate::primitives::retention_messaging::default_configuration_request::DefaultConfigurationRequest;
10+
use crate::primitives::retention_messaging::get_image_list_response::GetImageListResponse;
11+
use crate::primitives::retention_messaging::get_message_list_response::GetMessageListResponse;
12+
use crate::primitives::retention_messaging::upload_message_request_body::UploadMessageRequestBody;
13+
14+
pub struct RetentionMessagingApi;
15+
pub type RetentionMessagingApiClient<T> = ApiClient<T, RetentionMessagingApi, ApiErrorCode>;
16+
pub type ApiError = ApiServiceError<ApiErrorCode>;
17+
18+
impl<T: Transport> RetentionMessagingApiClient<T> {
19+
/// Upload an image to use for retention messaging.
20+
///
21+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/upload-image)
22+
///
23+
/// # Arguments
24+
///
25+
/// * `image_identifier` - A UUID you provide to uniquely identify the image you upload.
26+
/// * `image` - The PNG image data to upload.
27+
///
28+
/// # Errors
29+
///
30+
/// Returns an `APIError` if the request could not be processed.
31+
pub async fn upload_image(
32+
&self,
33+
image_identifier: Uuid,
34+
image: Vec<u8>,
35+
) -> Result<(), ApiError> {
36+
let path = format!("/inApps/v1/messaging/image/{}", image_identifier);
37+
let req = self.build_request_with_custom_content(
38+
&path,
39+
Method::PUT,
40+
image,
41+
"image/png",
42+
)?;
43+
self.make_request_without_response_body(req).await
44+
}
45+
46+
/// Delete a previously uploaded image.
47+
///
48+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/delete-image)
49+
///
50+
/// # Arguments
51+
///
52+
/// * `image_identifier` - The identifier of the image to delete.
53+
///
54+
/// # Errors
55+
///
56+
/// Returns an `APIError` if the request could not be processed.
57+
pub async fn delete_image(
58+
&self,
59+
image_identifier: Uuid,
60+
) -> Result<(), ApiError> {
61+
let path = format!("/inApps/v1/messaging/image/{}", image_identifier);
62+
let req = self.build_request::<()>(
63+
&path,
64+
Method::DELETE,
65+
None,
66+
)?;
67+
self.make_request_without_response_body(req).await
68+
}
69+
70+
/// Get the image identifier and state for all uploaded images.
71+
///
72+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/get-image-list)
73+
///
74+
/// # Returns
75+
///
76+
/// A response that contains status information for all images.
77+
///
78+
/// # Errors
79+
///
80+
/// Returns an `APIError` if the request could not be processed.
81+
pub async fn image_list(&self) -> Result<GetImageListResponse, ApiError> {
82+
let req = self.build_request::<()>(
83+
"/inApps/v1/messaging/image/list",
84+
Method::GET,
85+
None,
86+
)?;
87+
self.make_request_with_response_body(req).await
88+
}
89+
90+
/// Upload a message to use for retention messaging.
91+
///
92+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/upload-message)
93+
///
94+
/// # Arguments
95+
///
96+
/// * `message_identifier` - A UUID you provide to uniquely identify the message you upload.
97+
/// * `upload_message_request_body` - The message text to upload.
98+
///
99+
/// # Errors
100+
///
101+
/// Returns an `APIError` if the request could not be processed.
102+
pub async fn upload_message(
103+
&self,
104+
message_identifier: Uuid,
105+
upload_message_request_body: &UploadMessageRequestBody,
106+
) -> Result<(), ApiError> {
107+
let path = format!("/inApps/v1/messaging/message/{}", message_identifier);
108+
let req = self.build_request(
109+
&path,
110+
Method::PUT,
111+
Some(upload_message_request_body),
112+
)?;
113+
self.make_request_without_response_body(req).await
114+
}
115+
116+
/// Delete a previously uploaded message.
117+
///
118+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/delete-message)
119+
///
120+
/// # Arguments
121+
///
122+
/// * `message_identifier` - The identifier of the message to delete.
123+
///
124+
/// # Errors
125+
///
126+
/// Returns an `APIError` if the request could not be processed.
127+
pub async fn delete_message(
128+
&self,
129+
message_identifier: Uuid,
130+
) -> Result<(), ApiError> {
131+
let path = format!("/inApps/v1/messaging/message/{}", message_identifier);
132+
let req = self.build_request::<()>(
133+
&path,
134+
Method::DELETE,
135+
None,
136+
)?;
137+
self.make_request_without_response_body(req).await
138+
}
139+
140+
/// Get the message identifier and state of all uploaded messages.
141+
///
142+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/get-message-list)
143+
///
144+
/// # Returns
145+
///
146+
/// A response that contains status information for all messages.
147+
///
148+
/// # Errors
149+
///
150+
/// Returns an `APIError` if the request could not be processed.
151+
pub async fn message_list(&self) -> Result<GetMessageListResponse, ApiError> {
152+
let req = self.build_request::<()>(
153+
"/inApps/v1/messaging/message/list",
154+
Method::GET,
155+
None,
156+
)?;
157+
self.make_request_with_response_body(req).await
158+
}
159+
160+
/// Configure a default message for a specific product in a specific locale.
161+
///
162+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/set-default-configuration)
163+
///
164+
/// # Arguments
165+
///
166+
/// * `product_id` - The product identifier for the default configuration.
167+
/// * `locale` - The locale for the default configuration.
168+
/// * `default_configuration_request` - The request body that includes the message identifier to configure as the default message.
169+
///
170+
/// # Errors
171+
///
172+
/// Returns an `APIError` if the request could not be processed.
173+
pub async fn set_default_configuration(
174+
&self,
175+
product_id: &str,
176+
locale: &str,
177+
default_configuration_request: &DefaultConfigurationRequest,
178+
) -> Result<(), ApiError> {
179+
let path = format!(
180+
"/inApps/v1/messaging/default/{}/{}",
181+
product_id,
182+
locale
183+
);
184+
let req = self.build_request(
185+
&path,
186+
Method::PUT,
187+
Some(default_configuration_request),
188+
)?;
189+
self.make_request_without_response_body(req).await
190+
}
191+
192+
/// Get the default message configuration for a specific product in a specific locale.
193+
///
194+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/get-default-configuration)
195+
///
196+
/// # Arguments
197+
///
198+
/// * `product_id` - The product identifier for the default configuration.
199+
/// * `locale` - The locale for the default configuration.
200+
///
201+
/// # Returns
202+
///
203+
/// The default configuration for the specified product and locale.
204+
///
205+
/// # Errors
206+
///
207+
/// Returns an `APIError` if the request could not be processed.
208+
pub async fn default_configuration(
209+
&self,
210+
product_id: &str,
211+
locale: &str,
212+
) -> Result<DefaultConfigurationRequest, ApiError> {
213+
let path = format!(
214+
"/inApps/v1/messaging/default/{}/{}",
215+
product_id,
216+
locale
217+
);
218+
let req = self.build_request::<()>(
219+
&path,
220+
Method::GET,
221+
None,
222+
)?;
223+
self.make_request_with_response_body(req).await
224+
}
225+
226+
/// Delete the default message configuration for a specific product in a specific locale.
227+
///
228+
/// [Documentation](https://developer.apple.com/documentation/retentionmessaging/delete-default-configuration)
229+
///
230+
/// # Arguments
231+
///
232+
/// * `product_id` - The product identifier for the default configuration.
233+
/// * `locale` - The locale for the default configuration.
234+
///
235+
/// # Errors
236+
///
237+
/// Returns an `APIError` if the request could not be processed.
238+
pub async fn delete_default_configuration(
239+
&self,
240+
product_id: &str,
241+
locale: &str,
242+
) -> Result<(), ApiError> {
243+
let path = format!(
244+
"/inApps/v1/messaging/default/{}/{}",
245+
product_id,
246+
locale
247+
);
248+
let req = self.build_request::<()>(
249+
&path,
250+
Method::DELETE,
251+
None,
252+
)?;
253+
self.make_request_without_response_body(req).await
254+
}
255+
}

0 commit comments

Comments
 (0)