Skip to content

Commit 02b4113

Browse files
Use post for ref patch and serialize (#1192)
1 parent d08780f commit 02b4113

File tree

6 files changed

+99
-60
lines changed

6 files changed

+99
-60
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ Making a new release? Simply add the new header with the version and date undern
3131

3232
## Unreleased
3333
* Fixed a bug caused by having reference properties (such as `ObjectValue.Value`) that point to an Instance not included in syncback. ([#1179])
34+
* Fixed instance replacement fallback failing when too many instances needed to be replaced. ([#1192])
3435

3536
[#1179]: https://github.com/rojo-rbx/rojo/pull/1179
37+
[#1192]: https://github.com/rojo-rbx/rojo/pull/1192
3638

3739
## [7.7.0-rc.1] (November 27th, 2025)
3840

plugin/src/ApiContext.lua

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -290,31 +290,39 @@ function ApiContext:open(id)
290290
end
291291

292292
function ApiContext:serialize(ids: { string })
293-
local url = ("%s/api/serialize/%s"):format(self.__baseUrl, table.concat(ids, ","))
293+
local url = ("%s/api/serialize"):format(self.__baseUrl)
294+
local request_body = Http.jsonEncode({ sessionId = self.__sessionId, ids = ids })
294295

295-
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
296-
if body.sessionId ~= self.__sessionId then
297-
return Promise.reject("Server changed ID")
298-
end
296+
return Http.post(url, request_body)
297+
:andThen(rejectFailedRequests)
298+
:andThen(Http.Response.json)
299+
:andThen(function(response_body)
300+
if response_body.sessionId ~= self.__sessionId then
301+
return Promise.reject("Server changed ID")
302+
end
299303

300-
assert(validateApiSerialize(body))
304+
assert(validateApiSerialize(response_body))
301305

302-
return body
303-
end)
306+
return response_body
307+
end)
304308
end
305309

306310
function ApiContext:refPatch(ids: { string })
307-
local url = ("%s/api/ref-patch/%s"):format(self.__baseUrl, table.concat(ids, ","))
311+
local url = ("%s/api/ref-patch"):format(self.__baseUrl)
312+
local request_body = Http.jsonEncode({ sessionId = self.__sessionId, ids = ids })
308313

309-
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
310-
if body.sessionId ~= self.__sessionId then
311-
return Promise.reject("Server changed ID")
312-
end
314+
return Http.post(url, request_body)
315+
:andThen(rejectFailedRequests)
316+
:andThen(Http.Response.json)
317+
:andThen(function(response_body)
318+
if response_body.sessionId ~= self.__sessionId then
319+
return Promise.reject("Server changed ID")
320+
end
313321

314-
assert(validateApiRefPatch(body))
322+
assert(validateApiRefPatch(response_body))
315323

316-
return body
317-
end)
324+
return response_body
325+
end)
318326
end
319327

320328
return ApiContext

src/web/api.rs

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
//! Defines Rojo's HTTP API, all under /api. These endpoints generally return
22
//! JSON.
33
4-
use std::{
5-
collections::{HashMap, HashSet},
6-
fs,
7-
path::PathBuf,
8-
str::FromStr,
9-
sync::Arc,
10-
};
4+
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr, sync::Arc};
115

126
use futures::{sink::SinkExt, stream::StreamExt};
137
use hyper::{body, Body, Method, Request, Response, StatusCode};
@@ -30,7 +24,10 @@ use crate::{
3024
},
3125
util::{json, json_ok},
3226
},
33-
web_api::{BufferEncode, InstanceUpdate, RefPatchResponse, SerializeResponse},
27+
web_api::{
28+
BufferEncode, InstanceUpdate, RefPatchRequest, RefPatchResponse, SerializeRequest,
29+
SerializeResponse,
30+
},
3431
};
3532

3633
pub async fn call(serve_session: Arc<ServeSession>, mut request: Request<Body>) -> Response<Body> {
@@ -53,12 +50,8 @@ pub async fn call(serve_session: Arc<ServeSession>, mut request: Request<Body>)
5350
)
5451
}
5552
}
56-
(&Method::GET, path) if path.starts_with("/api/serialize/") => {
57-
service.handle_api_serialize(request).await
58-
}
59-
(&Method::GET, path) if path.starts_with("/api/ref-patch/") => {
60-
service.handle_api_ref_patch(request).await
61-
}
53+
(&Method::POST, "/api/serialize") => service.handle_api_serialize(request).await,
54+
(&Method::POST, "/api/ref-patch") => service.handle_api_ref_patch(request).await,
6255

