Skip to content

Commit 2d89530

Browse files
Partial document struct for updates (#53)
* DocumentPartial * xtask preprocess field * Clippy allow derivable_impls * Comment unused * CI exclude xtask * DocumentPartial skip id field * Derive root crate path * fixed open-api template generating unnecessary comment This fix is about vendor extension scoping problem * Clippy allow derivable_impls * tests for bulk operations: `import_jsonl`, `export_jsonl`, bulk update and bulk delete Also renamed `documents().export()` to `export_jsonl()` * Refactor tests to `expect()` --------- Co-authored-by: Hayden Hung Hoang <[email protected]>
1 parent 3c5dffd commit 2d89530

File tree

18 files changed

+373
-310
lines changed

18 files changed

+373
-310
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
uses: actions-rs/cargo@v1
6060
with:
6161
command: build
62-
args: --all-features --target ${{ matrix.platform.target }}
62+
args: --all-features --target ${{ matrix.platform.target }} --workspace --exclude xtask
6363
- name: Tests
6464
if: matrix.platform.target != 'wasm32-unknown-unknown'
6565
uses: actions-rs/cargo@v1

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@ jobs:
4545
uses: actions-rs/cargo@v1
4646
with:
4747
command: clippy
48-
args: --all-features -- -Drust-2018-idioms -Dwarnings
48+
args: --all-features -- -Drust-2018-idioms -Drust-2024-compatibility -Dwarnings

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ When updating or adding new parameters and endpoints, make changes directly in t
1212
Once your changes are merged, you can update this project as follows (you can also run tasks individually):
1313

1414
```bash
15-
cargo xtask fetch code-gen
15+
cargo xtask fetch preprocess code-gen
1616
```
1717

1818
This will:
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
#![allow(unused_imports)]
1+
#![allow(clippy::derivable_impls)]
22
#![allow(clippy::empty_docs)]
33
#![allow(clippy::needless_return)]
44
#![allow(elided_lifetimes_in_paths)]
5+
#![allow(unused_imports)]
56

67
pub mod apis;
78
pub mod models;

openapi-generator-template/reqwest/api.mustache

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,23 @@ use super::{Error, configuration, ContentType};
1212
{{#-first}}
1313
/// struct for passing parameters to the method [`{{operationId}}`]
1414
#[derive(Clone, Debug)]
15-
pub struct {{{operationIdCamelCase}}}Params {
15+
pub struct {{{operationIdCamelCase}}}Params{{!
16+
Iterate through ALL parameters in the operation.
17+
Only the requestBody has this extension defined, so it will print "<B>".
18+
The other parameters have nothing, so they will print nothing.
19+
This effectively extract the generic parameter from the requestBody
20+
and places it on the struct definition line.
21+
}}{{#allParams}}
22+
{{{vendorExtensions.x-rust-params-generic-parameter}}}
23+
{{/allParams}} {
1624
{{/-first}}
1725
{{#description}}
1826
/// {{{.}}}
1927
{{/description}}
28+
{{#vendorExtensions.x-rust-type}}
29+
pub {{{paramName}}}: {{{.}}},
30+
{{/vendorExtensions.x-rust-type}}
31+
{{^vendorExtensions.x-rust-type}}
2032
pub {{{paramName}}}: {{!
2133
### Option Start
2234
}}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{!
@@ -30,6 +42,7 @@ pub struct {{{operationIdCamelCase}}}Params {
3042
}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{!
3143
### Comma for next arguement
3244
}}{{^-last}},{{/-last}}
45+
{{/vendorExtensions.x-rust-type}}
3346
{{#-last}}
3447
}
3548

@@ -94,44 +107,10 @@ pub enum {{{operationIdCamelCase}}}Error {
94107
{{#vendorExtensions.x-group-parameters}}
95108
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}(configuration: &configuration::Configuration{{#allParams}}{{#-first}}, {{!
96109
### Params
97-
}}params: &{{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}{{!
110+
}}params: &{{{operationIdCamelCase}}}Params{{#allParams}}{{{vendorExtensions.x-rust-params-generic-parameter}}}{{/allParams}}{{/-first}}{{/allParams}}{{!
98111
### Function return type
99112
}}) -> Result<{{#vendorExtensions.x-rust-return-type}}{{{.}}}{{/vendorExtensions.x-rust-return-type}}{{^vendorExtensions.x-rust-return-type}}{{#isResponseFile}}{{#supportAsync}}reqwest::Response{{/supportAsync}}{{^supportAsync}}reqwest::blocking::Response{{/supportAsync}}{{/isResponseFile}}{{^isResponseFile}}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{/isResponseFile}}{{/vendorExtensions.x-rust-return-type}}, Error<{{{operationIdCamelCase}}}Error>> {
100113
{{/vendorExtensions.x-group-parameters}}
101-
{{^vendorExtensions.x-group-parameters}}
102-
pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensions.x-rust-generic-parameter}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{!
103-
### Option Start
104-
}}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{!
105-
### &str and Vec<&str>
106-
}}{{#isString}}{{#isArray}}Vec<{{/isArray}}{{^isUuid}}&str{{/isUuid}}{{#isArray}}>{{/isArray}}{{/isString}}{{!
107-
### UUIDs
108-
}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}&str{{#isArray}}>{{/isArray}}{{/isUuid}}{{!
109-
### Models and primative types
110-
}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{!
111-
### Option End
112-
}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{!
113-
### Comma for next arguement
114-
}}{{^-last}}, {{/-last}}{{/allParams}}{{!
115-
### Function return type
116-
}}) -> Result<{{#vendorExtensions.x-rust-return-type}}{{{vendorExtensions.x-rust-return-type}}}{{/vendorExtensions.x-rust-return-type}}{{^vendorExtensions.x-rust-return-type}}{{!
117-
### Response File Support
118-
}}{{#isResponseFile}}{{#supportAsync}}reqwest::Response{{/supportAsync}}{{^supportAsync}}reqwest::blocking::Response{{/supportAsync}}{{/isResponseFile}}{{!
119-
### Regular Responses
120-
}}{{^isResponseFile}}{{!
121-
### Multi response support
122-
}}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{!
123-
### Regular return type
124-
}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{{vendorExtensions.x-rust-return-type-generic-parameter}}}{{/supportMultipleResponses}}{{/isResponseFile}}{{/vendorExtensions.x-rust-return-type}}{{!
125-
### Error Type
126-
}}, Error<{{{operationIdCamelCase}}}Error>> {
127-
{{#allParams.0}}
128-
// add a prefix to parameters to efficiently prevent name collisions
129-
{{/allParams.0}}
130-
{{#allParams}}
131-
let {{{vendorExtensions.x-rust-param-identifier}}} = {{{paramName}}};
132-
{{/allParams}}
133-
{{/vendorExtensions.x-group-parameters}}
134-
135114
let uri_str = format!("{}{{{path}}}", configuration.base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode(&{{/isString}}{{{vendorExtensions.x-rust-param-identifier}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isArray}}.join(",").as_ref(){{/isArray}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}.to_string(){{/isContainer}}{{/isPrimitiveType}}{{/isUuid}}{{/isString}}{{#isString}}){{/isString}}{{/pathParams}});
136115
let mut req_builder = configuration.client.request(reqwest::Method::{{{httpMethod}}}, &uri_str);
137116

@@ -513,4 +492,4 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}{{{vendorExtensi
513492
}
514493

515494
{{/operation}}
516-
{{/operations}}
495+
{{/operations}}

preprocessed_openapi.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ paths:
348348
description: Can be any key-value pair
349349
x-go-type: interface{}
350350
required: true
351+
x-rust-params-generic-parameter: <B>
352+
x-rust-type: B
353+
x-rust-generic-parameter: '<B: Serialize>'
351354
delete:
352355
tags:
353356
- documents
@@ -1253,6 +1256,8 @@ paths:
12531256
description: Can be any key-value pair
12541257
x-go-type: interface{}
12551258
required: true
1259+
x-rust-params-generic-parameter: <B>
1260+
x-rust-type: B
12561261
responses:
12571262
'200':
12581263
description: The document referenced by the ID was updated
@@ -1267,6 +1272,7 @@ paths:
12671272
application/json:
12681273
schema:
12691274
$ref: '#/components/schemas/ApiResponse'
1275+
x-rust-generic-parameter: '<B: Serialize>'
12701276
delete:
12711277
tags:
12721278
- documents

typesense/src/client/collection/document.rs

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! via a parent `Collection` struct, for example:
55
//! `client.collection::<Book>().document("123")`
66
7-
use crate::{Client, Error, execute_wrapper};
7+
use crate::{Client, Error, execute_wrapper, traits};
88
use serde::{Serialize, de::DeserializeOwned};
99
use typesense_codegen::apis::documents_api;
1010

@@ -51,10 +51,32 @@ where
5151

5252
let result_value = execute_wrapper!(self, documents_api::get_document, params)?;
5353

54-
// Deserialize the raw JSON value into the user's type T.
54+
// Deserialize the raw JSON value into the user's type D.
5555
serde_json::from_value(result_value).map_err(Error::from)
5656
}
5757

58+
/// Deletes this individual document from the collection.
59+
/// The deleted document is returned.
60+
///
61+
/// # Returns
62+
/// A `Result` containing the deleted document deserialized into `D`.
63+
pub async fn delete(&self) -> Result<D, Error<documents_api::DeleteDocumentError>> {
64+
let params = documents_api::DeleteDocumentParams {
65+
collection_name: self.collection_name.to_owned(),
66+
document_id: self.document_id.to_owned(),
67+
};
68+
69+
let result_value = execute_wrapper!(self, documents_api::delete_document, params)?;
70+
71+
// Deserialize the raw JSON value of the deleted document into T.
72+
serde_json::from_value(result_value).map_err(Error::from)
73+
}
74+
}
75+
76+
impl<'c, 'n, D> Document<'c, 'n, D>
77+
where
78+
D: traits::Document,
79+
{
5880
/// Updates this individual document. The update can be partial.
5981
/// The updated full document is returned.
6082
///
@@ -68,9 +90,9 @@ where
6890
/// # Example
6991
/// ```no_run
7092
/// # use serde::{Serialize, Deserialize};
71-
/// # use typesense::{Client, models};
93+
/// # use typesense::{Client, Typesense, models};
7294
/// # use reqwest::Url;
73-
/// # #[derive(Serialize, Deserialize)]
95+
/// # #[derive(Typesense, Serialize, Deserialize)]
7496
/// # struct Book { id: String, title: String, pages: i32 }
7597
/// #
7698
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
@@ -79,7 +101,7 @@ where
79101
/// # .api_key("xyz")
80102
/// # .build()
81103
/// # .unwrap();
82-
/// let book_update = serde_json::json!({ "pages": 654 });
104+
/// let book_update = BookPartial { pages: Some(654), ..Default::default() };
83105
///
84106
/// // Simple update
85107
/// let updated_book = client.collection_named::<Book>("books").document("123")
@@ -97,15 +119,15 @@ where
97119
/// # Ok(())
98120
/// # }
99121
/// ```
100-
pub async fn update<U: Serialize>(
122+
pub async fn update(
101123
&self,
102-
partial_document: U,
124+
partial_document: &D::Partial,
103125
params: Option<crate::models::DocumentIndexParameters>,
104126
) -> Result<D, Error<documents_api::UpdateDocumentError>> {
105127
let params = documents_api::UpdateDocumentParams {
106128
collection_name: self.collection_name.to_owned(),
107129
document_id: self.document_id.to_owned(),
108-
body: serde_json::to_value(partial_document)?,
130+
body: partial_document,
109131
dirty_values: params.and_then(|d| d.dirty_values),
110132
};
111133

@@ -114,21 +136,4 @@ where
114136
// Deserialize the raw JSON value of the updated document into T.
115137
serde_json::from_value(result_value).map_err(Error::from)
116138
}
117-
118-
/// Deletes this individual document from the collection.
119-
/// The deleted document is returned.
120-
///
121-
/// # Returns
122-
/// A `Result` containing the deleted document deserialized into `D`.
123-
pub async fn delete(&self) -> Result<D, Error<documents_api::DeleteDocumentError>> {
124-
let params = documents_api::DeleteDocumentParams {
125-
collection_name: self.collection_name.to_owned(),
126-
document_id: self.document_id.to_owned(),
127-
};
128-
129-
let result_value = execute_wrapper!(self, documents_api::delete_document, params)?;
130-
131-
// Deserialize the raw JSON value of the deleted document into T.
132-
serde_json::from_value(result_value).map_err(Error::from)
133-
}
134139
}

0 commit comments

Comments
 (0)