Skip to main content

hanzo_client/apis/
author_api.rs

1/*
2 * Hanzo Cloud API
3 *
4 * The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
5 *
6 * The version of the OpenAPI document: v1
7 * 
8 * Generated by: https://openapi-generator.tech
9 */
10
11
12use reqwest;
13use serde::{Deserialize, Serialize, de::Error as _};
14use crate::{apis::ResponseContent, models};
15use super::{Error, configuration, ContentType};
16
17
18/// struct for typed errors of method [`get_author`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum GetAuthorError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`get_author_basis`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum GetAuthorBasisError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`post_author_connect`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum PostAuthorConnectError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`post_author_deploys_record`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum PostAuthorDeploysRecordError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`post_author_repos_verify`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum PostAuthorReposVerifyError {
50    UnknownValue(serde_json::Value),
51}
52
53
54/// Returns the caller's author-program dashboard: enrolment status, linked forge login, verified repositories and owner-wide claims, recorded deploys, accrued / pending / paid royalty, and the payout history.  It answers ONE OF TWO SHAPES from this address. An org that has never connected gets {\"isAuthor\": false, \"defaultShareBps\", \"badgeBase\"} — an honest \"not enrolled\" rather than a 404, so the console can render the connect form. An enrolled org gets the dashboard: isAuthor, id, status, githubLogin, verified, verifyCode, verifyFile, verifySnippet, shareBps, badgeBase, repos, orgs, deploys, accruedCents, pendingCents, paidCents, payouts and ledger.  For an APPROVED author this read ALSO runs the accrual sweep opportunistically, so the dashboard is self-updating. That is why the royalty AUDIT lives at its own address: an audit must not move the money it is auditing.
55pub async fn get_author(configuration: &configuration::Configuration, ) -> Result<std::collections::HashMap<String, serde_json::Value>, Error<GetAuthorError>> {
56
57    let uri_str = format!("{}/v1/author", configuration.base_path);
58    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
59
60    if let Some(ref user_agent) = configuration.user_agent {
61        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
62    }
63    if let Some(ref token) = configuration.bearer_access_token {
64        req_builder = req_builder.bearer_auth(token.to_owned());
65    };
66
67    let req = req_builder.build()?;
68    let resp = configuration.client.execute(req).await?;
69
70    let status = resp.status();
71    let content_type = resp
72        .headers()
73        .get("content-type")
74        .and_then(|v| v.to_str().ok())
75        .unwrap_or("application/octet-stream");
76    let content_type = super::ContentType::from(content_type);
77
78    if !status.is_client_error() && !status.is_server_error() {
79        let content = resp.text().await?;
80        match content_type {
81            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
82            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `std::collections::HashMap&lt;String, serde_json::Value&gt;`"))),
83            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `std::collections::HashMap&lt;String, serde_json::Value&gt;`")))),
84        }
85    } else {
86        let content = resp.text().await?;
87        let entity: Option<GetAuthorError> = serde_json::from_str(&content).ok();
88        Err(Error::ResponseError(ResponseContent { status, content, entity }))
89    }
90}
91
92/// Returns the AUDIT TRAIL behind the caller's own royalty: every ledger row with the spend it was computed from, the share applied at the time, the platform's matching half, whether each row satisfies the formula, and the attribution edges that already existed when the row was written.  It answers ONE OF TWO SHAPES. An org that has never connected gets {\"isAuthor\": false, \"defaultShareBps\"} — never a 404, which would answer \"is this org an author?\" for anyone who asked. An enrolled org gets the basis: isAuthor, id, status, asOf, shareBps, platformShareBps, defaultShareBps, shareSource, settlesTo, method (the formula, the rate card and the sizing), ledger, reconciliation, window, and period when one was requested.  This read NEVER sweeps, and that is the point of it being a separate address from the dashboard: an audit must not move the money it is auditing, so calling it N times leaves the balances and the ledger byte-identical.
93pub async fn get_author_basis(configuration: &configuration::Configuration, period: Option<&str>) -> Result<std::collections::HashMap<String, serde_json::Value>, Error<GetAuthorBasisError>> {
94    // add a prefix to parameters to efficiently prevent name collisions
95    let p_period = period;
96
97    let uri_str = format!("{}/v1/author/basis", configuration.base_path);
98    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
99
100    if let Some(ref param_value) = p_period {
101        req_builder = req_builder.query(&[("period", &param_value.to_string())]);
102    }
103    if let Some(ref user_agent) = configuration.user_agent {
104        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
105    }
106    if let Some(ref token) = configuration.bearer_access_token {
107        req_builder = req_builder.bearer_auth(token.to_owned());
108    };
109
110    let req = req_builder.build()?;
111    let resp = configuration.client.execute(req).await?;
112
113    let status = resp.status();
114    let content_type = resp
115        .headers()
116        .get("content-type")
117        .and_then(|v| v.to_str().ok())
118        .unwrap_or("application/octet-stream");
119    let content_type = super::ContentType::from(content_type);
120
121    if !status.is_client_error() && !status.is_server_error() {
122        let content = resp.text().await?;
123        match content_type {
124            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
125            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `std::collections::HashMap&lt;String, serde_json::Value&gt;`"))),
126            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `std::collections::HashMap&lt;String, serde_json::Value&gt;`")))),
127        }
128    } else {
129        let content = resp.text().await?;
130        let entity: Option<GetAuthorBasisError> = serde_json::from_str(&content).ok();
131        Err(Error::ResponseError(ResponseContent { status, content, entity }))
132    }
133}
134
135/// Enrols the caller's org in the author program at status \"connected\" and returns its enrolment, including the verify code the file method needs. It is IDEMPOTENT: a second call returns the same enrolment rather than a conflict.  The forge login is taken from IAM's LINKED account for the provider when there is one — that is identity proof, not a claim — and only otherwise from the login in the body, which then has to be proven per repository. Connecting does not admit an org to earning: a platform reviewer approves that separately.  Answers 201 when it enrolled the org and 200 when it found an existing enrolment.
136pub async fn post_author_connect(configuration: &configuration::Configuration, connect_request: models::ConnectRequest) -> Result<models::Enrolment, Error<PostAuthorConnectError>> {
137    // add a prefix to parameters to efficiently prevent name collisions
138    let p_connect_request = connect_request;
139
140    let uri_str = format!("{}/v1/author/connect", configuration.base_path);
141    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
142
143    if let Some(ref user_agent) = configuration.user_agent {
144        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
145    }
146    if let Some(ref token) = configuration.bearer_access_token {
147        req_builder = req_builder.bearer_auth(token.to_owned());
148    };
149    req_builder = req_builder.json(&p_connect_request);
150
151    let req = req_builder.build()?;
152    let resp = configuration.client.execute(req).await?;
153
154    let status = resp.status();
155    let content_type = resp
156        .headers()
157        .get("content-type")
158        .and_then(|v| v.to_str().ok())
159        .unwrap_or("application/octet-stream");
160    let content_type = super::ContentType::from(content_type);
161
162    if !status.is_client_error() && !status.is_server_error() {
163        let content = resp.text().await?;
164        match content_type {
165            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
166            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Enrolment`"))),
167            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Enrolment`")))),
168        }
169    } else {
170        let content = resp.text().await?;
171        let entity: Option<PostAuthorConnectError> = serde_json::from_str(&content).ok();
172        Err(Error::ResponseError(ResponseContent { status, content, entity }))
173    }
174}
175
176/// Records that the caller's org deployed a project built from a source repository, which is the edge that makes an author's work earn royalty.  It is deliberately NOT an error for a deploy to attribute to nobody: a project built from no repository, or from one no author has verified, answers {\"recorded\": false, \"reason\"} so a deploy pipeline can fire this on every deploy without branching. Attribution resolves per-repository first, then owner-wide, so a repository with its own claim always earns for its own author.  A deploy of a Hanzo-maintained template attributes to the platform treasury, and a self-deploy (the author's own org deploying its own repository) is recorded for provenance but excluded from accrual. The edge is idempotent per repository+project+org.  Answers 201 when it recorded a new edge and 200 otherwise.
177pub async fn post_author_deploys_record(configuration: &configuration::Configuration, deploy_request: models::DeployRequest) -> Result<models::DeployRecord, Error<PostAuthorDeploysRecordError>> {
178    // add a prefix to parameters to efficiently prevent name collisions
179    let p_deploy_request = deploy_request;
180
181    let uri_str = format!("{}/v1/author/deploys/record", configuration.base_path);
182    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
183
184    if let Some(ref user_agent) = configuration.user_agent {
185        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
186    }
187    if let Some(ref token) = configuration.bearer_access_token {
188        req_builder = req_builder.bearer_auth(token.to_owned());
189    };
190    req_builder = req_builder.json(&p_deploy_request);
191
192    let req = req_builder.build()?;
193    let resp = configuration.client.execute(req).await?;
194
195    let status = resp.status();
196    let content_type = resp
197        .headers()
198        .get("content-type")
199        .and_then(|v| v.to_str().ok())
200        .unwrap_or("application/octet-stream");
201    let content_type = super::ContentType::from(content_type);
202
203    if !status.is_client_error() && !status.is_server_error() {
204        let content = resp.text().await?;
205        match content_type {
206            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
207            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DeployRecord`"))),
208            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DeployRecord`")))),
209        }
210    } else {
211        let content = resp.text().await?;
212        let entity: Option<PostAuthorDeploysRecordError> = serde_json::from_str(&content).ok();
213        Err(Error::ResponseError(ResponseContent { status, content, entity }))
214    }
215}
216
217/// Proves that the caller owns a repository — or a whole OWNER — and records the claim, which is what makes deploys of that code earn royalty.  Ownership is proven the SAME two ways in both cases, tried in order: an IAM-linked forge token with admin or push permission, or a hanzo.json on the default branch carrying the author's verify code. Claiming an OWNER proves it against that owner's \".github\" control repository, and is exactly as strong as a per-repository claim — an owner the caller cannot prove is refused with 422, never assumed.  A per-repository claim wins over an owner-wide one, so a specifically-claimed repository always earns for its own author. A repository another author has already verified is a 409. The org must have connected first.  Answers 201 when it recorded a new claim and 200 when the claim already existed.
218pub async fn post_author_repos_verify(configuration: &configuration::Configuration, verify_request: models::VerifyRequest) -> Result<models::Claim, Error<PostAuthorReposVerifyError>> {
219    // add a prefix to parameters to efficiently prevent name collisions
220    let p_verify_request = verify_request;
221
222    let uri_str = format!("{}/v1/author/repos/verify", configuration.base_path);
223    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
224
225    if let Some(ref user_agent) = configuration.user_agent {
226        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
227    }
228    if let Some(ref token) = configuration.bearer_access_token {
229        req_builder = req_builder.bearer_auth(token.to_owned());
230    };
231    req_builder = req_builder.json(&p_verify_request);
232
233    let req = req_builder.build()?;
234    let resp = configuration.client.execute(req).await?;
235
236    let status = resp.status();
237    let content_type = resp
238        .headers()
239        .get("content-type")
240        .and_then(|v| v.to_str().ok())
241        .unwrap_or("application/octet-stream");
242    let content_type = super::ContentType::from(content_type);
243
244    if !status.is_client_error() && !status.is_server_error() {
245        let content = resp.text().await?;
246        match content_type {
247            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
248            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Claim`"))),
249            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Claim`")))),
250        }
251    } else {
252        let content = resp.text().await?;
253        let entity: Option<PostAuthorReposVerifyError> = serde_json::from_str(&content).ok();
254        Err(Error::ResponseError(ResponseContent { status, content, entity }))
255    }
256}
257