diff options
Diffstat (limited to 'rust')
-rw-r--r-- | rust/fatcat-api/README.md | 7 | ||||
-rw-r--r-- | rust/fatcat-api/api.yaml | 162 | ||||
-rw-r--r-- | rust/fatcat-api/api/swagger.yaml | 487 | ||||
-rw-r--r-- | rust/fatcat-api/examples/client.rs | 37 | ||||
-rw-r--r-- | rust/fatcat-api/examples/server_lib/server.rs | 37 | ||||
-rw-r--r-- | rust/fatcat-api/src/client.rs | 332 | ||||
-rw-r--r-- | rust/fatcat-api/src/lib.rs | 100 | ||||
-rw-r--r-- | rust/fatcat-api/src/mimetypes.rs | 100 | ||||
-rw-r--r-- | rust/fatcat-api/src/models.rs | 72 | ||||
-rw-r--r-- | rust/fatcat-api/src/server.rs | 537 | ||||
-rw-r--r-- | rust/fatcat-openapi2.yml | 162 | ||||
-rw-r--r-- | rust/src/api_server.rs | 199 | ||||
-rw-r--r-- | rust/src/lib.rs | 1 | ||||
-rw-r--r-- | rust/tests/test_api_server.rs | 16 |
14 files changed, 1964 insertions, 285 deletions
diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md index 99162994..32e6390b 100644 --- a/rust/fatcat-api/README.md +++ b/rust/fatcat-api/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) - API version: 0.1.0 -- Build date: 2018-05-27T21:43:16.246Z +- Build date: 2018-05-27T21:52:37.437Z This autogenerated project defines an API crate `fatcat` which contains: * An `Api` trait defining the API in Rust. @@ -56,9 +56,11 @@ cargo run --example server To run a client, follow one of the following simple steps: ``` +cargo run --example client ContainerBatchPost cargo run --example client ContainerIdGet cargo run --example client ContainerLookupGet cargo run --example client ContainerPost +cargo run --example client CreatorBatchPost cargo run --example client CreatorIdGet cargo run --example client CreatorLookupGet cargo run --example client CreatorPost @@ -67,12 +69,15 @@ cargo run --example client EditgroupIdGet cargo run --example client EditgroupPost cargo run --example client EditorUsernameChangelogGet cargo run --example client EditorUsernameGet +cargo run --example client FileBatchPost cargo run --example client FileIdGet cargo run --example client FileLookupGet cargo run --example client FilePost +cargo run --example client ReleaseBatchPost cargo run --example client ReleaseIdGet cargo run --example client ReleaseLookupGet cargo run --example client ReleasePost +cargo run --example client WorkBatchPost cargo run --example client WorkIdGet cargo run --example client WorkPost ``` diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml index f1b94234..8fcbf488 100644 --- a/rust/fatcat-api/api.yaml +++ b/rust/fatcat-api/api.yaml @@ -57,19 +57,6 @@ definitions: message: type: string example: "The computers did the thing successfully!" - creator_entity: - type: object - required: - - full_name - properties: - <<: *ENTITYPROPS - full_name: - type: string - example: "Grace Hopper" - orcid: - type: string - #format: custom - example: "0000-0002-1825-0097" container_entity: type: object required: @@ -90,6 +77,19 @@ definitions: type: string coden: type: string + creator_entity: + type: object + required: + - full_name + properties: + <<: *ENTITYPROPS + full_name: + type: string + example: "Grace Hopper" + orcid: + type: string + #format: custom + example: "0000-0002-1825-0097" file_entity: type: object properties: @@ -300,39 +300,39 @@ x-entity-responses: &ENTITYRESPONSES $ref: "#/definitions/error_response" paths: - /creator: + /container: post: parameters: - name: entity in: body required: true schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" responses: 201: description: Created Entity schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES -# /creator/batch: -# post: -# parameters: -# - name: entity_list -# in: body -# required: true -# schema: -# type: array -# items: -# $ref: "#/definitions/creator_entity" -# responses: -# 201: -# description: Created Entities -# schema: -# type: array -# items: -# $ref: "#/definitions/entity_edit" -# <<: *ENTITYRESPONSES - /creator/{id}: + /container/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/container_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /container/{id}: parameters: - name: id in: path @@ -343,12 +343,12 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" <<: *ENTITYRESPONSES - /creator/lookup: + /container/lookup: get: parameters: - - name: orcid + - name: issnl in: query type: string required: true @@ -356,23 +356,41 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" <<: *ENTITYRESPONSES - /container: + /creator: post: parameters: - name: entity in: body required: true schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" responses: 201: description: Created Entity schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES - /container/{id}: + /creator/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/creator_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /creator/{id}: parameters: - name: id in: path @@ -383,12 +401,12 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" <<: *ENTITYRESPONSES - /container/lookup: + /creator/lookup: get: parameters: - - name: issnl + - name: orcid in: query type: string required: true @@ -396,7 +414,7 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" <<: *ENTITYRESPONSES /file: post: @@ -412,6 +430,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /file/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/file_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /file/{id}: parameters: - name: id @@ -452,6 +488,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /release/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/release_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /release/{id}: parameters: - name: id @@ -492,6 +546,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /work/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/work_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /work/{id}: parameters: - name: id diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml index d9c03d42..8f491528 100644 --- a/rust/fatcat-api/api/swagger.yaml +++ b/rust/fatcat-api/api/swagger.yaml @@ -13,20 +13,20 @@ consumes: produces: - "application/json" paths: - /creator: + /container: post: parameters: - in: "body" name: "entity" required: true schema: - $ref: "#/definitions/creator_entity" - uppercase_data_type: "CREATORENTITY" - refName: "creator_entity" + $ref: "#/definitions/container_entity" + uppercase_data_type: "CONTAINERENTITY" + refName: "container_entity" formatString: "{:?}" example: "???" model_key: "changelogentries_inner" - uppercase_operation_id: "CREATOR_POST" + uppercase_operation_id: "CONTAINER_POST" consumesJson: true responses: 201: @@ -35,7 +35,7 @@ paths: $ref: "#/definitions/entity_edit" x-responseId: "CreatedEntity" x-uppercaseResponseId: "CREATED_ENTITY" - uppercase_operation_id: "CREATOR_POST" + uppercase_operation_id: "CONTAINER_POST" uppercase_data_type: "ENTITYEDIT" producesJson: true 400: @@ -44,7 +44,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CREATOR_POST" + uppercase_operation_id: "CONTAINER_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -53,7 +53,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CREATOR_POST" + uppercase_operation_id: "CONTAINER_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -62,16 +62,75 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CREATOR_POST" + uppercase_operation_id: "CONTAINER_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "creator_post" - uppercase_operation_id: "CREATOR_POST" - path: "/creator" + operation_id: "container_post" + uppercase_operation_id: "CONTAINER_POST" + path: "/container" HttpMethod: "Post" httpmethod: "post" noClientExample: true - /creator/{id}: + /container/batch: + post: + parameters: + - in: "body" + name: "entity_list" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/container_entity" + formatString: "{:?}" + example: "&Vec::new()" + model_key: "changelogentries_inner" + uppercase_operation_id: "CONTAINER_BATCH_POST" + consumesJson: true + responses: + 201: + description: "Created Entities" + schema: + type: "array" + items: + $ref: "#/definitions/entity_edit" + x-responseId: "CreatedEntities" + x-uppercaseResponseId: "CREATED_ENTITIES" + uppercase_operation_id: "CONTAINER_BATCH_POST" + uppercase_data_type: "VEC<ENTITYEDIT>" + producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CONTAINER_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + 404: + description: "Not Found" + schema: + $ref: "#/definitions/error_response" + x-responseId: "NotFound" + x-uppercaseResponseId: "NOT_FOUND" + uppercase_operation_id: "CONTAINER_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + default: + description: "Generic Error" + schema: + $ref: "#/definitions/error_response" + x-responseId: "GenericError" + x-uppercaseResponseId: "GENERIC_ERROR" + uppercase_operation_id: "CONTAINER_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + operation_id: "container_batch_post" + uppercase_operation_id: "CONTAINER_BATCH_POST" + path: "/container/batch" + HttpMethod: "Post" + httpmethod: "post" + /container/{id}: get: parameters: - name: "id" @@ -84,11 +143,11 @@ paths: 200: description: "Found Entity" schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" x-responseId: "FoundEntity" x-uppercaseResponseId: "FOUND_ENTITY" - uppercase_operation_id: "CREATOR_ID_GET" - uppercase_data_type: "CREATORENTITY" + uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_data_type: "CONTAINERENTITY" producesJson: true 400: description: "Bad Request" @@ -96,7 +155,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CREATOR_ID_GET" + uppercase_operation_id: "CONTAINER_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -105,7 +164,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CREATOR_ID_GET" + uppercase_operation_id: "CONTAINER_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -114,32 +173,32 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CREATOR_ID_GET" + uppercase_operation_id: "CONTAINER_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "creator_id_get" - uppercase_operation_id: "CREATOR_ID_GET" - path: "/creator/:id" + operation_id: "container_id_get" + uppercase_operation_id: "CONTAINER_ID_GET" + path: "/container/:id" HttpMethod: "Get" httpmethod: "get" - /creator/lookup: + /container/lookup: get: parameters: - - name: "orcid" + - name: "issnl" in: "query" required: true type: "string" formatString: "\\\"{}\\\"" - example: "\"orcid_example\".to_string()" + example: "\"issnl_example\".to_string()" responses: 200: description: "Found Entity" schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" x-responseId: "FoundEntity" x-uppercaseResponseId: "FOUND_ENTITY" - uppercase_operation_id: "CREATOR_LOOKUP_GET" - uppercase_data_type: "CREATORENTITY" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_data_type: "CONTAINERENTITY" producesJson: true 400: description: "Bad Request" @@ -147,7 +206,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -156,7 +215,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -165,28 +224,28 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "creator_lookup_get" - uppercase_operation_id: "CREATOR_LOOKUP_GET" - path: "/creator/lookup" + operation_id: "container_lookup_get" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + path: "/container/lookup" HttpMethod: "Get" httpmethod: "get" - /container: + /creator: post: parameters: - in: "body" name: "entity" required: true schema: - $ref: "#/definitions/container_entity" - uppercase_data_type: "CONTAINERENTITY" - refName: "container_entity" + $ref: "#/definitions/creator_entity" + uppercase_data_type: "CREATORENTITY" + refName: "creator_entity" formatString: "{:?}" example: "???" model_key: "changelogentries_inner" - uppercase_operation_id: "CONTAINER_POST" + uppercase_operation_id: "CREATOR_POST" consumesJson: true responses: 201: @@ -195,7 +254,7 @@ paths: $ref: "#/definitions/entity_edit" x-responseId: "CreatedEntity" x-uppercaseResponseId: "CREATED_ENTITY" - uppercase_operation_id: "CONTAINER_POST" + uppercase_operation_id: "CREATOR_POST" uppercase_data_type: "ENTITYEDIT" producesJson: true 400: @@ -204,7 +263,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CONTAINER_POST" + uppercase_operation_id: "CREATOR_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -213,7 +272,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CONTAINER_POST" + uppercase_operation_id: "CREATOR_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -222,16 +281,75 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CONTAINER_POST" + uppercase_operation_id: "CREATOR_POST" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "container_post" - uppercase_operation_id: "CONTAINER_POST" - path: "/container" + operation_id: "creator_post" + uppercase_operation_id: "CREATOR_POST" + path: "/creator" HttpMethod: "Post" httpmethod: "post" noClientExample: true - /container/{id}: + /creator/batch: + post: + parameters: + - in: "body" + name: "entity_list" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/creator_entity" + formatString: "{:?}" + example: "&Vec::new()" + model_key: "changelogentries_inner" + uppercase_operation_id: "CREATOR_BATCH_POST" + consumesJson: true + responses: + 201: + description: "Created Entities" + schema: + type: "array" + items: + $ref: "#/definitions/entity_edit" + x-responseId: "CreatedEntities" + x-uppercaseResponseId: "CREATED_ENTITIES" + uppercase_operation_id: "CREATOR_BATCH_POST" + uppercase_data_type: "VEC<ENTITYEDIT>" + producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CREATOR_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + 404: + description: "Not Found" + schema: + $ref: "#/definitions/error_response" + x-responseId: "NotFound" + x-uppercaseResponseId: "NOT_FOUND" + uppercase_operation_id: "CREATOR_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + default: + description: "Generic Error" + schema: + $ref: "#/definitions/error_response" + x-responseId: "GenericError" + x-uppercaseResponseId: "GENERIC_ERROR" + uppercase_operation_id: "CREATOR_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + operation_id: "creator_batch_post" + uppercase_operation_id: "CREATOR_BATCH_POST" + path: "/creator/batch" + HttpMethod: "Post" + httpmethod: "post" + /creator/{id}: get: parameters: - name: "id" @@ -244,11 +362,11 @@ paths: 200: description: "Found Entity" schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" x-responseId: "FoundEntity" x-uppercaseResponseId: "FOUND_ENTITY" - uppercase_operation_id: "CONTAINER_ID_GET" - uppercase_data_type: "CONTAINERENTITY" + uppercase_operation_id: "CREATOR_ID_GET" + uppercase_data_type: "CREATORENTITY" producesJson: true 400: description: "Bad Request" @@ -256,7 +374,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_operation_id: "CREATOR_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -265,7 +383,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_operation_id: "CREATOR_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -274,32 +392,32 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_operation_id: "CREATOR_ID_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "container_id_get" - uppercase_operation_id: "CONTAINER_ID_GET" - path: "/container/:id" + operation_id: "creator_id_get" + uppercase_operation_id: "CREATOR_ID_GET" + path: "/creator/:id" HttpMethod: "Get" httpmethod: "get" - /container/lookup: + /creator/lookup: get: parameters: - - name: "issnl" + - name: "orcid" in: "query" required: true type: "string" formatString: "\\\"{}\\\"" - example: "\"issnl_example\".to_string()" + example: "\"orcid_example\".to_string()" responses: 200: description: "Found Entity" schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" x-responseId: "FoundEntity" x-uppercaseResponseId: "FOUND_ENTITY" - uppercase_operation_id: "CONTAINER_LOOKUP_GET" - uppercase_data_type: "CONTAINERENTITY" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_data_type: "CREATORENTITY" producesJson: true 400: description: "Bad Request" @@ -307,7 +425,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "BadRequest" x-uppercaseResponseId: "BAD_REQUEST" - uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_operation_id: "CREATOR_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true 404: @@ -316,7 +434,7 @@ paths: $ref: "#/definitions/error_response" x-responseId: "NotFound" x-uppercaseResponseId: "NOT_FOUND" - uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_operation_id: "CREATOR_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true default: @@ -325,12 +443,12 @@ paths: $ref: "#/definitions/error_response" x-responseId: "GenericError" x-uppercaseResponseId: "GENERIC_ERROR" - uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_operation_id: "CREATOR_LOOKUP_GET" uppercase_data_type: "ERRORRESPONSE" producesJson: true - operation_id: "container_lookup_get" - uppercase_operation_id: "CONTAINER_LOOKUP_GET" - path: "/container/lookup" + operation_id: "creator_lookup_get" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + path: "/creator/lookup" HttpMethod: "Get" httpmethod: "get" /file: @@ -391,6 +509,65 @@ paths: HttpMethod: "Post" httpmethod: "post" noClientExample: true + /file/batch: + post: + parameters: + - in: "body" + name: "entity_list" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/file_entity" + formatString: "{:?}" + example: "&Vec::new()" + model_key: "changelogentries_inner" + uppercase_operation_id: "FILE_BATCH_POST" + consumesJson: true + responses: + 201: + description: "Created Entities" + schema: + type: "array" + items: + $ref: "#/definitions/entity_edit" + x-responseId: "CreatedEntities" + x-uppercaseResponseId: "CREATED_ENTITIES" + uppercase_operation_id: "FILE_BATCH_POST" + uppercase_data_type: "VEC<ENTITYEDIT>" + producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "FILE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + 404: + description: "Not Found" + schema: + $ref: "#/definitions/error_response" + x-responseId: "NotFound" + x-uppercaseResponseId: "NOT_FOUND" + uppercase_operation_id: "FILE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + default: + description: "Generic Error" + schema: + $ref: "#/definitions/error_response" + x-responseId: "GenericError" + x-uppercaseResponseId: "GENERIC_ERROR" + uppercase_operation_id: "FILE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + operation_id: "file_batch_post" + uppercase_operation_id: "FILE_BATCH_POST" + path: "/file/batch" + HttpMethod: "Post" + httpmethod: "post" /file/{id}: get: parameters: @@ -551,6 +728,65 @@ paths: HttpMethod: "Post" httpmethod: "post" noClientExample: true + /release/batch: + post: + parameters: + - in: "body" + name: "entity_list" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/release_entity" + formatString: "{:?}" + example: "&Vec::new()" + model_key: "changelogentries_inner" + uppercase_operation_id: "RELEASE_BATCH_POST" + consumesJson: true + responses: + 201: + description: "Created Entities" + schema: + type: "array" + items: + $ref: "#/definitions/entity_edit" + x-responseId: "CreatedEntities" + x-uppercaseResponseId: "CREATED_ENTITIES" + uppercase_operation_id: "RELEASE_BATCH_POST" + uppercase_data_type: "VEC<ENTITYEDIT>" + producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "RELEASE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + 404: + description: "Not Found" + schema: + $ref: "#/definitions/error_response" + x-responseId: "NotFound" + x-uppercaseResponseId: "NOT_FOUND" + uppercase_operation_id: "RELEASE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + default: + description: "Generic Error" + schema: + $ref: "#/definitions/error_response" + x-responseId: "GenericError" + x-uppercaseResponseId: "GENERIC_ERROR" + uppercase_operation_id: "RELEASE_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + operation_id: "release_batch_post" + uppercase_operation_id: "RELEASE_BATCH_POST" + path: "/release/batch" + HttpMethod: "Post" + httpmethod: "post" /release/{id}: get: parameters: @@ -711,6 +947,65 @@ paths: HttpMethod: "Post" httpmethod: "post" noClientExample: true + /work/batch: + post: + parameters: + - in: "body" + name: "entity_list" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/work_entity" + formatString: "{:?}" + example: "&Vec::new()" + model_key: "changelogentries_inner" + uppercase_operation_id: "WORK_BATCH_POST" + consumesJson: true + responses: + 201: + description: "Created Entities" + schema: + type: "array" + items: + $ref: "#/definitions/entity_edit" + x-responseId: "CreatedEntities" + x-uppercaseResponseId: "CREATED_ENTITIES" + uppercase_operation_id: "WORK_BATCH_POST" + uppercase_data_type: "VEC<ENTITYEDIT>" + producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "WORK_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + 404: + description: "Not Found" + schema: + $ref: "#/definitions/error_response" + x-responseId: "NotFound" + x-uppercaseResponseId: "NOT_FOUND" + uppercase_operation_id: "WORK_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + default: + description: "Generic Error" + schema: + $ref: "#/definitions/error_response" + x-responseId: "GenericError" + x-uppercaseResponseId: "GENERIC_ERROR" + uppercase_operation_id: "WORK_BATCH_POST" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true + operation_id: "work_batch_post" + uppercase_operation_id: "WORK_BATCH_POST" + path: "/work/batch" + HttpMethod: "Post" + httpmethod: "post" /work/{id}: get: parameters: @@ -1020,17 +1315,24 @@ definitions: example: message: "The computers did the thing successfully!" upperCaseName: "SUCCESS" - creator_entity: + container_entity: type: "object" required: - - "full_name" + - "name" properties: - orcid: + coden: type: "string" - example: "0000-0002-1825-0097" - full_name: + abbrev: type: "string" - example: "Grace Hopper" + issnl: + type: "string" + example: "1234-5678" + publisher: + type: "string" + example: "Society of Curious Students" + name: + type: "string" + example: "Journal of Important Results" extra: type: "object" editgroup_id: @@ -1056,32 +1358,28 @@ definitions: - "deleted" example: redirect: "f1f046a3-45c9-4b99-adce-000000000002" - full_name: "Grace Hopper" + coden: "coden" ident: "f1f046a3-45c9-4b99-adce-000000000001" extra: "{}" + name: "Journal of Important Results" + publisher: "Society of Curious Students" editgroup_id: 16 - orcid: "0000-0002-1825-0097" + issnl: "1234-5678" + abbrev: "abbrev" state: "wip" revision: 42 - upperCaseName: "CREATOR_ENTITY" - container_entity: + upperCaseName: "CONTAINER_ENTITY" + creator_entity: type: "object" required: - - "name" + - "full_name" properties: - coden: - type: "string" - abbrev: - type: "string" - issnl: - type: "string" - example: "1234-5678" - publisher: + orcid: type: "string" - example: "Society of Curious Students" - name: + example: "0000-0002-1825-0097" + full_name: type: "string" - example: "Journal of Important Results" + example: "Grace Hopper" state: type: "string" enum: @@ -1107,17 +1405,14 @@ definitions: type: "object" example: redirect: "f1f046a3-45c9-4b99-adce-000000000002" - coden: "coden" + full_name: "Grace Hopper" ident: "f1f046a3-45c9-4b99-adce-000000000001" extra: "{}" - name: "Journal of Important Results" - publisher: "Society of Curious Students" editgroup_id: 16 - issnl: "1234-5678" - abbrev: "abbrev" + orcid: "0000-0002-1825-0097" state: "wip" revision: 42 - upperCaseName: "CONTAINER_ENTITY" + upperCaseName: "CREATOR_ENTITY" file_entity: type: "object" properties: diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs index f2eccb9e..b08a70bb 100644 --- a/rust/fatcat-api/examples/client.rs +++ b/rust/fatcat-api/examples/client.rs @@ -11,9 +11,10 @@ extern crate uuid; use clap::{App, Arg}; #[allow(unused_imports)] -use fatcat::{ApiError, ApiNoContext, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, ContextWrapperExt, CreatorIdGetResponse, CreatorLookupGetResponse, - CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, - FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use fatcat::{ApiError, ApiNoContext, ContainerBatchPostResponse, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, ContextWrapperExt, CreatorBatchPostResponse, + CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, + EditorUsernameGetResponse, FileBatchPostResponse, FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseBatchPostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkBatchPostResponse, WorkIdGetResponse, WorkPostResponse}; #[allow(unused_imports)] use futures::{future, stream, Future, Stream}; @@ -23,18 +24,23 @@ fn main() { Arg::with_name("operation") .help("Sets the operation to run") .possible_values(&[ + "ContainerBatchPost", "ContainerIdGet", "ContainerLookupGet", + "CreatorBatchPost", "CreatorIdGet", "CreatorLookupGet", "EditgroupIdAcceptPost", "EditgroupIdGet", "EditorUsernameChangelogGet", "EditorUsernameGet", + "FileBatchPost", "FileIdGet", "FileLookupGet", + "ReleaseBatchPost", "ReleaseIdGet", "ReleaseLookupGet", + "WorkBatchPost", "WorkIdGet", ]) .required(true) @@ -64,6 +70,11 @@ fn main() { let client = client.with_context(fatcat::Context::new_with_span_id(self::uuid::Uuid::new_v4().to_string())); match matches.value_of("operation") { + Some("ContainerBatchPost") => { + let result = client.container_batch_post(&Vec::new()).wait(); + println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); + } + Some("ContainerIdGet") => { let result = client.container_id_get("id_example".to_string()).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); @@ -79,6 +90,11 @@ fn main() { // let result = client.container_post(???).wait(); // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, + Some("CreatorBatchPost") => { + let result = client.creator_batch_post(&Vec::new()).wait(); + println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); + } + Some("CreatorIdGet") => { let result = client.creator_id_get("id_example".to_string()).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); @@ -119,6 +135,11 @@ fn main() { println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); } + Some("FileBatchPost") => { + let result = client.file_batch_post(&Vec::new()).wait(); + println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); + } + Some("FileIdGet") => { let result = client.file_id_get("id_example".to_string()).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); @@ -134,6 +155,11 @@ fn main() { // let result = client.file_post(???).wait(); // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, + Some("ReleaseBatchPost") => { + let result = client.release_batch_post(&Vec::new()).wait(); + println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); + } + Some("ReleaseIdGet") => { let result = client.release_id_get("id_example".to_string()).wait(); println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); @@ -149,6 +175,11 @@ fn main() { // let result = client.release_post(???).wait(); // println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); // }, + Some("WorkBatchPost") => { + let result = client.work_batch_post(&Vec::new()).wait(); + println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>"))); + } + Some("WorkIdGet") => { let result = client.work_id_get("id_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 4ea73046..7c27b09a 100644 --- a/rust/fatcat-api/examples/server_lib/server.rs +++ b/rust/fatcat-api/examples/server_lib/server.rs @@ -10,14 +10,21 @@ use std::collections::HashMap; use swagger; use fatcat::models; -use fatcat::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, - EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, - FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use fatcat::{Api, ApiError, ContainerBatchPostResponse, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, Context, CreatorBatchPostResponse, CreatorIdGetResponse, + CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, + EditorUsernameGetResponse, FileBatchPostResponse, FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseBatchPostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkBatchPostResponse, WorkIdGetResponse, WorkPostResponse}; #[derive(Copy, Clone)] pub struct Server; impl Api for Server { + fn container_batch_post(&self, entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = ContainerBatchPostResponse, Error = ApiError> + Send> { + let context = context.clone(); + println!("container_batch_post({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + fn container_id_get(&self, id: String, context: &Context) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!("container_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone()); @@ -36,6 +43,12 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } + fn creator_batch_post(&self, entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreatorBatchPostResponse, Error = ApiError> + Send> { + let context = context.clone(); + println!("creator_batch_post({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + fn creator_id_get(&self, id: String, context: &Context) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!("creator_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone()); @@ -88,6 +101,12 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } + fn file_batch_post(&self, entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = FileBatchPostResponse, Error = ApiError> + Send> { + let context = context.clone(); + println!("file_batch_post({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + fn file_id_get(&self, id: String, context: &Context) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!("file_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone()); @@ -106,6 +125,12 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } + fn release_batch_post(&self, entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = ReleaseBatchPostResponse, Error = ApiError> + Send> { + let context = context.clone(); + println!("release_batch_post({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + fn release_id_get(&self, id: String, context: &Context) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!("release_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone()); @@ -124,6 +149,12 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } + fn work_batch_post(&self, entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = WorkBatchPostResponse, Error = ApiError> + Send> { + let context = context.clone(); + println!("work_batch_post({:?}) - X-Span-ID: {:?}", entity_list, context.x_span_id.unwrap_or(String::from("<none>")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + fn work_id_get(&self, id: String, context: &Context) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!("work_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone()); diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs index d81d251f..e9f8bf0e 100644 --- a/rust/fatcat-api/src/client.rs +++ b/rust/fatcat-api/src/client.rs @@ -34,9 +34,10 @@ use swagger; use swagger::{ApiError, Context, XSpanId}; use models; -use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse, - EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, - ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use {Api, ContainerBatchPostResponse, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, CreatorBatchPostResponse, CreatorIdGetResponse, CreatorLookupGetResponse, + CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileBatchPostResponse, + FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseBatchPostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, ReleasePostResponse, WorkBatchPostResponse, + WorkIdGetResponse, WorkPostResponse}; /// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. fn into_base_path<T: IntoUrl>(input: T, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> { @@ -161,6 +162,71 @@ impl Client { } impl Api for Client { + fn container_batch_post(&self, param_entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = ContainerBatchPostResponse, Error = ApiError> + Send> { + let url = format!("{}/v0/container/batch", self.base_path); + + let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Post, &url); + let mut custom_headers = hyper::header::Headers::new(); + + let request = request.body(&body); + + custom_headers.set(ContentType(mimetypes::requests::CONTAINER_BATCH_POST.clone())); + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<ContainerBatchPostResponse, ApiError> { + match response.status.to_u16() { + 201 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<Vec<models::EntityEdit>>(&buf)?; + + Ok(ContainerBatchPostResponse::CreatedEntities(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ContainerBatchPostResponse::BadRequest(body)) + } + 404 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ContainerBatchPostResponse::NotFound(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ContainerBatchPostResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("<Failed to read body: {}>", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn container_id_get(&self, param_id: String, context: &Context) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send> { let url = format!("{}/v0/container/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); @@ -349,6 +415,71 @@ impl Api for Client { Box::new(futures::done(result)) } + fn creator_batch_post(&self, param_entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreatorBatchPostResponse, Error = ApiError> + Send> { + let url = format!("{}/v0/creator/batch", self.base_path); + + let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Post, &url); + let mut custom_headers = hyper::header::Headers::new(); + + let request = request.body(&body); + + custom_headers.set(ContentType(mimetypes::requests::CREATOR_BATCH_POST.clone())); + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<CreatorBatchPostResponse, ApiError> { + match response.status.to_u16() { + 201 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<Vec<models::EntityEdit>>(&buf)?; + + Ok(CreatorBatchPostResponse::CreatedEntities(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(CreatorBatchPostResponse::BadRequest(body)) + } + 404 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(CreatorBatchPostResponse::NotFound(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(CreatorBatchPostResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("<Failed to read body: {}>", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn creator_id_get(&self, param_id: String, context: &Context) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError> + Send> { let url = format!("{}/v0/creator/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); @@ -829,6 +960,71 @@ impl Api for Client { Box::new(futures::done(result)) } + fn file_batch_post(&self, param_entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = FileBatchPostResponse, Error = ApiError> + Send> { + let url = format!("{}/v0/file/batch", self.base_path); + + let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Post, &url); + let mut custom_headers = hyper::header::Headers::new(); + + let request = request.body(&body); + + custom_headers.set(ContentType(mimetypes::requests::FILE_BATCH_POST.clone())); + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<FileBatchPostResponse, ApiError> { + match response.status.to_u16() { + 201 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<Vec<models::EntityEdit>>(&buf)?; + + Ok(FileBatchPostResponse::CreatedEntities(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(FileBatchPostResponse::BadRequest(body)) + } + 404 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(FileBatchPostResponse::NotFound(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(FileBatchPostResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("<Failed to read body: {}>", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn file_id_get(&self, param_id: String, context: &Context) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> { let url = format!("{}/v0/file/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); @@ -1017,6 +1213,71 @@ impl Api for Client { Box::new(futures::done(result)) } + fn release_batch_post(&self, param_entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = ReleaseBatchPostResponse, Error = ApiError> + Send> { + let url = format!("{}/v0/release/batch", self.base_path); + + let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Post, &url); + let mut custom_headers = hyper::header::Headers::new(); + + let request = request.body(&body); + + custom_headers.set(ContentType(mimetypes::requests::RELEASE_BATCH_POST.clone())); + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<ReleaseBatchPostResponse, ApiError> { + match response.status.to_u16() { + 201 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<Vec<models::EntityEdit>>(&buf)?; + + Ok(ReleaseBatchPostResponse::CreatedEntities(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ReleaseBatchPostResponse::BadRequest(body)) + } + 404 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ReleaseBatchPostResponse::NotFound(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(ReleaseBatchPostResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("<Failed to read body: {}>", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn release_id_get(&self, param_id: String, context: &Context) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> { let url = format!("{}/v0/release/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); @@ -1205,6 +1466,71 @@ impl Api for Client { Box::new(futures::done(result)) } + fn work_batch_post(&self, param_entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = WorkBatchPostResponse, Error = ApiError> + Send> { + let url = format!("{}/v0/work/batch", self.base_path); + + let body = serde_json::to_string(¶m_entity_list).expect("impossible to fail to serialize"); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Post, &url); + let mut custom_headers = hyper::header::Headers::new(); + + let request = request.body(&body); + + custom_headers.set(ContentType(mimetypes::requests::WORK_BATCH_POST.clone())); + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result<WorkBatchPostResponse, ApiError> { + match response.status.to_u16() { + 201 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<Vec<models::EntityEdit>>(&buf)?; + + Ok(WorkBatchPostResponse::CreatedEntities(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(WorkBatchPostResponse::BadRequest(body)) + } + 404 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(WorkBatchPostResponse::NotFound(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(WorkBatchPostResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("<Failed to read body: {}>", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn work_id_get(&self, param_id: String, context: &Context) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> { let url = format!("{}/v0/work/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs index dca1aa35..1a46fbeb 100644 --- a/rust/fatcat-api/src/lib.rs +++ b/rust/fatcat-api/src/lib.rs @@ -33,6 +33,18 @@ mod mimetypes; pub use swagger::{ApiError, Context, ContextWrapper}; #[derive(Debug, PartialEq)] +pub enum ContainerBatchPostResponse { + /// Created Entities + CreatedEntities(Vec<models::EntityEdit>), + /// Bad Request + BadRequest(models::ErrorResponse), + /// Not Found + NotFound(models::ErrorResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + +#[derive(Debug, PartialEq)] pub enum ContainerIdGetResponse { /// Found Entity FoundEntity(models::ContainerEntity), @@ -69,6 +81,18 @@ pub enum ContainerPostResponse { } #[derive(Debug, PartialEq)] +pub enum CreatorBatchPostResponse { + /// Created Entities + CreatedEntities(Vec<models::EntityEdit>), + /// Bad Request + BadRequest(models::ErrorResponse), + /// Not Found + NotFound(models::ErrorResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + +#[derive(Debug, PartialEq)] pub enum CreatorIdGetResponse { /// Found Entity FoundEntity(models::CreatorEntity), @@ -159,6 +183,18 @@ pub enum EditorUsernameGetResponse { } #[derive(Debug, PartialEq)] +pub enum FileBatchPostResponse { + /// Created Entities + CreatedEntities(Vec<models::EntityEdit>), + /// Bad Request + BadRequest(models::ErrorResponse), + /// Not Found + NotFound(models::ErrorResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + +#[derive(Debug, PartialEq)] pub enum FileIdGetResponse { /// Found Entity FoundEntity(models::FileEntity), @@ -195,6 +231,18 @@ pub enum FilePostResponse { } #[derive(Debug, PartialEq)] +pub enum ReleaseBatchPostResponse { + /// Created Entities + CreatedEntities(Vec<models::EntityEdit>), + /// Bad Request + BadRequest(models::ErrorResponse), + /// Not Found + NotFound(models::ErrorResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + +#[derive(Debug, PartialEq)] pub enum ReleaseIdGetResponse { /// Found Entity FoundEntity(models::ReleaseEntity), @@ -231,6 +279,18 @@ pub enum ReleasePostResponse { } #[derive(Debug, PartialEq)] +pub enum WorkBatchPostResponse { + /// Created Entities + CreatedEntities(Vec<models::EntityEdit>), + /// Bad Request + BadRequest(models::ErrorResponse), + /// Not Found + NotFound(models::ErrorResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + +#[derive(Debug, PartialEq)] pub enum WorkIdGetResponse { /// Found Entity FoundEntity(models::WorkEntity), @@ -256,12 +316,16 @@ pub enum WorkPostResponse { /// API pub trait Api { + fn container_batch_post(&self, entity_list: &Vec<models::ContainerEntity>, context: &Context) -> Box<Future<Item = ContainerBatchPostResponse, Error = ApiError> + Send>; + fn container_id_get(&self, id: String, context: &Context) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send>; fn container_lookup_get(&self, issnl: String, context: &Context) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError> + Send>; fn container_post(&self, entity: models::ContainerEntity, context: &Context) -> Box<Future<Item = ContainerPostResponse, Error = ApiError> + Send>; + fn creator_batch_post(&self, entity_list: &Vec<models::CreatorEntity>, context: &Context) -> Box<Future<Item = CreatorBatchPostResponse, Error = ApiError> + Send>; + fn creator_id_get(&self, id: String, context: &Context) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError> + Send>; fn creator_lookup_get(&self, orcid: String, context: &Context) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError> + Send>; @@ -278,18 +342,24 @@ pub trait Api { fn editor_username_get(&self, username: String, context: &Context) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send>; + fn file_batch_post(&self, entity_list: &Vec<models::FileEntity>, context: &Context) -> Box<Future<Item = FileBatchPostResponse, Error = ApiError> + Send>; + fn file_id_get(&self, id: String, context: &Context) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send>; fn file_lookup_get(&self, sha1: String, context: &Context) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send>; fn file_post(&self, entity: models::FileEntity, context: &Context) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send>; + fn release_batch_post(&self, entity_list: &Vec<models::ReleaseEntity>, context: &Context) -> Box<Future<Item = ReleaseBatchPostResponse, Error = ApiError> + Send>; + fn release_id_get(&self, id: String, context: &Context) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send>; fn release_lookup_get(&self, doi: String, context: &Context) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send>; fn release_post(&self, entity: models::ReleaseEntity, context: &Context) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send>; + fn work_batch_post(&self, entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = WorkBatchPostResponse, Error = ApiError> + Send>; + fn work_id_get(&self, id: String, context: &Context) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send>; fn work_post(&self, entity: models::WorkEntity, context: &Context) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send>; @@ -297,12 +367,16 @@ pub trait Api { /// API without a `Context` pub trait ApiNoContext { + fn container_batch_post(&self, entity_list: &Vec<models::ContainerEntity>) -> Box<Future<Item = ContainerBatchPostResponse, Error = ApiError> + Send>; + fn container_id_get(&self, id: String) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send>; fn container_lookup_get(&self, issnl: String) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError> + Send>; fn container_post(&self, entity: models::ContainerEntity) -> Box<Future<Item = ContainerPostResponse, Error = ApiError> + Send>; + fn creator_batch_post(&self, entity_list: &Vec<models::CreatorEntity>) -> Box<Future<Item = CreatorBatchPostResponse, Error = ApiError> + Send>; + fn creator_id_get(&self, id: String) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError> + Send>; fn creator_lookup_get(&self, orcid: String) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError> + Send>; @@ -319,18 +393,24 @@ pub trait ApiNoContext { fn editor_username_get(&self, username: String) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send>; + fn file_batch_post(&self, entity_list: &Vec<models::FileEntity>) -> Box<Future<Item = FileBatchPostResponse, Error = ApiError> + Send>; + fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send>; fn file_lookup_get(&self, sha1: String) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send>; fn file_post(&self, entity: models::FileEntity) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send>; + fn release_batch_post(&self, entity_list: &Vec<models::ReleaseEntity>) -> Box<Future<Item = ReleaseBatchPostResponse, Error = ApiError> + Send>; + fn release_id_get(&self, id: String) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send>; fn release_lookup_get(&self, doi: String) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send>; fn release_post(&self, entity: models::ReleaseEntity) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send>; + fn work_batch_post(&self, entity_list: &Vec<models::WorkEntity>) -> Box<Future<Item = WorkBatchPostResponse, Error = ApiError> + Send>; + fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send>; fn work_post(&self, entity: models::WorkEntity) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send>; @@ -352,6 +432,10 @@ impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T { } impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { + fn container_batch_post(&self, entity_list: &Vec<models::ContainerEntity>) -> Box<Future<Item = ContainerBatchPostResponse, Error = ApiError> + Send> { + self.api().container_batch_post(entity_list, &self.context()) + } + fn container_id_get(&self, id: String) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send> { self.api().container_id_get(id, &self.context()) } @@ -364,6 +448,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().container_post(entity, &self.context()) } + fn creator_batch_post(&self, entity_list: &Vec<models::CreatorEntity>) -> Box<Future<Item = CreatorBatchPostResponse, Error = ApiError> + Send> { + self.api().creator_batch_post(entity_list, &self.context()) + } + fn creator_id_get(&self, id: String) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError> + Send> { self.api().creator_id_get(id, &self.context()) } @@ -396,6 +484,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().editor_username_get(username, &self.context()) } + fn file_batch_post(&self, entity_list: &Vec<models::FileEntity>) -> Box<Future<Item = FileBatchPostResponse, Error = ApiError> + Send> { + self.api().file_batch_post(entity_list, &self.context()) + } + fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> { self.api().file_id_get(id, &self.context()) } @@ -408,6 +500,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().file_post(entity, &self.context()) } + fn release_batch_post(&self, entity_list: &Vec<models::ReleaseEntity>) -> Box<Future<Item = ReleaseBatchPostResponse, Error = ApiError> + Send> { + self.api().release_batch_post(entity_list, &self.context()) + } + fn release_id_get(&self, id: String) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> { self.api().release_id_get(id, &self.context()) } @@ -420,6 +516,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().release_post(entity, &self.context()) } + fn work_batch_post(&self, entity_list: &Vec<models::WorkEntity>) -> Box<Future<Item = WorkBatchPostResponse, Error = ApiError> + Send> { + self.api().work_batch_post(entity_list, &self.context()) + } + fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> { self.api().work_id_get(id, &self.context()) } diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs index 5814890f..7b85cac5 100644 --- a/rust/fatcat-api/src/mimetypes.rs +++ b/rust/fatcat-api/src/mimetypes.rs @@ -4,6 +4,22 @@ pub mod responses { use hyper::mime::*; // The macro is called per-operation to beat the recursion limit + /// Create Mime objects for the response content types for ContainerBatchPost + lazy_static! { + pub static ref CONTAINER_BATCH_POST_CREATED_ENTITIES: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ContainerBatchPost + lazy_static! { + pub static ref CONTAINER_BATCH_POST_BAD_REQUEST: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ContainerBatchPost + lazy_static! { + pub static ref CONTAINER_BATCH_POST_NOT_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ContainerBatchPost + lazy_static! { + pub static ref CONTAINER_BATCH_POST_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for ContainerIdGet lazy_static! { pub static ref CONTAINER_ID_GET_FOUND_ENTITY: Mime = mime!(Application / Json); @@ -52,6 +68,22 @@ pub mod responses { lazy_static! { pub static ref CONTAINER_POST_GENERIC_ERROR: Mime = mime!(Application / Json); } + /// Create Mime objects for the response content types for CreatorBatchPost + lazy_static! { + pub static ref CREATOR_BATCH_POST_CREATED_ENTITIES: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for CreatorBatchPost + lazy_static! { + pub static ref CREATOR_BATCH_POST_BAD_REQUEST: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for CreatorBatchPost + lazy_static! { + pub static ref CREATOR_BATCH_POST_NOT_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for CreatorBatchPost + lazy_static! { + pub static ref CREATOR_BATCH_POST_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for CreatorIdGet lazy_static! { pub static ref CREATOR_ID_GET_FOUND_ENTITY: Mime = mime!(Application / Json); @@ -168,6 +200,22 @@ pub mod responses { lazy_static! { pub static ref EDITOR_USERNAME_GET_GENERIC_ERROR: Mime = mime!(Application / Json); } + /// Create Mime objects for the response content types for FileBatchPost + lazy_static! { + pub static ref FILE_BATCH_POST_CREATED_ENTITIES: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for FileBatchPost + lazy_static! { + pub static ref FILE_BATCH_POST_BAD_REQUEST: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for FileBatchPost + lazy_static! { + pub static ref FILE_BATCH_POST_NOT_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for FileBatchPost + lazy_static! { + pub static ref FILE_BATCH_POST_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for FileIdGet lazy_static! { pub static ref FILE_ID_GET_FOUND_ENTITY: Mime = mime!(Application / Json); @@ -216,6 +264,22 @@ pub mod responses { lazy_static! { pub static ref FILE_POST_GENERIC_ERROR: Mime = mime!(Application / Json); } + /// Create Mime objects for the response content types for ReleaseBatchPost + lazy_static! { + pub static ref RELEASE_BATCH_POST_CREATED_ENTITIES: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ReleaseBatchPost + lazy_static! { + pub static ref RELEASE_BATCH_POST_BAD_REQUEST: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ReleaseBatchPost + lazy_static! { + pub static ref RELEASE_BATCH_POST_NOT_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for ReleaseBatchPost + lazy_static! { + pub static ref RELEASE_BATCH_POST_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for ReleaseIdGet lazy_static! { pub static ref RELEASE_ID_GET_FOUND_ENTITY: Mime = mime!(Application / Json); @@ -264,6 +328,22 @@ pub mod responses { lazy_static! { pub static ref RELEASE_POST_GENERIC_ERROR: Mime = mime!(Application / Json); } + /// Create Mime objects for the response content types for WorkBatchPost + lazy_static! { + pub static ref WORK_BATCH_POST_CREATED_ENTITIES: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for WorkBatchPost + lazy_static! { + pub static ref WORK_BATCH_POST_BAD_REQUEST: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for WorkBatchPost + lazy_static! { + pub static ref WORK_BATCH_POST_NOT_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for WorkBatchPost + lazy_static! { + pub static ref WORK_BATCH_POST_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for WorkIdGet lazy_static! { pub static ref WORK_ID_GET_FOUND_ENTITY: Mime = mime!(Application / Json); @@ -301,10 +381,18 @@ pub mod responses { pub mod requests { use hyper::mime::*; + /// Create Mime objects for the request content types for ContainerBatchPost + lazy_static! { + pub static ref CONTAINER_BATCH_POST: Mime = mime!(Application / Json); + } /// Create Mime objects for the request content types for ContainerPost lazy_static! { pub static ref CONTAINER_POST: Mime = mime!(Application / Json); } + /// Create Mime objects for the request content types for CreatorBatchPost + lazy_static! { + pub static ref CREATOR_BATCH_POST: Mime = mime!(Application / Json); + } /// Create Mime objects for the request content types for CreatorPost lazy_static! { pub static ref CREATOR_POST: Mime = mime!(Application / Json); @@ -313,14 +401,26 @@ pub mod requests { lazy_static! { pub static ref EDITGROUP_POST: Mime = mime!(Application / Json); } + /// Create Mime objects for the request content types for FileBatchPost + lazy_static! { + pub static ref FILE_BATCH_POST: Mime = mime!(Application / Json); + } /// Create Mime objects for the request content types for FilePost lazy_static! { pub static ref FILE_POST: Mime = mime!(Application / Json); } + /// Create Mime objects for the request content types for ReleaseBatchPost + lazy_static! { + pub static ref RELEASE_BATCH_POST: Mime = mime!(Application / Json); + } /// Create Mime objects for the request content types for ReleasePost lazy_static! { pub static ref RELEASE_POST: Mime = mime!(Application / Json); } + /// Create Mime objects for the request content types for WorkBatchPost + lazy_static! { + pub static ref WORK_BATCH_POST: Mime = mime!(Application / Json); + } /// Create Mime objects for the request content types for WorkPost lazy_static! { pub static ref WORK_POST: Mime = mime!(Application / Json); diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs index 24c66f6b..ab18e9b3 100644 --- a/rust/fatcat-api/src/models.rs +++ b/rust/fatcat-api/src/models.rs @@ -113,30 +113,30 @@ pub struct ContainerEntity { #[serde(rename = "name")] pub name: String, - // Note: inline enums are not fully supported by swagger-codegen - #[serde(rename = "state")] - #[serde(skip_serializing_if = "Option::is_none")] - pub state: Option<String>, - - #[serde(rename = "ident")] + #[serde(rename = "extra")] #[serde(skip_serializing_if = "Option::is_none")] - pub ident: Option<String>, + pub extra: Option<serde_json::Value>, - #[serde(rename = "revision")] + #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] - pub revision: Option<i64>, + pub editgroup_id: Option<i64>, #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] pub redirect: Option<String>, - #[serde(rename = "editgroup_id")] + #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] - pub editgroup_id: Option<i64>, + pub revision: Option<i64>, - #[serde(rename = "extra")] + #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] - pub extra: Option<serde_json::Value>, + pub ident: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, } impl ContainerEntity { @@ -147,12 +147,12 @@ impl ContainerEntity { issnl: None, publisher: None, name: name, - state: None, - ident: None, - revision: None, - redirect: None, - editgroup_id: None, extra: None, + editgroup_id: None, + redirect: None, + revision: None, + ident: None, + state: None, } } } @@ -166,30 +166,30 @@ pub struct CreatorEntity { #[serde(rename = "full_name")] pub full_name: String, - #[serde(rename = "extra")] - #[serde(skip_serializing_if = "Option::is_none")] - pub extra: Option<serde_json::Value>, - - #[serde(rename = "editgroup_id")] + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] #[serde(skip_serializing_if = "Option::is_none")] - pub editgroup_id: Option<i64>, + pub state: Option<String>, - #[serde(rename = "redirect")] + #[serde(rename = "ident")] #[serde(skip_serializing_if = "Option::is_none")] - pub redirect: Option<String>, + pub ident: Option<String>, #[serde(rename = "revision")] #[serde(skip_serializing_if = "Option::is_none")] pub revision: Option<i64>, - #[serde(rename = "ident")] + #[serde(rename = "redirect")] #[serde(skip_serializing_if = "Option::is_none")] - pub ident: Option<String>, + pub redirect: Option<String>, - // Note: inline enums are not fully supported by swagger-codegen - #[serde(rename = "state")] + #[serde(rename = "editgroup_id")] #[serde(skip_serializing_if = "Option::is_none")] - pub state: Option<String>, + pub editgroup_id: Option<i64>, + + #[serde(rename = "extra")] + #[serde(skip_serializing_if = "Option::is_none")] + pub extra: Option<serde_json::Value>, } impl CreatorEntity { @@ -197,12 +197,12 @@ impl CreatorEntity { CreatorEntity { orcid: None, full_name: full_name, - extra: None, - editgroup_id: None, - redirect: None, - revision: None, - ident: None, state: None, + ident: None, + revision: None, + redirect: None, + editgroup_id: None, + extra: None, } } } diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs index 7fdc5d2a..0db2c445 100644 --- a/rust/fatcat-api/src/server.rs +++ b/rust/fatcat-api/src/server.rs @@ -36,9 +36,10 @@ use swagger::{ApiError, Context, XSpanId}; #[allow(unused_imports)] use models; -use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse, - EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, - ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use {Api, ContainerBatchPostResponse, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, CreatorBatchPostResponse, CreatorIdGetResponse, CreatorLookupGetResponse, + CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileBatchPostResponse, + FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseBatchPostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, ReleasePostResponse, WorkBatchPostResponse, + WorkIdGetResponse, WorkPostResponse}; header! { (Warning, "Warning") => [String] } @@ -86,6 +87,112 @@ where T: Api + Send + Sync + Clone + 'static, { let api_clone = api.clone(); + router.post( + "/v0/container/batch", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> + where + T: Api, + { + context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::<AuthData>(); + context.authorization = req.extensions.remove::<Authorization>(); + + // 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. + + let param_entity_list = req.get::<bodyparser::Raw>() + .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - not valid UTF-8: {}", e))))?; + + let mut unused_elements = Vec::new(); + + let param_entity_list = if let Some(param_entity_list_raw) = param_entity_list { + let deserializer = &mut serde_json::Deserializer::from_str(¶m_entity_list_raw); + + let param_entity_list: Option<Vec<models::ContainerEntity>> = + serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - doesn't match schema: {}", e))))?; + + param_entity_list + } else { + None + }; + let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; + + match api.container_batch_post(param_entity_list.as_ref(), context).wait() { + Ok(rsp) => match rsp { + ContainerBatchPostResponse::CreatedEntities(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(201), body_string)); + response.headers.set(ContentType(mimetypes::responses::CONTAINER_BATCH_POST_CREATED_ENTITIES.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ContainerBatchPostResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::CONTAINER_BATCH_POST_BAD_REQUEST.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ContainerBatchPostResponse::NotFound(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(404), body_string)); + response.headers.set(ContentType(mimetypes::responses::CONTAINER_BATCH_POST_NOT_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ContainerBatchPostResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::CONTAINER_BATCH_POST_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "ContainerBatchPost", + ); + + let api_clone = api.clone(); router.get( "/v0/container/:id", move |req: &mut Request| { @@ -364,6 +471,112 @@ where ); let api_clone = api.clone(); + router.post( + "/v0/creator/batch", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> + where + T: Api, + { + context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::<AuthData>(); + context.authorization = req.extensions.remove::<Authorization>(); + + // 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. + + let param_entity_list = req.get::<bodyparser::Raw>() + .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - not valid UTF-8: {}", e))))?; + + let mut unused_elements = Vec::new(); + + let param_entity_list = if let Some(param_entity_list_raw) = param_entity_list { + let deserializer = &mut serde_json::Deserializer::from_str(¶m_entity_list_raw); + + let param_entity_list: Option<Vec<models::CreatorEntity>> = + serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - doesn't match schema: {}", e))))?; + + param_entity_list + } else { + None + }; + let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; + + match api.creator_batch_post(param_entity_list.as_ref(), context).wait() { + Ok(rsp) => match rsp { + CreatorBatchPostResponse::CreatedEntities(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(201), body_string)); + response.headers.set(ContentType(mimetypes::responses::CREATOR_BATCH_POST_CREATED_ENTITIES.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + CreatorBatchPostResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::CREATOR_BATCH_POST_BAD_REQUEST.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + CreatorBatchPostResponse::NotFound(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(404), body_string)); + response.headers.set(ContentType(mimetypes::responses::CREATOR_BATCH_POST_NOT_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + CreatorBatchPostResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::CREATOR_BATCH_POST_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "CreatorBatchPost", + ); + + let api_clone = api.clone(); router.get( "/v0/creator/:id", move |req: &mut Request| { @@ -1067,6 +1280,112 @@ where ); let api_clone = api.clone(); + router.post( + "/v0/file/batch", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> + where + T: Api, + { + context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::<AuthData>(); + context.authorization = req.extensions.remove::<Authorization>(); + + // 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. + + let param_entity_list = req.get::<bodyparser::Raw>() + .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - not valid UTF-8: {}", e))))?; + + let mut unused_elements = Vec::new(); + + let param_entity_list = if let Some(param_entity_list_raw) = param_entity_list { + let deserializer = &mut serde_json::Deserializer::from_str(¶m_entity_list_raw); + + let param_entity_list: Option<Vec<models::FileEntity>> = + serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - doesn't match schema: {}", e))))?; + + param_entity_list + } else { + None + }; + let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; + + match api.file_batch_post(param_entity_list.as_ref(), context).wait() { + Ok(rsp) => match rsp { + FileBatchPostResponse::CreatedEntities(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(201), body_string)); + response.headers.set(ContentType(mimetypes::responses::FILE_BATCH_POST_CREATED_ENTITIES.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + FileBatchPostResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::FILE_BATCH_POST_BAD_REQUEST.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + FileBatchPostResponse::NotFound(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(404), body_string)); + response.headers.set(ContentType(mimetypes::responses::FILE_BATCH_POST_NOT_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + FileBatchPostResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::FILE_BATCH_POST_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "FileBatchPost", + ); + + let api_clone = api.clone(); router.get( "/v0/file/:id", move |req: &mut Request| { @@ -1345,6 +1664,112 @@ where ); let api_clone = api.clone(); + router.post( + "/v0/release/batch", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> + where + T: Api, + { + context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::<AuthData>(); + context.authorization = req.extensions.remove::<Authorization>(); + + // 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. + + let param_entity_list = req.get::<bodyparser::Raw>() + .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - not valid UTF-8: {}", e))))?; + + let mut unused_elements = Vec::new(); + + let param_entity_list = if let Some(param_entity_list_raw) = param_entity_list { + let deserializer = &mut serde_json::Deserializer::from_str(¶m_entity_list_raw); + + let param_entity_list: Option<Vec<models::ReleaseEntity>> = + serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - doesn't match schema: {}", e))))?; + + param_entity_list + } else { + None + }; + let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; + + match api.release_batch_post(param_entity_list.as_ref(), context).wait() { + Ok(rsp) => match rsp { + ReleaseBatchPostResponse::CreatedEntities(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(201), body_string)); + response.headers.set(ContentType(mimetypes::responses::RELEASE_BATCH_POST_CREATED_ENTITIES.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ReleaseBatchPostResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::RELEASE_BATCH_POST_BAD_REQUEST.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ReleaseBatchPostResponse::NotFound(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(404), body_string)); + response.headers.set(ContentType(mimetypes::responses::RELEASE_BATCH_POST_NOT_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + ReleaseBatchPostResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::RELEASE_BATCH_POST_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "ReleaseBatchPost", + ); + + let api_clone = api.clone(); router.get( "/v0/release/:id", move |req: &mut Request| { @@ -1623,6 +2048,112 @@ where ); let api_clone = api.clone(); + router.post( + "/v0/work/batch", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response> + where + T: Api, + { + context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::<AuthData>(); + context.authorization = req.extensions.remove::<Authorization>(); + + // 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. + + let param_entity_list = req.get::<bodyparser::Raw>() + .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - not valid UTF-8: {}", e))))?; + + let mut unused_elements = Vec::new(); + + let param_entity_list = if let Some(param_entity_list_raw) = param_entity_list { + let deserializer = &mut serde_json::Deserializer::from_str(¶m_entity_list_raw); + + let param_entity_list: Option<Vec<models::WorkEntity>> = + serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }).map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter entity_list - doesn't match schema: {}", e))))?; + + param_entity_list + } else { + None + }; + let param_entity_list = param_entity_list.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter entity_list".to_string())))?; + + match api.work_batch_post(param_entity_list.as_ref(), context).wait() { + Ok(rsp) => match rsp { + WorkBatchPostResponse::CreatedEntities(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(201), body_string)); + response.headers.set(ContentType(mimetypes::responses::WORK_BATCH_POST_CREATED_ENTITIES.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + WorkBatchPostResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::WORK_BATCH_POST_BAD_REQUEST.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + WorkBatchPostResponse::NotFound(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(404), body_string)); + response.headers.set(ContentType(mimetypes::responses::WORK_BATCH_POST_NOT_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + WorkBatchPostResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::WORK_BATCH_POST_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + if !unused_elements.is_empty() { + response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); + } + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "WorkBatchPost", + ); + + let api_clone = api.clone(); router.get( "/v0/work/:id", move |req: &mut Request| { diff --git a/rust/fatcat-openapi2.yml b/rust/fatcat-openapi2.yml index f1b94234..8fcbf488 100644 --- a/rust/fatcat-openapi2.yml +++ b/rust/fatcat-openapi2.yml @@ -57,19 +57,6 @@ definitions: message: type: string example: "The computers did the thing successfully!" - creator_entity: - type: object - required: - - full_name - properties: - <<: *ENTITYPROPS - full_name: - type: string - example: "Grace Hopper" - orcid: - type: string - #format: custom - example: "0000-0002-1825-0097" container_entity: type: object required: @@ -90,6 +77,19 @@ definitions: type: string coden: type: string + creator_entity: + type: object + required: + - full_name + properties: + <<: *ENTITYPROPS + full_name: + type: string + example: "Grace Hopper" + orcid: + type: string + #format: custom + example: "0000-0002-1825-0097" file_entity: type: object properties: @@ -300,39 +300,39 @@ x-entity-responses: &ENTITYRESPONSES $ref: "#/definitions/error_response" paths: - /creator: + /container: post: parameters: - name: entity in: body required: true schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" responses: 201: description: Created Entity schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES -# /creator/batch: -# post: -# parameters: -# - name: entity_list -# in: body -# required: true -# schema: -# type: array -# items: -# $ref: "#/definitions/creator_entity" -# responses: -# 201: -# description: Created Entities -# schema: -# type: array -# items: -# $ref: "#/definitions/entity_edit" -# <<: *ENTITYRESPONSES - /creator/{id}: + /container/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/container_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /container/{id}: parameters: - name: id in: path @@ -343,12 +343,12 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" <<: *ENTITYRESPONSES - /creator/lookup: + /container/lookup: get: parameters: - - name: orcid + - name: issnl in: query type: string required: true @@ -356,23 +356,41 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/creator_entity" + $ref: "#/definitions/container_entity" <<: *ENTITYRESPONSES - /container: + /creator: post: parameters: - name: entity in: body required: true schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" responses: 201: description: Created Entity schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES - /container/{id}: + /creator/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/creator_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /creator/{id}: parameters: - name: id in: path @@ -383,12 +401,12 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" <<: *ENTITYRESPONSES - /container/lookup: + /creator/lookup: get: parameters: - - name: issnl + - name: orcid in: query type: string required: true @@ -396,7 +414,7 @@ paths: 200: description: Found Entity schema: - $ref: "#/definitions/container_entity" + $ref: "#/definitions/creator_entity" <<: *ENTITYRESPONSES /file: post: @@ -412,6 +430,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /file/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/file_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /file/{id}: parameters: - name: id @@ -452,6 +488,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /release/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/release_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /release/{id}: parameters: - name: id @@ -492,6 +546,24 @@ paths: schema: $ref: "#/definitions/entity_edit" <<: *ENTITYRESPONSES + /work/batch: + post: + parameters: + - name: entity_list + in: body + required: true + schema: + type: array + items: + $ref: "#/definitions/work_entity" + responses: + 201: + description: Created Entities + schema: + type: array + items: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES /work/{id}: parameters: - name: id diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index bde73da5..883b3ae4 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -13,13 +13,7 @@ use diesel::{self, insert_into}; use errors::*; use fatcat_api::models; use fatcat_api::models::*; -use fatcat_api::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse, - ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse, - CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, - EditgroupPostResponse, EditorUsernameChangelogGetResponse, - EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, - FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, - ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use fatcat_api::*; use futures::{self, Future}; use uuid; @@ -34,7 +28,8 @@ macro_rules! wrap_entity_handlers { // in the context of defining new functions. // The only stable approach I know of would be: https://github.com/dtolnay/mashup ($get_fn:ident, $get_handler:ident, $get_resp:ident, $post_fn:ident, $post_handler:ident, - $post_resp:ident, $model:ident) => { + $post_resp:ident, $post_batch_fn:ident, $post_batch_handler:ident, + $post_batch_resp:ident, $model:ident) => { fn $get_fn( &self, id: String, @@ -57,7 +52,7 @@ macro_rules! wrap_entity_handlers { entity: models::$model, _context: &Context, ) -> Box<Future<Item = $post_resp, Error = ApiError> + Send> { - let ret = match self.$post_handler(entity) { + let ret = match self.$post_handler(entity, None) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => @@ -66,8 +61,38 @@ macro_rules! wrap_entity_handlers { }; Box::new(futures::done(Ok(ret))) } + + fn $post_batch_fn( + &self, + entity_list: &Vec<models::$model>, + _context: &Context, + ) -> Box<Future<Item = $post_batch_resp, Error = ApiError> + Send> { + let ret = match self.$post_batch_handler(entity_list) { + Ok(edit) => + $post_batch_resp::CreatedEntities(edit), + Err(Error(ErrorKind::Diesel(e), _)) => + $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => $post_batch_resp::GenericError(ErrorResponse { message: e.to_string() }), + }; + Box::new(futures::done(Ok(ret))) + } + } +} + +macro_rules! entity_batch_post_handler { + ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { + fn $post_batch_handler(&self, entity_list: &Vec<models::$model>) -> Result<Vec<EntityEdit>> { + let conn = self.db_pool.get().expect("db_pool error"); + // TODO: start a transaction + let mut ret: Vec<EntityEdit> = vec![]; + for entity in entity_list.into_iter() { + ret.push(self.$post_handler(entity.clone(), Some(&conn))?); + } + Ok(ret) + } } } + macro_rules! wrap_lookup_handler { ($get_fn:ident, $get_handler:ident, $get_resp:ident, $idname:ident, $idtype:ident) => { fn $get_fn( @@ -374,11 +399,20 @@ impl Server { work_row2entity(Some(ident), rev) } - fn container_post_handler(&self, entity: models::ContainerEntity) -> Result<EntityEdit> { - let conn = self.db_pool.get().expect("db_pool error"); + fn container_post_handler(&self, entity: models::ContainerEntity, conn: Option<&DbConn>) -> Result<EntityEdit> { + // TODO: still can't cast for some reason + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), + None => get_or_create_editgroup(editor_id, &conn)?, Some(param) => param as i64, }; @@ -399,13 +433,38 @@ impl Server { .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.coden) .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) .bind::<diesel::sql_types::BigInt, _>(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; edit.to_model() } - fn creator_post_handler(&self, entity: models::CreatorEntity) -> Result<EntityEdit> { + entity_batch_post_handler!(container_post_handler, container_batch_post_handler, ContainerEntity); + entity_batch_post_handler!(creator_post_handler, creator_batch_post_handler, CreatorEntity); + entity_batch_post_handler!(file_post_handler, file_batch_post_handler, FileEntity); + entity_batch_post_handler!(release_post_handler, release_batch_post_handler, ReleaseEntity); + entity_batch_post_handler!(work_post_handler, work_batch_post_handler, WorkEntity); +/* XXX: + fn container_batch_post_handler(&self, entity_list: &Vec<models::ContainerEntity>) -> Result<Vec<EntityEdit>> { let conn = self.db_pool.get().expect("db_pool error"); + // TODO: start a transaction + let ret: Vec<EntityEdit> = vec![]; + for entity in entity_list.into_iter() { + ret.push(self.container_post_handler(*entity, Some(conn))?); + } + Ok(ret) + } +*/ + + fn creator_post_handler(&self, entity: models::CreatorEntity, conn: Option<&DbConn>) -> Result<EntityEdit> { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -426,13 +485,21 @@ impl Server { .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.orcid) .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) .bind::<diesel::sql_types::BigInt, _>(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; edit.to_model() } - fn file_post_handler(&self, entity: models::FileEntity) -> Result<EntityEdit> { - let conn = self.db_pool.get().expect("db_pool error"); + fn file_post_handler(&self, entity: models::FileEntity, conn: Option<&DbConn>) -> Result<EntityEdit> { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -456,7 +523,7 @@ impl Server { .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.url) .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) .bind::<diesel::sql_types::BigInt, _>(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; let _releases: Option<Vec<FileReleaseRow>> = match entity.releases { None => None, @@ -473,7 +540,7 @@ impl Server { .collect(); let release_rows: Vec<FileReleaseRow> = insert_into(file_release::table) .values(release_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting file_releases"); Some(release_rows) } @@ -483,35 +550,16 @@ impl Server { edit.to_model() } - fn work_post_handler(&self, entity: models::WorkEntity) -> Result<EntityEdit> { - let conn = self.db_pool.get().expect("db_pool error"); - let editor_id = 1; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), - Some(param) => param as i64, + fn release_post_handler(&self, entity: models::ReleaseEntity, conn: Option<&DbConn>) -> Result<EntityEdit> { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), }; - - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (work_type, extra_json) - VALUES ($1, $2) - RETURNING id ), - ident AS ( INSERT INTO work_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES - ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.work_type) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::BigInt, _>(editgroup_id) - .get_result(&conn)?; - - edit.to_model() - } - - fn release_post_handler(&self, entity: models::ReleaseEntity) -> Result<EntityEdit> { - let conn = self.db_pool.get().expect("db_pool error"); let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -548,7 +596,7 @@ impl Server { .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.publisher) .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) .bind::<diesel::sql_types::BigInt, _>(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; let _refs: Option<Vec<ReleaseRefRow>> = match entity.refs { None => None, @@ -569,7 +617,7 @@ impl Server { .collect(); let ref_rows: Vec<ReleaseRefRow> = insert_into(release_ref::table) .values(ref_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting release_refs"); Some(ref_rows) } @@ -596,7 +644,7 @@ impl Server { .collect(); let contrib_rows: Vec<ReleaseContribRow> = insert_into(release_contrib::table) .values(contrib_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting release_contribs"); Some(contrib_rows) } @@ -606,6 +654,42 @@ impl Server { edit.to_model() } + fn work_post_handler(&self, entity: models::WorkEntity, conn: Option<&DbConn>) -> Result<EntityEdit> { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; + + let editor_id = 1; // TODO: auth + let editgroup_id = match entity.editgroup_id { + None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), + Some(param) => param as i64, + }; + + let edit: WorkEditRow = + diesel::sql_query( + "WITH rev AS ( INSERT INTO work_rev (work_type, extra_json) + VALUES ($1, $2) + RETURNING id ), + ident AS ( INSERT INTO work_ident (rev_id) + VALUES ((SELECT rev.id FROM rev)) + RETURNING id ) + INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES + ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + RETURNING *", + ).bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.work_type) + .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) + .bind::<diesel::sql_types::BigInt, _>(editgroup_id) + .get_result(conn)?; + + edit.to_model() + } + fn editgroup_id_get_handler(&self, id: i64) -> Result<Option<Editgroup>> { let conn = self.db_pool.get().expect("db_pool error"); @@ -709,6 +793,9 @@ impl Api for Server { container_post, container_post_handler, ContainerPostResponse, + container_batch_post, + container_batch_post_handler, + ContainerBatchPostResponse, ContainerEntity ); wrap_entity_handlers!( @@ -718,6 +805,9 @@ impl Api for Server { creator_post, creator_post_handler, CreatorPostResponse, + creator_batch_post, + creator_batch_post_handler, + CreatorBatchPostResponse, CreatorEntity ); wrap_entity_handlers!( @@ -727,6 +817,9 @@ impl Api for Server { file_post, file_post_handler, FilePostResponse, + file_batch_post, + file_batch_post_handler, + FileBatchPostResponse, FileEntity ); wrap_entity_handlers!( @@ -736,6 +829,9 @@ impl Api for Server { release_post, release_post_handler, ReleasePostResponse, + release_batch_post, + release_batch_post_handler, + ReleaseBatchPostResponse, ReleaseEntity ); wrap_entity_handlers!( @@ -745,6 +841,9 @@ impl Api for Server { work_post, work_post_handler, WorkPostResponse, + work_batch_post, + work_batch_post_handler, + WorkBatchPostResponse, WorkEntity ); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0038f9db..8ec850c5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -24,6 +24,7 @@ mod errors { error_chain! { foreign_links { Fmt(::std::fmt::Error); Diesel(::diesel::result::Error); + R2d2(::diesel::r2d2::Error); Uuid(::uuid::ParseError); Io(::std::io::Error) #[cfg(unix)]; } diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index a973b333..c5a5a60c 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -163,6 +163,22 @@ fn test_post_container() { } #[test] +fn test_post_batch_container() { + let (headers, router, _conn) = setup(); + + check_response( + request::post( + "http://localhost:9411/v0/container/batch", + headers, + r#"[{"name": "test journal"}, {"name": "another test journal"}]"#, + &router, + ), + status::Created, + None, + ); // TODO: "test journal" +} + +#[test] fn test_post_creator() { let (headers, router, _conn) = setup(); |