Skip to main content

roblox_api/api/assets/
v1.rs

1use reqwest::{
2    header::{self, HeaderValue},
3    multipart::{Form, Part},
4};
5use serde::{Deserialize, Serialize};
6
7use crate::{AssetTypeId, DateTime, Error, client::Client, endpoint};
8
9pub const URL: &str = "https://apis.roblox.com/assets/user-auth/v1";
10
11#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
12#[serde(rename_all = "camelCase")]
13pub enum Creator {
14    // TODO: cast to u64
15    UserId(String), // can be sent as a u64, but it's returned as a string in some cases
16    GroupId(String),
17}
18
19#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
20#[serde(rename_all = "camelCase")]
21pub struct CreationContext {
22    pub creator: Creator,
23    pub expected_price: Option<u64>,
24}
25
26#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
27pub struct ModerationResult {
28    #[serde(rename = "moderationState")]
29    pub state: String,
30}
31
32#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
33#[serde(rename_all = "camelCase")]
34pub struct AssetInfo {
35    #[serde(rename = "assetId")]
36    pub id: String,
37    pub icon: Option<String>,
38    #[serde(rename = "displayName")]
39    pub name: String,
40    pub description: String,
41
42    pub path: String,
43    pub state: String,
44    pub asset_type: AssetTypeId,
45
46    pub revision_id: String,
47    #[serde(rename = "revisionCreateTime")]
48    pub revision_creation_time: DateTime,
49
50    pub creation_context: CreationContext,
51    pub moderation_result: ModerationResult,
52}
53
54#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
55#[serde(rename_all = "camelCase")]
56pub struct AssetUploadResponse {
57    pub path: String,
58    // TODO: cast to u64 please
59    #[serde(rename = "assetId")]
60    pub id: String,
61    #[serde(rename = "displayName")]
62    pub name: String,
63
64    pub state: String,
65    pub asset_type: AssetTypeId,
66
67    pub revision_id: String,
68    #[serde(rename = "revisionCreateTime")]
69    pub revision_creation_time: DateTime,
70
71    pub creation_context: CreationContext,
72    pub moderation_result: ModerationResult,
73}
74
75#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
76#[serde(rename_all = "camelCase")]
77pub struct AssetUploadStatus {
78    pub path: String,
79    pub operation_id: String,
80    #[serde(rename = "done")]
81    pub complete: bool,
82    pub response: Option<AssetUploadResponse>,
83}
84
85endpoint! {
86    asset(id: u64) -> AssetInfo {
87        GET "{URL}/assets/{id}";
88    }
89
90    status(operation_id: &str) -> AssetUploadStatus {
91        GET "{URL}/operations/{operation_id}";
92    }
93}
94
95// TODO: this api also takes in a patch request to update an exists asset "{URL}/assets/{id}"
96pub async fn upload(
97    client: &mut Client,
98    bytes: &[u8],
99    title: &str,
100    description: &str,
101    asset_type: AssetTypeId,
102    creation_context: CreationContext,
103) -> Result<AssetUploadStatus, Error> {
104    let mut headers = client.requestor.default_headers.clone();
105    headers.insert(header::ACCEPT, HeaderValue::from_str("*/*").unwrap());
106
107    #[derive(Clone, Debug, Deserialize, Serialize)]
108    #[serde(rename_all = "camelCase")]
109    struct Request<'a> {
110        #[serde(rename = "displayName")]
111        title: &'a str,
112        description: &'a str,
113        asset_type: AssetTypeId,
114        creation_context: CreationContext,
115    }
116
117    let request = serde_json::to_string(&Request {
118        title,
119        description,
120        asset_type,
121        creation_context,
122    })
123    .unwrap();
124
125    let mime = match asset_type {
126        AssetTypeId::Model => "model/x-rbxm",
127        _ => infer::get(bytes)
128            .map(|t| t.mime_type())
129            .unwrap_or("application/octet-stream"),
130    };
131
132    let file_part = Part::bytes(bytes.to_owned())
133        .file_name("instance")
134        .mime_str(mime)?;
135
136    let result = client
137        .requestor
138        .client
139        .post(format!("{URL}/assets"))
140        .headers(headers)
141        .multipart(
142            Form::new()
143                .text("request", request)
144                .part("fileContent", file_part),
145        )
146        .send()
147        .await;
148
149    let response = client.requestor.validate_response(result).await?;
150    client
151        .requestor
152        .parse_json::<AssetUploadStatus>(response)
153        .await
154}
155
156pub async fn update(
157    client: &mut Client,
158    id: u64,
159    bytes: &[u8],
160    title: &str,
161    description: &str,
162    asset_type: AssetTypeId,
163) -> Result<AssetUploadStatus, Error> {
164    let mut headers = client.requestor.default_headers.clone();
165    headers.insert(header::ACCEPT, HeaderValue::from_str("*/*").unwrap());
166
167    #[derive(Clone, Debug, Deserialize, Serialize)]
168    #[serde(rename_all = "camelCase")]
169    struct Request<'a> {
170        #[serde(rename = "assetId")]
171        id: u64,
172        #[serde(rename = "displayName")]
173        title: &'a str,
174        description: &'a str,
175    }
176
177    let request = serde_json::to_string(&Request {
178        id,
179        title,
180        description,
181    })
182    .unwrap();
183
184    let mime = match asset_type {
185        AssetTypeId::Model => "model/x-rbxm",
186        _ => infer::get(bytes)
187            .map(|t| t.mime_type())
188            .unwrap_or("application/octet-stream"),
189    };
190
191    let file_part = Part::bytes(bytes.to_owned())
192        .file_name("instance")
193        .mime_str(mime)?;
194
195    let result = client
196        .requestor
197        .client
198        .patch(format!("{URL}/assets/{id}"))
199        .headers(headers)
200        .multipart(
201            Form::new()
202                .text("request", request)
203                .part("fileContent", file_part),
204        )
205        .send()
206        .await;
207
208    let response = client.requestor.validate_response(result).await?;
209    client
210        .requestor
211        .parse_json::<AssetUploadStatus>(response)
212        .await
213}