diff options
-rw-r--r-- | TODO | 19 | ||||
-rw-r--r-- | fatcat-openapi2.yml | 54 | ||||
-rw-r--r-- | notes/autoaccept_api.txt | 31 | ||||
-rw-r--r-- | notes/cloud_instances.txt | 2 | ||||
-rw-r--r-- | python/fatcat/crossref_importer.py | 2 | ||||
-rw-r--r-- | python/fatcat/importer_common.py | 3 | ||||
-rw-r--r-- | python/fatcat/issn_importer.py | 2 | ||||
-rw-r--r-- | python/fatcat/orcid_importer.py | 2 | ||||
-rw-r--r-- | python/fatcat_client/api/default_api.py | 50 | ||||
-rw-r--r-- | rust/fatcat-api/README.md | 10 | ||||
-rw-r--r-- | rust/fatcat-api/api.yaml | 54 | ||||
-rw-r--r-- | rust/fatcat-api/api/swagger.yaml | 80 | ||||
-rw-r--r-- | rust/fatcat-api/examples/client.rs | 10 | ||||
-rw-r--r-- | rust/fatcat-api/examples/server_lib/server.rs | 76 | ||||
-rw-r--r-- | rust/fatcat-api/src/client.rs | 95 | ||||
-rw-r--r-- | rust/fatcat-api/src/lib.rs | 100 | ||||
-rw-r--r-- | rust/fatcat-api/src/server.rs | 35 | ||||
-rw-r--r-- | rust/src/api_server.rs | 31 | ||||
-rw-r--r-- | rust/src/api_wrappers.rs | 6 | ||||
-rw-r--r-- | rust/tests/test_api_server.rs | 41 |
20 files changed, 628 insertions, 75 deletions
@@ -2,14 +2,23 @@ ## Next Up - some significant slow-down has happened? transactions, or regexes? +summer roadmap: +- PUT/UPDATE, DELETE, and merge code paths +- faster UPDATE-free bulk import code path +- container import (extra?): lang, region, subject +- basic API+webface creation, editing, merging, editgroup approval +- elastic schema/transform for releases; bulk and continuous scripts features: - fast database dump command: both changelog-based and entity-based (rust) => lighter, more complete dumps for each entity type? +- guide skeleton (mdbook; guide.fatcat.wiki) importers: +- CORE +- wikidata cross-ref (if they have a dump) - manifest: multiple URLs per SHA1 -- pubmed (medline) +- pubmed (medline), if not in CORE => and/or, use pubmed ID lookups on crossref import - core - semantic scholar (up to 39 million; author de-dupe) @@ -36,10 +45,16 @@ july roadmap: ## Schema / Alignment / Scope - "container" -> "venue"? -- release_type, release_status, url.rel enums (and others?) +- release_type, release_status, url.rel write-time schema(and others?) name ref: https://www.w3.org/International/questions/qa-personal-names +## API + +- how to send edit "extra" metadata? +- hydrate entities in API + ? "expand" query param + ## High-Level Priorities - full database dump (export) diff --git a/fatcat-openapi2.yml b/fatcat-openapi2.yml index d9cf8813..a8919216 100644 --- a/fatcat-openapi2.yml +++ b/fatcat-openapi2.yml @@ -57,6 +57,10 @@ x-entity-props: &ENTITYPROPS extra: type: object additionalProperties: {} +# TODO: +# edit_extra: +# type: object +# additionalProperties: {} definitions: error_response: @@ -438,6 +442,16 @@ paths: post: operationId: "create_container_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -554,6 +568,16 @@ paths: post: operationId: "create_creator_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -686,6 +710,16 @@ paths: post: operationId: "create_file_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -802,6 +836,16 @@ paths: post: operationId: "create_release_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -934,6 +978,16 @@ paths: post: operationId: "create_work_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true diff --git a/notes/autoaccept_api.txt b/notes/autoaccept_api.txt new file mode 100644 index 00000000..b7e0a824 --- /dev/null +++ b/notes/autoaccept_api.txt @@ -0,0 +1,31 @@ + +Currently only on batch creation (POST) for entities. + +For all bulk operations, optional 'editgroup' query parameter overrides +individual editgroup parameters. + +If autoaccept flag is set and editgroup is not, a new editgroup is +automatically created and overrides for all entities inserted. Note +that this is different behavior from the "use current or create new" +default behavior for regular creation. + +Unfortunately, "true" and "false" are the only values acceptable for boolean +rust/openapi2 query parameters + +THOUGHT: doing an UPDATE in a transaction is probably not expensive + +Intent: +- check can_autoaccept flag on editor table + +--------- + +Crude benchmarking... + +cat /data/crossref/crossref-works.2018-01-21.badsample_5k.json | time ./fatcat_import.py import-crossref - /data/issn/20180216.ISSN-to-ISSN-L.txt + +autoaccept: 7.47user 0.48system 0:30.64elapsed 25%CPU +master: 5.70user 0.34system 0:25.61elapsed 23%CPU + batch creation: ~153ms+ + accept: ~5ms + +uh... diff --git a/notes/cloud_instances.txt b/notes/cloud_instances.txt index 4582c431..b7071758 100644 --- a/notes/cloud_instances.txt +++ b/notes/cloud_instances.txt @@ -6,3 +6,5 @@ digital ocean aws i3.2xlarge 61 GB RAM, 8 cores, 1900 GB NVMe, $455/month +OVH + MG-128 128 GB RAM, 16 cores, 2880 GB SSD (RAID), 500mbps unlimited b/w, $315/month diff --git a/python/fatcat/crossref_importer.py b/python/fatcat/crossref_importer.py index d3e525a4..54a3e84f 100644 --- a/python/fatcat/crossref_importer.py +++ b/python/fatcat/crossref_importer.py @@ -152,4 +152,4 @@ class FatcatCrossrefImporter(FatcatImporter): re.container_id = container.ident self._issnl_id_map[ce.issnl] = container.ident release_batch.append(re) - self.api.create_release_batch(release_batch) + self.api.create_release_batch(release_batch, autoaccept="true", editgroup=editgroup_id) diff --git a/python/fatcat/importer_common.py b/python/fatcat/importer_common.py index e084d8c4..0b02d175 100644 --- a/python/fatcat/importer_common.py +++ b/python/fatcat/importer_common.py @@ -45,8 +45,7 @@ class FatcatImporter: for rows in grouper(source, size): eg = self.api.create_editgroup( fatcat_client.Editgroup(editor_id='aaaaaaaaaaaabkvkaaaaaaaaae')) - self.create_batch(rows, eg.id) - self.api.accept_editgroup(eg.id) + self.create_batch(rows, editgroup_id=eg.id) def process_csv_source(self, source, group_size=100, delimiter=','): reader = csv.DictReader(source, delimiter=delimiter) diff --git a/python/fatcat/issn_importer.py b/python/fatcat/issn_importer.py index 181137ac..eb8a50ba 100644 --- a/python/fatcat/issn_importer.py +++ b/python/fatcat/issn_importer.py @@ -70,4 +70,4 @@ class FatcatIssnImporter(FatcatImporter): objects = [o for o in objects if o != None] for o in objects: o.editgroup_id = editgroup_id - self.api.create_container_batch(objects) + self.api.create_container_batch(objects, autoaccept="true", editgroup=editgroup_id) diff --git a/python/fatcat/orcid_importer.py b/python/fatcat/orcid_importer.py index e57703d5..fe76b02c 100644 --- a/python/fatcat/orcid_importer.py +++ b/python/fatcat/orcid_importer.py @@ -71,4 +71,4 @@ class FatcatOrcidImporter(FatcatImporter): objects = [o for o in objects if o != None] for o in objects: o.editgroup_id = editgroup_id - self.api.create_creator_batch(objects) + self.api.create_creator_batch(objects, autoaccept="true", editgroup=editgroup_id) diff --git a/python/fatcat_client/api/default_api.py b/python/fatcat_client/api/default_api.py index acd33c88..a0298750 100644 --- a/python/fatcat_client/api/default_api.py +++ b/python/fatcat_client/api/default_api.py @@ -245,6 +245,8 @@ class DefaultApi(object): :param async bool :param list[ContainerEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. @@ -266,12 +268,14 @@ class DefaultApi(object): :param async bool :param list[ContainerEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. """ - all_params = ['entity_list'] # noqa: E501 + all_params = ['entity_list', 'autoaccept', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -296,6 +300,10 @@ class DefaultApi(object): path_params = {} query_params = [] + if 'autoaccept' in params: + query_params.append(('autoaccept', params['autoaccept'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -439,6 +447,8 @@ class DefaultApi(object): :param async bool :param list[CreatorEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. @@ -460,12 +470,14 @@ class DefaultApi(object): :param async bool :param list[CreatorEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. """ - all_params = ['entity_list'] # noqa: E501 + all_params = ['entity_list', 'autoaccept', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -490,6 +502,10 @@ class DefaultApi(object): path_params = {} query_params = [] + if 'autoaccept' in params: + query_params.append(('autoaccept', params['autoaccept'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -730,6 +746,8 @@ class DefaultApi(object): :param async bool :param list[FileEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. @@ -751,12 +769,14 @@ class DefaultApi(object): :param async bool :param list[FileEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. """ - all_params = ['entity_list'] # noqa: E501 + all_params = ['entity_list', 'autoaccept', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -781,6 +801,10 @@ class DefaultApi(object): path_params = {} query_params = [] + if 'autoaccept' in params: + query_params.append(('autoaccept', params['autoaccept'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -924,6 +948,8 @@ class DefaultApi(object): :param async bool :param list[ReleaseEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. @@ -945,12 +971,14 @@ class DefaultApi(object): :param async bool :param list[ReleaseEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. """ - all_params = ['entity_list'] # noqa: E501 + all_params = ['entity_list', 'autoaccept', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -975,6 +1003,10 @@ class DefaultApi(object): path_params = {} query_params = [] + if 'autoaccept' in params: + query_params.append(('autoaccept', params['autoaccept'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1118,6 +1150,8 @@ class DefaultApi(object): :param async bool :param list[WorkEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. @@ -1139,12 +1173,14 @@ class DefaultApi(object): :param async bool :param list[WorkEntity] entity_list: (required) + :param bool autoaccept: If true, and editor is authorized, batch is accepted all at once + :param str editgroup: Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True) :return: list[EntityEdit] If the method is called asynchronously, returns the request thread. """ - all_params = ['entity_list'] # noqa: E501 + all_params = ['entity_list', 'autoaccept', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1169,6 +1205,10 @@ class DefaultApi(object): path_params = {} query_params = [] + if 'autoaccept' in params: + query_params.append(('autoaccept', params['autoaccept'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md index 8381b9f7..4be4939f 100644 --- a/rust/fatcat-api/README.md +++ b/rust/fatcat-api/README.md @@ -68,11 +68,6 @@ cargo run --example client CreateRelease cargo run --example client CreateReleaseBatch cargo run --example client CreateWork cargo run --example client CreateWorkBatch -cargo run --example client DeleteContainer -cargo run --example client DeleteCreator -cargo run --example client DeleteFile -cargo run --example client DeleteRelease -cargo run --example client DeleteWork cargo run --example client GetChangelog cargo run --example client GetChangelogEntry cargo run --example client GetContainer @@ -96,11 +91,6 @@ cargo run --example client LookupContainer cargo run --example client LookupCreator cargo run --example client LookupFile cargo run --example client LookupRelease -cargo run --example client UpdateContainer -cargo run --example client UpdateCreator -cargo run --example client UpdateFile -cargo run --example client UpdateRelease -cargo run --example client UpdateWork ``` ### HTTPS diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml index d9cf8813..a8919216 100644 --- a/rust/fatcat-api/api.yaml +++ b/rust/fatcat-api/api.yaml @@ -57,6 +57,10 @@ x-entity-props: &ENTITYPROPS extra: type: object additionalProperties: {} +# TODO: +# edit_extra: +# type: object +# additionalProperties: {} definitions: error_response: @@ -438,6 +442,16 @@ paths: post: operationId: "create_container_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -554,6 +568,16 @@ paths: post: operationId: "create_creator_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -686,6 +710,16 @@ paths: post: operationId: "create_file_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -802,6 +836,16 @@ paths: post: operationId: "create_release_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true @@ -934,6 +978,16 @@ paths: post: operationId: "create_work_batch" parameters: + - name: autoaccept + in: query + type: boolean + required: false + description: "If true, and editor is authorized, batch is accepted all at once" + - name: editgroup + in: query + type: string + required: false + description: "Editgroup to auto-accept and apply to all entities (required if 'autoaccept' is True)" - name: entity_list in: body required: true diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml index e8026c8d..0b1ca88a 100644 --- a/rust/fatcat-api/api/swagger.yaml +++ b/rust/fatcat-api/api/swagger.yaml @@ -76,6 +76,22 @@ paths: post: operationId: "create_container_batch" parameters: + - name: "autoaccept" + in: "query" + description: "If true, and editor is authorized, batch is accepted all at\ + \ once" + required: false + type: "boolean" + formatString: "{:?}" + example: "Some(true)" + - name: "editgroup" + in: "query" + description: "Editgroup to auto-accept and apply to all entities (required\ + \ if 'autoaccept' is True)" + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"editgroup_example\".to_string())" - in: "body" name: "entity_list" required: true @@ -492,6 +508,22 @@ paths: post: operationId: "create_creator_batch" parameters: + - name: "autoaccept" + in: "query" + description: "If true, and editor is authorized, batch is accepted all at\ + \ once" + required: false + type: "boolean" + formatString: "{:?}" + example: "Some(true)" + - name: "editgroup" + in: "query" + description: "Editgroup to auto-accept and apply to all entities (required\ + \ if 'autoaccept' is True)" + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"editgroup_example\".to_string())" - in: "body" name: "entity_list" required: true @@ -962,6 +994,22 @@ paths: post: operationId: "create_file_batch" parameters: + - name: "autoaccept" + in: "query" + description: "If true, and editor is authorized, batch is accepted all at\ + \ once" + required: false + type: "boolean" + formatString: "{:?}" + example: "Some(true)" + - name: "editgroup" + in: "query" + description: "Editgroup to auto-accept and apply to all entities (required\ + \ if 'autoaccept' is True)" + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"editgroup_example\".to_string())" - in: "body" name: "entity_list" required: true @@ -1375,6 +1423,22 @@ paths: post: operationId: "create_release_batch" parameters: + - name: "autoaccept" + in: "query" + description: "If true, and editor is authorized, batch is accepted all at\ + \ once" + required: false + type: "boolean" + formatString: "{:?}" + example: "Some(true)" + - name: "editgroup" + in: "query" + description: "Editgroup to auto-accept and apply to all entities (required\ + \ if 'autoaccept' is True)" + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"editgroup_example\".to_string())" - in: "body" name: "entity_list" required: true @@ -1842,6 +1906,22 @@ paths: post: operationId: "create_work_batch" parameters: + - name: "autoaccept" + in: "query" + description: "If true, and editor is authorized, batch is accepted all at\ + \ once" + required: false + type: "boolean" + formatString: "{:?}" + example: "Some(true)" + - name: "editgroup" + in: "query" + description: "Editgroup to auto-accept and apply to all entities (required\ + \ if 'autoaccept' is True)" + required: false + type: "string" + formatString: "{:?}" + example: "Some(\"editgroup_example\".to_string())" - in: "body" name: "entity_list" required: true diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs index 2ee65d7e..cc94af11 100644 --- a/rust/fatcat-api/examples/client.rs +++ b/rust/fatcat-api/examples/client.rs @@ -101,7 +101,7 @@ fn main() { // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, Some("CreateContainerBatch") => { - let result = client.create_container_batch(&Vec::new()).wait(); + let result = client.create_container_batch(&Vec::new(), Some(true), Some("editgroup_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } @@ -111,7 +111,7 @@ fn main() { // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, Some("CreateCreatorBatch") => { - let result = client.create_creator_batch(&Vec::new()).wait(); + let result = client.create_creator_batch(&Vec::new(), Some(true), Some("editgroup_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } @@ -127,7 +127,7 @@ fn main() { // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, Some("CreateFileBatch") => { - let result = client.create_file_batch(&Vec::new()).wait(); + let result = client.create_file_batch(&Vec::new(), Some(true), Some("editgroup_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } @@ -137,7 +137,7 @@ fn main() { // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, Some("CreateReleaseBatch") => { - let result = client.create_release_batch(&Vec::new()).wait(); + let result = client.create_release_batch(&Vec::new(), Some(true), Some("editgroup_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } @@ -147,7 +147,7 @@ fn main() { // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, Some("CreateWorkBatch") => { - let result = client.create_work_batch(&Vec::new()).wait(); + let result = client.create_work_batch(&Vec::new(), Some(true), Some("editgroup_example".to_string())).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } diff --git a/rust/fatcat-api/examples/server_lib/server.rs b/rust/fatcat-api/examples/server_lib/server.rs index af3ece12..ab08f594 100644 --- a/rust/fatcat-api/examples/server_lib/server.rs +++ b/rust/fatcat-api/examples/server_lib/server.rs @@ -35,11 +35,19 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn create_container_batch(&self, entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { + fn create_container_batch( + &self, + entity_list: &Vec<models::ContainerEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "create_container_batch({:?}) - X-Span-ID: {:?}", + "create_container_batch({:?}, {:?}, {:?}) - X-Span-ID: {:?}", entity_list, + autoaccept, + editgroup, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) @@ -51,9 +59,21 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn create_creator_batch(&self, entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { + fn create_creator_batch( + &self, + entity_list: &Vec<models::CreatorEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { let context = context.clone(); - println!("create_creator_batch({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + println!( + "create_creator_batch({:?}, {:?}, {:?}) - X-Span-ID: {:?}", + entity_list, + autoaccept, + editgroup, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); Box::new(futures::failed("Generic failure".into())) } @@ -69,9 +89,21 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { + fn create_file_batch( + &self, + entity_list: &Vec<models::FileEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { let context = context.clone(); - println!("create_file_batch({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + println!( + "create_file_batch({:?}, {:?}, {:?}) - X-Span-ID: {:?}", + entity_list, + autoaccept, + editgroup, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); Box::new(futures::failed("Generic failure".into())) } @@ -81,9 +113,21 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn create_release_batch(&self, entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { + fn create_release_batch( + &self, + entity_list: &Vec<models::ReleaseEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { let context = context.clone(); - println!("create_release_batch({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + println!( + "create_release_batch({:?}, {:?}, {:?}) - X-Span-ID: {:?}", + entity_list, + autoaccept, + editgroup, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); Box::new(futures::failed("Generic failure".into())) } @@ -93,9 +137,21 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { + fn create_work_batch( + &self, + entity_list: &Vec<models::WorkEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { let context = context.clone(); - println!("create_work_batch({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + println!( + "create_work_batch({:?}, {:?}, {:?}) - X-Span-ID: {:?}", + entity_list, + autoaccept, + editgroup, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); Box::new(futures::failed("Generic failure".into())) } diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs index 9b2caa32..6f61f773 100644 --- a/rust/fatcat-api/src/client.rs +++ b/rust/fatcat-api/src/client.rs @@ -298,8 +298,23 @@ impl Api for Client { Box::new(futures::done(result)) } - fn create_container_batch(&self, param_entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { - let url = format!("{}/v0/container/batch", self.base_path); + fn create_container_batch( + &self, + param_entity_list: &Vec<models::ContainerEntity>, + param_autoaccept: Option<bool>, + param_editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { + // Query parameters + let query_autoaccept = param_autoaccept.map_or_else(String::new, |query| format!("autoaccept={autoaccept}&", autoaccept = query.to_string())); + let query_editgroup = param_editgroup.map_or_else(String::new, |query| format!("editgroup={editgroup}&", editgroup = query.to_string())); + + let url = format!( + "{}/v0/container/batch?{autoaccept}{editgroup}", + self.base_path, + autoaccept = utf8_percent_encode(&query_autoaccept, QUERY_ENCODE_SET), + editgroup = utf8_percent_encode(&query_editgroup, QUERY_ENCODE_SET) + ); let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); @@ -428,8 +443,23 @@ impl Api for Client { Box::new(futures::done(result)) } - fn create_creator_batch(&self, param_entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { - let url = format!("{}/v0/creator/batch", self.base_path); + fn create_creator_batch( + &self, + param_entity_list: &Vec<models::CreatorEntity>, + param_autoaccept: Option<bool>, + param_editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { + // Query parameters + let query_autoaccept = param_autoaccept.map_or_else(String::new, |query| format!("autoaccept={autoaccept}&", autoaccept = query.to_string())); + let query_editgroup = param_editgroup.map_or_else(String::new, |query| format!("editgroup={editgroup}&", editgroup = query.to_string())); + + let url = format!( + "{}/v0/creator/batch?{autoaccept}{editgroup}", + self.base_path, + autoaccept = utf8_percent_encode(&query_autoaccept, QUERY_ENCODE_SET), + editgroup = utf8_percent_encode(&query_editgroup, QUERY_ENCODE_SET) + ); let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); @@ -616,8 +646,23 @@ impl Api for Client { Box::new(futures::done(result)) } - fn create_file_batch(&self, param_entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { - let url = format!("{}/v0/file/batch", self.base_path); + fn create_file_batch( + &self, + param_entity_list: &Vec<models::FileEntity>, + param_autoaccept: Option<bool>, + param_editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { + // Query parameters + let query_autoaccept = param_autoaccept.map_or_else(String::new, |query| format!("autoaccept={autoaccept}&", autoaccept = query.to_string())); + let query_editgroup = param_editgroup.map_or_else(String::new, |query| format!("editgroup={editgroup}&", editgroup = query.to_string())); + + let url = format!( + "{}/v0/file/batch?{autoaccept}{editgroup}", + self.base_path, + autoaccept = utf8_percent_encode(&query_autoaccept, QUERY_ENCODE_SET), + editgroup = utf8_percent_encode(&query_editgroup, QUERY_ENCODE_SET) + ); let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); @@ -746,8 +791,23 @@ impl Api for Client { Box::new(futures::done(result)) } - fn create_release_batch(&self, param_entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { - let url = format!("{}/v0/release/batch", self.base_path); + fn create_release_batch( + &self, + param_entity_list: &Vec<models::ReleaseEntity>, + param_autoaccept: Option<bool>, + param_editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { + // Query parameters + let query_autoaccept = param_autoaccept.map_or_else(String::new, |query| format!("autoaccept={autoaccept}&", autoaccept = query.to_string())); + let query_editgroup = param_editgroup.map_or_else(String::new, |query| format!("editgroup={editgroup}&", editgroup = query.to_string())); + + let url = format!( + "{}/v0/release/batch?{autoaccept}{editgroup}", + self.base_path, + autoaccept = utf8_percent_encode(&query_autoaccept, QUERY_ENCODE_SET), + editgroup = utf8_percent_encode(&query_editgroup, QUERY_ENCODE_SET) + ); let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); @@ -876,8 +936,23 @@ impl Api for Client { Box::new(futures::done(result)) } - fn create_work_batch(&self, param_entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { - let url = format!("{}/v0/work/batch", self.base_path); + fn create_work_batch( + &self, + param_entity_list: &Vec<models::WorkEntity>, + param_autoaccept: Option<bool>, + param_editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { + // Query parameters + let query_autoaccept = param_autoaccept.map_or_else(String::new, |query| format!("autoaccept={autoaccept}&", autoaccept = query.to_string())); + let query_editgroup = param_editgroup.map_or_else(String::new, |query| format!("editgroup={editgroup}&", editgroup = query.to_string())); + + let url = format!( + "{}/v0/work/batch?{autoaccept}{editgroup}", + self.base_path, + autoaccept = utf8_percent_encode(&query_autoaccept, QUERY_ENCODE_SET), + editgroup = utf8_percent_encode(&query_editgroup, QUERY_ENCODE_SET) + ); let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs index 8ff89b9f..fc1ae2a1 100644 --- a/rust/fatcat-api/src/lib.rs +++ b/rust/fatcat-api/src/lib.rs @@ -564,25 +564,55 @@ pub trait Api { fn create_container(&self, entity: models::ContainerEntity, context: &Context) -> Box<Future<Item = CreateContainerResponse, Error = ApiError> + Send>; - fn create_container_batch(&self, entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send>; + fn create_container_batch( + &self, + entity_list: &Vec<models::ContainerEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send>; fn create_creator(&self, entity: models::CreatorEntity, context: &Context) -> Box<Future<Item = CreateCreatorResponse, Error = ApiError> + Send>; - fn create_creator_batch(&self, entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send>; + fn create_creator_batch( + &self, + entity_list: &Vec<models::CreatorEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send>; fn create_editgroup(&self, entity: models::Editgroup, context: &Context) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send>; fn create_file(&self, entity: models::FileEntity, context: &Context) -> Box<Future<Item = CreateFileResponse, Error = ApiError> + Send>; - fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send>; + fn create_file_batch( + &self, + entity_list: &Vec<models::FileEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send>; fn create_release(&self, entity: models::ReleaseEntity, context: &Context) -> Box<Future<Item = CreateReleaseResponse, Error = ApiError> + Send>; - fn create_release_batch(&self, entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send>; + fn create_release_batch( + &self, + entity_list: &Vec<models::ReleaseEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send>; fn create_work(&self, entity: models::WorkEntity, context: &Context) -> Box<Future<Item = CreateWorkResponse, Error = ApiError> + Send>; - fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>; + fn create_work_batch( + &self, + entity_list: &Vec<models::WorkEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + context: &Context, + ) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>; fn delete_container(&self, id: String, editgroup: Option<String>, context: &Context) -> Box<Future<Item = DeleteContainerResponse, Error = ApiError> + Send>; @@ -657,25 +687,40 @@ pub trait ApiNoContext { fn create_container(&self, entity: models::ContainerEntity) -> Box<Future<Item = CreateContainerResponse, Error = ApiError> + Send>; - fn create_container_batch(&self, entity_list: &Vec<models::ContainerEntity>) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send>; + fn create_container_batch( + &self, + entity_list: &Vec<models::ContainerEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send>; fn create_creator(&self, entity: models::CreatorEntity) -> Box<Future<Item = CreateCreatorResponse, Error = ApiError> + Send>; - fn create_creator_batch(&self, entity_list: &Vec<models::CreatorEntity>) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send>; + fn create_creator_batch( + &self, + entity_list: &Vec<models::CreatorEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send>; fn create_editgroup(&self, entity: models::Editgroup) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send>; fn create_file(&self, entity: models::FileEntity) -> Box<Future<Item = CreateFileResponse, Error = ApiError> + Send>; - fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send>; + fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>, autoaccept: Option<bool>, editgroup: Option<String>) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send>; fn create_release(&self, entity: models::ReleaseEntity) -> Box<Future<Item = CreateReleaseResponse, Error = ApiError> + Send>; - fn create_release_batch(&self, entity_list: &Vec<models::ReleaseEntity>) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send>; + fn create_release_batch( + &self, + entity_list: &Vec<models::ReleaseEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send>; fn create_work(&self, entity: models::WorkEntity) -> Box<Future<Item = CreateWorkResponse, Error = ApiError> + Send>; - fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>; + fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>, autoaccept: Option<bool>, editgroup: Option<String>) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>; fn delete_container(&self, id: String, editgroup: Option<String>) -> Box<Future<Item = DeleteContainerResponse, Error = ApiError> + Send>; @@ -768,16 +813,26 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().create_container(entity, &self.context()) } - fn create_container_batch(&self, entity_list: &Vec<models::ContainerEntity>) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { - self.api().create_container_batch(entity_list, &self.context()) + fn create_container_batch( + &self, + entity_list: &Vec<models::ContainerEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateContainerBatchResponse, Error = ApiError> + Send> { + self.api().create_container_batch(entity_list, autoaccept, editgroup, &self.context()) } fn create_creator(&self, entity: models::CreatorEntity) -> Box<Future<Item = CreateCreatorResponse, Error = ApiError> + Send> { self.api().create_creator(entity, &self.context()) } - fn create_creator_batch(&self, entity_list: &Vec<models::CreatorEntity>) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { - self.api().create_creator_batch(entity_list, &self.context()) + fn create_creator_batch( + &self, + entity_list: &Vec<models::CreatorEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateCreatorBatchResponse, Error = ApiError> + Send> { + self.api().create_creator_batch(entity_list, autoaccept, editgroup, &self.context()) } fn create_editgroup(&self, entity: models::Editgroup) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send> { @@ -788,24 +843,29 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().create_file(entity, &self.context()) } - fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { - self.api().create_file_batch(entity_list, &self.context()) + fn create_file_batch(&self, entity_list: &Vec<models::FileEntity>, autoaccept: Option<bool>, editgroup: Option<String>) -> Box<Future<Item = CreateFileBatchResponse, Error = ApiError> + Send> { + self.api().create_file_batch(entity_list, autoaccept, editgroup, &self.context()) } fn create_release(&self, entity: models::ReleaseEntity) -> Box<Future<Item = CreateReleaseResponse, Error = ApiError> + Send> { self.api().create_release(entity, &self.context()) } - fn create_release_batch(&self, entity_list: &Vec<models::ReleaseEntity>) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { - self.api().create_release_batch(entity_list, &self.context()) + fn create_release_batch( + &self, + entity_list: &Vec<models::ReleaseEntity>, + autoaccept: Option<bool>, + editgroup: Option<String>, + ) -> Box<Future<Item = CreateReleaseBatchResponse, Error = ApiError> + Send> { + self.api().create_release_batch(entity_list, autoaccept, editgroup, &self.context()) } fn create_work(&self, entity: models::WorkEntity) -> Box<Future<Item = CreateWorkResponse, Error = ApiError> + Send> { self.api().create_work(entity, &self.context()) } - fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { - self.api().create_work_batch(entity_list, &self.context()) + fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>, autoaccept: Option<bool>, editgroup: Option<String>) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send> { + self.api().create_work_batch(entity_list, autoaccept, editgroup, &self.context()) } fn delete_container(&self, id: String, editgroup: Option<String>) -> Box<Future<Item = DeleteContainerResponse, Error = ApiError> + Send> { diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs index 8db27496..04d10e14 100644 --- a/rust/fatcat-api/src/server.rs +++ b/rust/fatcat-api/src/server.rs @@ -309,6 +309,11 @@ where context.auth_data = req.extensions.remove::<AuthData>(); context.authorization = req.extensions.remove::<Authorization>(); + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::<UrlEncodedQuery>().unwrap_or_default(); + let param_autoaccept = query_params.get("autoaccept").and_then(|list| list.first()).and_then(|x| x.parse::<bool>().ok()); + let param_editgroup = query_params.get("editgroup").and_then(|list| list.first()).and_then(|x| x.parse::<String>().ok()); + // Body parameters (note that non-required body parameters will ignore garbage // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. @@ -333,7 +338,7 @@ where }; let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; - match api.create_container_batch(param_entity_list.as_ref(), context).wait() { + match api.create_container_batch(param_entity_list.as_ref(), param_autoaccept, param_editgroup, context).wait() { Ok(rsp) => match rsp { CreateContainerBatchResponse::CreatedEntities(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -521,6 +526,11 @@ where context.auth_data = req.extensions.remove::<AuthData>(); context.authorization = req.extensions.remove::<Authorization>(); + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::<UrlEncodedQuery>().unwrap_or_default(); + let param_autoaccept = query_params.get("autoaccept").and_then(|list| list.first()).and_then(|x| x.parse::<bool>().ok()); + let param_editgroup = query_params.get("editgroup").and_then(|list| list.first()).and_then(|x| x.parse::<String>().ok()); + // Body parameters (note that non-required body parameters will ignore garbage // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. @@ -545,7 +555,7 @@ where }; let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; - match api.create_creator_batch(param_entity_list.as_ref(), context).wait() { + match api.create_creator_batch(param_entity_list.as_ref(), param_autoaccept, param_editgroup, context).wait() { Ok(rsp) => match rsp { CreateCreatorBatchResponse::CreatedEntities(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -826,6 +836,11 @@ where context.auth_data = req.extensions.remove::<AuthData>(); context.authorization = req.extensions.remove::<Authorization>(); + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::<UrlEncodedQuery>().unwrap_or_default(); + let param_autoaccept = query_params.get("autoaccept").and_then(|list| list.first()).and_then(|x| x.parse::<bool>().ok()); + let param_editgroup = query_params.get("editgroup").and_then(|list| list.first()).and_then(|x| x.parse::<String>().ok()); + // Body parameters (note that non-required body parameters will ignore garbage // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. @@ -850,7 +865,7 @@ where }; let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; - match api.create_file_batch(param_entity_list.as_ref(), context).wait() { + match api.create_file_batch(param_entity_list.as_ref(), param_autoaccept, param_editgroup, context).wait() { Ok(rsp) => match rsp { CreateFileBatchResponse::CreatedEntities(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -1038,6 +1053,11 @@ where context.auth_data = req.extensions.remove::<AuthData>(); context.authorization = req.extensions.remove::<Authorization>(); + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::<UrlEncodedQuery>().unwrap_or_default(); + let param_autoaccept = query_params.get("autoaccept").and_then(|list| list.first()).and_then(|x| x.parse::<bool>().ok()); + let param_editgroup = query_params.get("editgroup").and_then(|list| list.first()).and_then(|x| x.parse::<String>().ok()); + // Body parameters (note that non-required body parameters will ignore garbage // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. @@ -1062,7 +1082,7 @@ where }; let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; - match api.create_release_batch(param_entity_list.as_ref(), context).wait() { + match api.create_release_batch(param_entity_list.as_ref(), param_autoaccept, param_editgroup, context).wait() { Ok(rsp) => match rsp { CreateReleaseBatchResponse::CreatedEntities(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); @@ -1250,6 +1270,11 @@ where context.auth_data = req.extensions.remove::<AuthData>(); context.authorization = req.extensions.remove::<Authorization>(); + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::<UrlEncodedQuery>().unwrap_or_default(); + let param_autoaccept = query_params.get("autoaccept").and_then(|list| list.first()).and_then(|x| x.parse::<bool>().ok()); + let param_editgroup = query_params.get("editgroup").and_then(|list| list.first()).and_then(|x| x.parse::<String>().ok()); + // Body parameters (note that non-required body parameters will ignore garbage // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. @@ -1274,7 +1299,7 @@ where }; let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; - match api.create_work_batch(param_entity_list.as_ref(), context).wait() { + match api.create_work_batch(param_entity_list.as_ref(), param_autoaccept, param_editgroup, context).wait() { Ok(rsp) => match rsp { CreateWorkBatchResponse::CreatedEntities(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 57aeebea..602fbfb7 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -25,11 +25,40 @@ macro_rules! entity_batch_handler { pub fn $post_batch_handler( &self, entity_list: &[models::$model], + autoaccept: bool, + editgroup: Option<String>, conn: &DbConn, ) -> Result<Vec<EntityEdit>> { - let edit_context = make_edit_context(conn, None)?; + // editgroup override logic based on parameters + let eg_id: Option<Uuid> = match (editgroup, autoaccept) { + (Some(eg_string), _) => Some(fcid2uuid(&eg_string)?), + (None, true) => { + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values((editgroup::editor_id.eq(editor_id),)) + .get_result(conn)?; + Some(eg_row.id) + }, + (None, false) => None + }; + for entity in entity_list { + let mut e = entity.clone(); + // override individual editgroup IDs (if set earlier) + if let Some(inner_id) = eg_id { + e.editgroup_id = Some(uuid2fcid(&inner_id)); + } + // actual wrapped function call here + ret.push(self.$post_handler(e, autoaccept, conn)?); + } + let mut edit_context = make_edit_context(conn, eg_id)?; + edit_context.autoaccept = autoaccept; let model_list: Vec<&models::$model> = entity_list.iter().map(|e| e).collect(); let edits = $model::db_create_batch(conn, &edit_context, model_list.as_slice())?; + if autoaccept { + // if autoaccept, eg_id is always Some + let _clr: ChangelogRow = diesel::insert_into(changelog::table) + .values((changelog::editgroup_id.eq(eg_id.unwrap()),)) + .get_result(conn)?; + } edits.into_iter().map(|e| e.into_model()).collect() } } diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index de1fc9da..6272814e 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -61,7 +61,7 @@ macro_rules! wrap_entity_handlers { _context: &Context, ) -> Box<Future<Item = $post_resp, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_handler(entity, &conn)) { + let ret = match conn.transaction(|| self.$post_handler(entity, false, &conn)) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => @@ -84,10 +84,12 @@ macro_rules! wrap_entity_handlers { fn $post_batch_fn( &self, entity_list: &Vec<models::$model>, + autoaccept: Option<bool>, + editgroup: Option<String>, _context: &Context, ) -> Box<Future<Item = $post_batch_resp, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, &conn)) { + let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, autoaccept.unwrap_or(false), editgroup, &conn)) { Ok(edit) => $post_batch_resp::CreatedEntities(edit), Err(Error(ErrorKind::Diesel(e), _)) => diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index 4b181188..54639228 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -983,3 +983,44 @@ fn test_contribs() { None, ); } + +#[test] +fn test_post_batch_autoaccept() { + let (headers, router, _conn) = setup(); + + // "true" + check_response( + request::post( + "http://localhost:9411/v0/container/batch?autoaccept=true", + headers.clone(), + r#"[{"name": "test journal"}, {"name": "another test journal"}]"#, + &router, + ), + status::Created, + None, + ); + + // "n" + check_response( + request::post( + "http://localhost:9411/v0/container/batch?autoaccept=n", + headers.clone(), + r#"[{"name": "test journal"}, {"name": "another test journal"}]"#, + &router, + ), + status::Created, + None, + ); + + // editgroup + check_response( + request::post( + "http://localhost:9411/v0/container/batch?autoaccept=yes&editgroup=asdf", + headers.clone(), + r#"[{"name": "test journal"}, {"name": "another test journal"}]"#, + &router, + ), + status::BadRequest, + None, + ); +} |