1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use reqwest::{Client, RequestBuilder};
use serde::Serialize;

#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Row<T: Serialize> {
    /// [Optional] A unique ID for each row. BigQuery uses this
    /// property to detect duplicate insertion requests on a best-effort basis.
    pub insert_id: Option<String>,

    /// [Required] A JSON object that contains a row of data. The
    /// object's properties and values must match the destination table's schema.
    pub json: T,
}

#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InsertAllRequest<T: Serialize> {
    /// Optional. Insert all valid rows of a request, even if invalid rows exist.
    /// The default value is false, which causes the entire request to fail if any invalid rows exist.
    pub skip_invalid_rows: Option<bool>,
    /// Optional. Accept rows that contain values that do not match the schema.
    /// The unknown values are ignored. Default is false, which treats unknown values as errors.
    pub ignore_unknown_values: Option<bool>,
    /// Optional. If specified, treats the destination table as a base template, and inserts the rows into an instance table named "{destination}{templateSuffix}". BigQuery will manage creation of the instance table, using the schema of the base template table.
    /// See https://cloud.google.com/bigquery/streaming-data-into-bigquery#template-tables for considerations when working with templates tables.
    pub template_suffix: Option<String>,
    /// Data to insert
    pub rows: Vec<Row<T>>,
    /// Optional. Unique request trace id. Used for debugging purposes only.
    /// It is case-sensitive, limited to up to 36 ASCII characters. A UUID is recommended.
    pub trace_id: Option<String>,
}

impl<T: Serialize> Default for InsertAllRequest<T> {
    fn default() -> Self {
        Self {
            skip_invalid_rows: None,
            ignore_unknown_values: None,
            template_suffix: None,
            rows: vec![],
            trace_id: None,
        }
    }
}

#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Default, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ErrorMessage {
    /// A short error code that summarizes the error.
    pub reason: String,
    /// Specifies where the error occurred, if present.
    pub location: String,
    /// Debugging information. This property is internal to Google and should not be used.
    pub debug_info: String,
    /// A human-readable description of the error.
    pub message: String,
}

#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Default, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Error {
    pub index: i32,
    pub errors: Vec<ErrorMessage>,
}

#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Default, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InsertAllResponse {
    pub kind: String,
    pub insert_errors: Option<Vec<Error>>,
}

pub fn build<T: Serialize>(
    base_url: &str,
    client: &Client,
    project_id: &str,
    dataset_id: &str,
    table_id: &str,
    data: &InsertAllRequest<T>,
) -> RequestBuilder {
    let url = format!(
        "{}/projects/{}/datasets/{}/tables/{}/insertAll",
        base_url, project_id, dataset_id, table_id
    );
    client.post(url).json(data)
}