From 0546434481fd5febdb6c63b232da51741bea57de Mon Sep 17 00:00:00 2001 From: Seto Elkahfi <1797197+setoelkahfi@users.noreply.github.com> Date: Mon, 12 Oct 2026 00:29:14 +0200 Subject: [PATCH] fix(aso): omit null attributes in beta app review submit body Apple's betaAppReviewSubmissions endpoint 422s (ENTITY_UNPROCESSABLE) when data.attributes is present but null. The create body built its POST with the generic CreateBody/CreateData whose attributes: () serialized to "attributes": null. Introduce dedicated local CreateBody/CreateData/CreateRelationships carrying only the build relationship (no attributes field), mirroring the review_submission.rs pattern. Replace the misleading test that asserted the buggy null with one asserting attributes is absent. Co-Authored-By: siGit Code v1.6.7-acp --- crates/aso/src/beta_app_review_submission.rs | 52 +++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/crates/aso/src/beta_app_review_submission.rs b/crates/aso/src/beta_app_review_submission.rs index 948ddf1..a8fa03d 100644 --- a/crates/aso/src/beta_app_review_submission.rs +++ b/crates/aso/src/beta_app_review_submission.rs @@ -14,9 +14,7 @@ use async_trait::async_trait; use reqwest::Method; use serde::{Deserialize, Serialize}; -use smbcloud_ascapi_core::jsonapi::{ - CreateBody, CreateData, Document, ListDocument, Resource, ResourceId, ToOne, -}; +use smbcloud_ascapi_core::jsonapi::{Document, ListDocument, Resource, ResourceId, ToOne}; use smbcloud_ascapi_core::Client; use smbcloud_ascapi_core::Result; @@ -51,6 +49,23 @@ pub type BetaAppReviewSubmission = Resource; // ── internal create helper ──────────────────────────────────────────────────── +/// A beta app review submission carries only a `build` relationship. Apple's +/// endpoint rejects a request whose `data.attributes` is present but null +/// (422 `ENTITY_UNPROCESSABLE`), so this body omits the field entirely rather +/// than reusing [`smbcloud_ascapi_core::jsonapi::CreateBody`] with `()` +/// attributes. +#[derive(Debug, Clone, Serialize)] +struct CreateBody { + data: CreateData, +} + +#[derive(Debug, Clone, Serialize)] +struct CreateData { + #[serde(rename = "type")] + resource_type: &'static str, + relationships: CreateRelationships, +} + #[derive(Debug, Clone, Serialize)] struct CreateRelationships { build: ToOne, @@ -99,15 +114,14 @@ impl BetaAppReviewSubmissionsApi for Client { let body = CreateBody { data: CreateData { resource_type: RESOURCE_TYPE, - attributes: (), - relationships: Some(CreateRelationships { + relationships: CreateRelationships { build: ToOne { data: ResourceId { resource_type: crate::build::RESOURCE_TYPE, id: build_id.to_string(), }, }, - }), + }, }, }; let doc: Document = self @@ -196,17 +210,31 @@ mod tests { } #[test] - fn create_body_attributes_unit_serializes() { - // The create body has no attributes — verify `()` round-trips cleanly. + fn create_body_omits_attributes() { + // Apple's endpoint 422s on a null `data.attributes`; the body must omit + // the field entirely and carry only the `build` relationship. let body = CreateBody { data: CreateData { resource_type: RESOURCE_TYPE, - attributes: (), - relationships: None::<()>, + relationships: CreateRelationships { + build: ToOne { + data: ResourceId { + resource_type: crate::build::RESOURCE_TYPE, + id: "BUILD_ID".to_string(), + }, + }, + }, }, }; let v = serde_json::to_value(&body).unwrap(); - // `()` serialises as `null`; the key is present in the object. - assert!(v["data"]["attributes"].is_null()); + assert!( + v["data"].get("attributes").is_none(), + "attributes must be omitted, not null: {v}" + ); + assert_eq!(v["data"]["type"], RESOURCE_TYPE); + assert_eq!( + v["data"]["relationships"]["build"]["data"]["id"], + "BUILD_ID" + ); } }