6356
(&Method::POST, path) if path.starts_with("/api/open/") => {
6457
service.handle_api_open(request).await
@@ -229,22 +222,30 @@ impl ApiService {
229222
/// that correspond to the requested Instances. These values have their
230223
/// `Value` property set to point to the requested Instance.
231224
async fn handle_api_serialize(&self, request: Request<Body>) -> Response<Body> {
232-
let argument = &request.uri().path()["/api/serialize/".len()..];
233-
let requested_ids: Result<Vec<Ref>, _> = argument.split(',').map(Ref::from_str).collect();
225+
let session_id = self.serve_session.session_id();
226+
let body = body::to_bytes(request.into_body()).await.unwrap();
234227

235-
let requested_ids = match requested_ids {
236-
Ok(ids) => ids,
237-
Err(_) => {
228+
let request: SerializeRequest = match json::from_slice(&body) {
229+
Ok(request) => request,
230+
Err(err) => {
238231
return json(
239-
ErrorResponse::bad_request("Malformed ID list"),
232+
ErrorResponse::bad_request(format!("Invalid body: {}", err)),
240233
StatusCode::BAD_REQUEST,
241234
);
242235
}
243236
};
237+
238+
if request.session_id != session_id {
239+
return json(
240+
ErrorResponse::bad_request("Wrong session ID"),
241+
StatusCode::BAD_REQUEST,
242+
);
243+
}
244+
244245
let mut response_dom = WeakDom::new(InstanceBuilder::new("Folder"));
245246

246247
let tree = self.serve_session.tree();
247-
for id in &requested_ids {
248+
for id in &request.ids {
248249
if let Some(instance) = tree.get_instance(*id) {
249250
let clone = response_dom.insert(
250251
Ref::none(),
@@ -290,20 +291,26 @@ impl ApiService {
290291
/// and referent properties need to be updated after the serialize
291292
/// endpoint is used.
292293
async fn handle_api_ref_patch(self, request: Request<Body>) -> Response<Body> {
293-
let argument = &request.uri().path()["/api/ref-patch/".len()..];
294-
let requested_ids: Result<HashSet<Ref>, _> =
295-
argument.split(',').map(Ref::from_str).collect();
294+
let session_id = self.serve_session.session_id();
295+
let body = body::to_bytes(request.into_body()).await.unwrap();
296296

297-
let requested_ids = match requested_ids {
298-
Ok(ids) => ids,
299-
Err(_) => {
297+
let request: RefPatchRequest = match json::from_slice(&body) {
298+
Ok(request) => request,
299+
Err(err) => {
300300
return json(
301-
ErrorResponse::bad_request("Malformed ID list"),
301+
ErrorResponse::bad_request(format!("Invalid body: {}", err)),
302302
StatusCode::BAD_REQUEST,
303303
);
304304
}
305305
};
306306

307+
if request.session_id != session_id {
308+
return json(
309+
ErrorResponse::bad_request("Wrong session ID"),
310+
StatusCode::BAD_REQUEST,
311+
);
312+
}
313+
307314
let mut instance_updates: HashMap<Ref, InstanceUpdate> = HashMap::new();
308315

309316
let tree = self.serve_session.tree();
@@ -312,7 +319,7 @@ impl ApiService {
312319
let Variant::Ref(prop_value) = prop_value else {
313320
continue;
314321
};
315-
if let Some(target_id) = requested_ids.get(prop_value) {
322+
if let Some(target_id) = request.ids.get(prop_value) {
316323
let instance_id = instance.id();
317324
let update =
318325
instance_updates

src/web/interface.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,13 @@ pub struct OpenResponse {
238238
pub session_id: SessionId,
239239
}
240240

241+
#[derive(Debug, Serialize, Deserialize)]
242+
#[serde(rename_all = "camelCase")]
243+
pub struct SerializeRequest {
244+
pub session_id: SessionId,
245+
pub ids: Vec<Ref>,
246+
}
247+
241248
#[derive(Debug, Serialize, Deserialize)]
242249
#[serde(rename_all = "camelCase")]
243250
pub struct SerializeResponse {
@@ -269,6 +276,13 @@ impl BufferEncode {
269276
}
270277
}
271278

279+
#[derive(Debug, Serialize, Deserialize)]
280+
#[serde(rename_all = "camelCase")]
281+
pub struct RefPatchRequest {
282+
pub session_id: SessionId,
283+
pub ids: HashSet<Ref>,
284+
}
285+
272286
#[derive(Debug, Serialize, Deserialize)]
273287
#[serde(rename_all = "camelCase")]
274288
pub struct RefPatchResponse<'a> {

tests/rojo_test/serve_util.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::{
2-
fmt::Write as _,
32
fs,
43
path::{Path, PathBuf},
54
process::Command,
@@ -13,8 +12,12 @@ use rbx_dom_weak::types::Ref;
1312

1413
use tempfile::{tempdir, TempDir};
1514

16-
use librojo::web_api::{
17-
ReadResponse, SerializeResponse, ServerInfoResponse, SocketPacket, SocketPacketType,
15+
use librojo::{
16+
web_api::{
17+
ReadResponse, SerializeRequest, SerializeResponse, ServerInfoResponse, SocketPacket,
18+
SocketPacketType,
19+
},
20+
SessionId,
1821
};
1922
use rojo_insta_ext::RedactionMap;
2023

@@ -226,16 +229,19 @@ impl TestServeSession {
226229
}
227230
}
228231

229-
pub fn get_api_serialize(&self, ids: &[Ref]) -> Result<SerializeResponse, reqwest::Error> {
230-
let mut id_list = String::with_capacity(ids.len() * 33);
231-
for id in ids {
232-
write!(id_list, "{id},").unwrap();
233-
}
234-
id_list.pop();
235-
236-
let url = format!("http://localhost:{}/api/serialize/{}", self.port, id_list);
237-
238-
reqwest::blocking::get(url)?.json()
232+
pub fn get_api_serialize(
233+
&self,
234+
ids: &[Ref],
235+
session_id: SessionId,
236+
) -> Result<SerializeResponse, reqwest::Error> {
237+
let client = reqwest::blocking::Client::new();
238+
let url = format!("http://localhost:{}/api/serialize", self.port);
239+
let body = serde_json::to_string(&SerializeRequest {
240+
session_id,
241+
ids: ids.to_vec(),
242+
});
243+
244+
client.post(url).body((body).unwrap()).send()?.json()
239245
}
240246
}
241247

tests/tests/serve.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ fn meshpart_with_id() {
646646
.unwrap();
647647

648648
let serialize_response = session
649-
.get_api_serialize(&[*meshpart, *objectvalue])
649+
.get_api_serialize(&[*meshpart, *objectvalue], info.session_id)
650650
.unwrap();
651651

652652
// We don't assert a snapshot on the SerializeResponse because the model includes the
@@ -673,7 +673,9 @@ fn forced_parent() {
673673
read_response.intern_and_redact(&mut redactions, root_id)
674674
);
675675

676-
let serialize_response = session.get_api_serialize(&[root_id]).unwrap();
676+
let serialize_response = session
677+
.get_api_serialize(&[root_id], info.session_id)
678+
.unwrap();
677679

678680
assert_eq!(serialize_response.session_id, info.session_id);
679681

0 commit comments

Comments
 (0)