Skip to main content

smbcloud_ascapi_core/
jsonapi.rs

1//! Shared JSON:API envelope types used across every App Metadata resource.
2//! Resource-specific attribute structs live in their own modules
3//! (`app`, `app_store_version`, ...) and plug into these generics.
4
5use serde::{Deserialize, Serialize};
6
7/// A single-resource JSON:API document — the body of e.g. `GET
8/// /v1/apps/{id}` or the response to a `POST`/`PATCH`.
9#[derive(Debug, Clone, Deserialize)]
10pub struct Document<A> {
11    pub data: Resource<A>,
12}
13
14/// A resource-collection JSON:API document — the body of e.g. `GET
15/// /v1/apps`.
16#[derive(Debug, Clone, Deserialize)]
17pub struct ListDocument<A> {
18    pub data: Vec<Resource<A>>,
19}
20
21/// A single JSON:API resource object: its id, type, and typed attributes.
22/// Relationships are intentionally left untyped (`serde_json::Value`) —
23/// this crate builds relationship payloads explicitly on requests rather
24/// than typing every relationship shape App Store Connect can return.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Resource<A> {
27    pub id: String,
28    #[serde(rename = "type")]
29    pub resource_type: String,
30    pub attributes: A,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub relationships: Option<serde_json::Value>,
33}
34
35/// A bare `{ type, id }` resource identifier, used to point a create
36/// request's relationships at an existing resource.
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct ResourceId {
39    #[serde(rename = "type")]
40    pub resource_type: &'static str,
41    pub id: String,
42}
43
44/// A to-one relationship payload, e.g. `relationships.app` on a
45/// `POST /v1/appStoreVersions` body.
46#[derive(Debug, Clone, Serialize)]
47pub struct ToOne {
48    pub data: ResourceId,
49}
50
51/// A to-many relationship payload, e.g. `relationships.certificates` on a
52/// `POST /v1/profiles` body.
53///
54/// Apple distinguishes an empty array from an absent relationship: sending
55/// `{"data": []}` for `devices` means "no devices", which a development
56/// profile rejects, while omitting the key entirely is what an App Store
57/// profile wants. Callers that mean "absent" should pass `None` for the
58/// whole field rather than an empty `ToMany`.
59#[derive(Debug, Clone, Serialize)]
60pub struct ToMany {
61    pub data: Vec<ResourceId>,
62}
63
64#[derive(Debug, Clone, Serialize)]
65pub struct CreateBody<A, R> {
66    pub data: CreateData<A, R>,
67}
68
69#[derive(Debug, Clone, Serialize)]
70pub struct CreateData<A, R> {
71    #[serde(rename = "type")]
72    pub resource_type: &'static str,
73    pub attributes: A,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub relationships: Option<R>,
76}
77
78#[derive(Debug, Clone, Serialize)]
79pub struct UpdateBody<A> {
80    pub data: UpdateData<A>,
81}
82
83/// A `PATCH` body that updates only relationships (no attributes) — e.g.
84/// attaching a `Build` to an `AppStoreVersion`.
85#[derive(Debug, Clone, Serialize)]
86pub struct UpdateRelationshipsBody<R> {
87    pub data: UpdateRelationshipsData<R>,
88}
89
90#[derive(Debug, Clone, Serialize)]
91pub struct UpdateRelationshipsData<R> {
92    #[serde(rename = "type")]
93    pub resource_type: &'static str,
94    pub id: String,
95    pub relationships: R,
96}
97
98#[derive(Debug, Clone, Serialize)]
99pub struct UpdateData<A> {
100    #[serde(rename = "type")]
101    pub resource_type: &'static str,
102    pub id: String,
103    pub attributes: A,
104}
105
106/// The `errors` envelope App Store Connect returns on non-2xx responses.
107#[derive(Debug, Clone, Deserialize, Default)]
108pub struct ErrorDocument {
109    #[serde(default)]
110    pub errors: Vec<ApiError>,
111}
112
113#[derive(Debug, Clone, Deserialize)]
114pub struct ApiError {
115    pub status: String,
116    pub code: String,
117    pub title: String,
118    #[serde(default)]
119    pub detail: String,
120}