From 66d19f7a2d8bce7f565ee7113a6cc920a7142ded Mon Sep 17 00:00:00 2001 From: Lionel Sambuc Date: Wed, 25 Sep 2019 17:11:10 +0200 Subject: [PATCH] Upgraded to actix_web 1.0+ * Some refactoring * Adding CORS support * Updating API & tests with 405 for the relevent calls * Fixing test so they can be run --- Cargo.toml | 20 +- api/swagger.yaml | 2140 ----------------- src/main.rs | 33 +- src/rest_api/actions.rs | 91 +- src/rest_api/core.rs | 91 +- src/rest_api/cores.rs | 75 +- src/rest_api/default.rs | 101 - src/rest_api/helpers.rs | 88 + src/rest_api/helpers_dynamic_pages.rs | 23 + src/rest_api/helpers_static_pages.rs | 25 + src/rest_api/mod.rs | 452 ++-- src/rest_api/space.rs | 77 +- src/rest_api/spaces.rs | 77 +- src/rest_api/spatial_object.rs | 102 +- src/rest_api/spatial_objects.rs | 79 +- .../api/v0.2.yaml | 9 +- static/{ => errors}/400.html | 0 static/{ => errors}/404.html | 0 static/errors/405.html | 8 + 19 files changed, 731 insertions(+), 2760 deletions(-) delete mode 100755 api/swagger.yaml delete mode 100644 src/rest_api/default.rs create mode 100644 src/rest_api/helpers.rs create mode 100644 src/rest_api/helpers_dynamic_pages.rs create mode 100644 src/rest_api/helpers_static_pages.rs rename api/spatial_search-v0.2.yaml => static/api/v0.2.yaml (99%) rename static/{ => errors}/400.html (100%) rename static/{ => errors}/404.html (100%) create mode 100644 static/errors/405.html diff --git a/Cargo.toml b/Cargo.toml index 4993a81..bce9622 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,16 +18,21 @@ license = "MIT" include = ["Cargo.toml", "README.md", "LICENSE", "ACKNOWLEDGEMENTS", "src/**/*.rs"] -[dependencies] -mercator_db = "^0.1" -mercator_parser = "^0.1" +[features] +static-error-pages = [] -actix = "^0.7" -actix-web = "^0.7" +[dependencies] +actix-web = "^1.0" +actix-files = "^0.1" +actix-service = "^0.4" +actix-cors = "^0.1" measure_time = "^0.6" memmap = "^0.7" +mercator_db = "^0.1" +mercator_parser = "^0.1" + serde = "^1.0" serde_derive = "^1.0" serde_json = "^1.0" @@ -37,3 +42,8 @@ bincode = "^1.1" #log = { version = "^0.4", features = ["max_level_trace", "release_max_level_info"] } log = { version = "^0.4", features = ["max_level_trace", "release_max_level_trace"] } pretty_env_logger = "^0.3" # Logger implementation + +[dev-dependencies] +# Only for tests +actix-server-config = "^0.1" +actix-http = "^0.2" \ No newline at end of file diff --git a/api/swagger.yaml b/api/swagger.yaml deleted file mode 100755 index 76966ab..0000000 --- a/api/swagger.yaml +++ /dev/null @@ -1,2140 +0,0 @@ ---- -swagger: "2.0" -info: - description: "API Documentation for the spatial search backend." - version: "0.2.0" - title: "Spatial Search Backend API Documentation" - termsOfService: "" - contact: {} - license: - name: "The MIT License" - url: "https://opensource.org/licenses/MIT" -tags: -- name: "Actions" - description: "General Database actions." -- name: "Spaces" - description: "Operations on Reference Spaces." -- name: "Data Sets" - description: "Operations on Data Sets." -- name: "Spatial Objects" - description: "Operations on Spatial Objects." -paths: - /health: - get: - tags: - - "Actions" - summary: "Health check of the service.\n" - description: "Please note that making anything but a **GET** call is a bad request.\n" - operationId: "get_health_check" - parameters: [] - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "get_health_check" - uppercase_operation_id: "GET_HEALTH_CHECK" - path: "/health" - PATH_ID: "HEALTH" - hasPathParams: false - HttpMethod: "Get" - /queries: - post: - tags: - - "Actions" - summary: "Execute an arbitrary query.\n" - operationId: "query" - parameters: - - in: "body" - name: "query" - description: "For more about the query syntax, please refer to `FIXME: URL`\ - \ http://repo/queries.g4.\n" - required: true - schema: - type: "string" - upperCaseName: "QUERY" - refName: null - formatString: "\\\"{}\\\"" - example: "\"query_example\".to_string()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "QUERY" - consumesJson: true - responses: - 200: - description: "OK" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "QUERY" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "query" - uppercase_operation_id: "QUERY" - path: "/queries" - PATH_ID: "QUERIES" - hasPathParams: false - HttpMethod: "Post" - /spaces: - post: - tags: - - "Spaces" - summary: "Retrieve a list of space definition names.\n" - operationId: "post_spaces" - parameters: - - in: "body" - name: "filters" - description: "Filter string to use to select the data.\nFor more about the\ - \ filter syntax, please refer to `FIXME: URL` http://repo/filters.g4.\n\ - If **ids_only** is true, then a list of **unique identifiers** is returned,\ - \ instead of the whole, distinct, objects for the selected objects.\n" - required: false - schema: - $ref: "#/definitions/filters" - uppercase_data_type: "FILTERS" - refName: "filters" - formatString: "{:?}" - example: "None" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "array" - items: - type: "string" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - uppercase_data_type: "VEC" - producesJson: true - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "post_spaces" - uppercase_operation_id: "POST_SPACES" - path: "/spaces" - PATH_ID: "SPACES" - hasPathParams: false - HttpMethod: "Post" - put: - tags: - - "Spaces" - summary: "Create or update multiple spaces at a time.\n" - operationId: "put_spaces" - parameters: - - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/spaces" - uppercase_data_type: "SPACES" - refName: "spaces" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPACES" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_spaces" - uppercase_operation_id: "PUT_SPACES" - path: "/spaces" - PATH_ID: "SPACES" - hasPathParams: false - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Spaces" - summary: "Delete mulltiple spaces at a time.\nEach reference space can only\ - \ be removed if and only if there\n are no spatial objects left referencing\ - \ it.\n" - operationId: "delete_spaces" - parameters: - - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/spaces_1" - uppercase_data_type: "SPACES1" - refName: "spaces_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_SPACES" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_spaces" - uppercase_operation_id: "DELETE_SPACES" - path: "/spaces" - PATH_ID: "SPACES" - hasPathParams: false - HttpMethod: "Delete" - noClientExample: true - patch: - tags: - - "Spaces" - summary: "Update multiple spaces at a time.\n" - operationId: "patch_spaces" - parameters: - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPACE" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_spaces" - uppercase_operation_id: "PATCH_SPACES" - path: "/spaces" - PATH_ID: "SPACES" - hasPathParams: false - HttpMethod: "Patch" - /spaces/{name}: - get: - tags: - - "Spaces" - summary: "Retrieve the space `name`.\n" - operationId: "get_space" - parameters: - - name: "name" - in: "path" - description: "Name of the reference space\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPACE" - uppercase_data_type: "SPACE" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "get_space" - uppercase_operation_id: "GET_SPACE" - path: "/spaces/:name" - PATH_ID: "SPACES_NAME" - hasPathParams: true - HttpMethod: "Get" - put: - tags: - - "Spaces" - summary: "Create or update a space `name` in the database\n" - operationId: "put_space" - parameters: - - name: "name" - in: "path" - description: "Name of the reference space\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/Space" - uppercase_data_type: "SPACE" - refName: "Space" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPACE" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/Space" - current: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPACE" - uppercase_data_type: "INLINERESPONSE200" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_space" - uppercase_operation_id: "PUT_SPACE" - path: "/spaces/:name" - PATH_ID: "SPACES_NAME" - hasPathParams: true - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Spaces" - summary: "Remove the space `name`. This operation is authorized if and\n only\ - \ if there is no references to the space being removed.\n" - operationId: "delete_space" - parameters: - - name: "name" - in: "path" - description: "Name of the reference space\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPACE" - uppercase_data_type: "SPACE" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_space" - uppercase_operation_id: "DELETE_SPACE" - path: "/spaces/:name" - PATH_ID: "SPACES_NAME" - hasPathParams: true - HttpMethod: "Delete" - patch: - tags: - - "Spaces" - summary: "Update the space `name`.\n" - operationId: "patch_space" - parameters: - - name: "name" - in: "path" - description: "Name of the reference space\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPACE" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/Space" - current: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPACE" - uppercase_data_type: "INLINERESPONSE200" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_space" - uppercase_operation_id: "PATCH_SPACE" - path: "/spaces/:name" - PATH_ID: "SPACES_NAME" - hasPathParams: true - HttpMethod: "Patch" - /datasets: - post: - tags: - - "Data Sets" - summary: "Retrieve a list of data set names.\n" - operationId: "post_datasets" - parameters: - - in: "body" - name: "filters" - description: "Filter string to use to select the data.\nFor more about the\ - \ filter syntax, please refer to `FIXME: URL` http://repo/filters.g4.\n\ - If **ids_only** is true, then a list of **unique identifiers** is returned,\ - \ instead of the whole, distinct, objects for the selected objects.\n" - required: false - schema: - $ref: "#/definitions/filters" - uppercase_data_type: "FILTERS" - refName: "filters" - formatString: "{:?}" - example: "None" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "array" - items: - type: "string" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - uppercase_data_type: "VEC" - producesJson: true - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "post_datasets" - uppercase_operation_id: "POST_DATASETS" - path: "/datasets" - PATH_ID: "DATASETS" - hasPathParams: false - HttpMethod: "Post" - put: - tags: - - "Data Sets" - summary: "Create or update multiple data sets at a time.\n" - operationId: "put_datasets" - parameters: - - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/datasets" - uppercase_data_type: "DATASETS" - refName: "datasets" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_DATASETS" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_datasets" - uppercase_operation_id: "PUT_DATASETS" - path: "/datasets" - PATH_ID: "DATASETS" - hasPathParams: false - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Data Sets" - summary: "Delete mulltiple data sets at a time. This also removes all the\n\ - \ Spatial Objects tied to these datasets.\n" - operationId: "delete_datasets" - parameters: - - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/datasets_1" - uppercase_data_type: "DATASETS1" - refName: "datasets_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_DATASETS" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_datasets" - uppercase_operation_id: "DELETE_DATASETS" - path: "/datasets" - PATH_ID: "DATASETS" - hasPathParams: false - HttpMethod: "Delete" - noClientExample: true - patch: - tags: - - "Data Sets" - summary: "Update multiple data sets at a time.\n" - operationId: "patch_datasets" - parameters: - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_1" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_DATASET" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_datasets" - uppercase_operation_id: "PATCH_DATASETS" - path: "/datasets" - PATH_ID: "DATASETS" - hasPathParams: false - HttpMethod: "Patch" - /datasets/{name}: - get: - tags: - - "Data Sets" - summary: "Retrieve the data set `name` properties. This does not include\n \ - \ the SpatialObjects contained in this dataset.\n" - operationId: "get_dataset" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_DATASET" - uppercase_data_type: "DATASET" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "get_dataset" - uppercase_operation_id: "GET_DATASET" - path: "/datasets/:name" - PATH_ID: "DATASETS_NAME" - hasPathParams: true - HttpMethod: "Get" - put: - tags: - - "Data Sets" - summary: "Create or update a data set `name` in the database.\n" - operationId: "put_dataset" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/DataSet" - uppercase_data_type: "DATASET" - refName: "DataSet" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_DATASET" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/DataSet" - current: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_DATASET" - uppercase_data_type: "INLINERESPONSE2001" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_dataset" - uppercase_operation_id: "PUT_DATASET" - path: "/datasets/:name" - PATH_ID: "DATASETS_NAME" - hasPathParams: true - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Data Sets" - summary: "Remove the data set `name`. This also removes all the Spatial\n Objects\ - \ stored as part of that data set.\n" - operationId: "delete_dataset" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_DATASET" - uppercase_data_type: "DATASET" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_dataset" - uppercase_operation_id: "DELETE_DATASET" - path: "/datasets/:name" - PATH_ID: "DATASETS_NAME" - hasPathParams: true - HttpMethod: "Delete" - patch: - tags: - - "Data Sets" - summary: "Update the properties of the data set `name`.\n" - operationId: "patch_dataset" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_1" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_DATASET" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/DataSet" - current: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_DATASET" - uppercase_data_type: "INLINERESPONSE2001" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_dataset" - uppercase_operation_id: "PATCH_DATASET" - path: "/datasets/:name" - PATH_ID: "DATASETS_NAME" - hasPathParams: true - HttpMethod: "Patch" - /datasets/{name}/index: - put: - tags: - - "Data Sets" - summary: "Rebuild the index of data set `name`.\n" - operationId: "put_dataset_index" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - responses: - 200: - description: "OK" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PUT_DATASET_INDEX" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_dataset_index" - uppercase_operation_id: "PUT_DATASET_INDEX" - path: "/datasets/:name/index" - PATH_ID: "DATASETS_NAME_INDEX" - hasPathParams: true - HttpMethod: "Put" - /datasets/{name}/spatial_objects: - post: - tags: - - "Spatial Objects" - summary: "Retrieve a list of spatial object.\n" - operationId: "post_spatial_objects" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "filters" - description: "Filter string to use to select the data.\nFor more about the\ - \ filter syntax, please refer to `FIXME: URL` http://repo/filters.g4.\n\ - If **ids_only** is true, then a list of **unique identifiers** is returned,\ - \ instead of the whole, distinct, objects for the selected objects.\n" - required: false - schema: - $ref: "#/definitions/filters" - uppercase_data_type: "FILTERS" - refName: "filters" - formatString: "{:?}" - example: "None" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "array" - items: - type: "string" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - uppercase_data_type: "VEC" - producesJson: true - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "post_spatial_objects" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - path: "/datasets/:name/spatial_objects" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS" - hasPathParams: true - HttpMethod: "Post" - put: - tags: - - "Spatial Objects" - summary: "Create or update multiple spatial objects at a time.\n" - operationId: "put_spatial_objects" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/spatial_objects" - uppercase_data_type: "SPATIALOBJECTS" - refName: "spatial_objects" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPATIAL_OBJECTS" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_spatial_objects" - uppercase_operation_id: "PUT_SPATIAL_OBJECTS" - path: "/datasets/:name/spatial_objects" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS" - hasPathParams: true - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Spatial Objects" - summary: "Delete mulltiple spatial objects at a time.\n" - operationId: "delete_spatial_objects" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/spatial_objects_1" - uppercase_data_type: "SPATIALOBJECTS1" - refName: "spatial_objects_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_SPATIAL_OBJECTS" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_spatial_objects" - uppercase_operation_id: "DELETE_SPATIAL_OBJECTS" - path: "/datasets/:name/spatial_objects" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS" - hasPathParams: true - HttpMethod: "Delete" - noClientExample: true - patch: - tags: - - "Spatial Objects" - summary: "Update multiple spatial objects at a time.\n" - operationId: "patch_spatial_objects" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_2" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - consumesJson: true - responses: - 200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_spatial_objects" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" - path: "/datasets/:name/spatial_objects" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS" - hasPathParams: true - HttpMethod: "Patch" - /datasets/{name}/spatial_objects/{id}: - get: - tags: - - "Spatial Objects" - summary: "Retrieve the spatial object `id` of the data set `name`.\n" - operationId: "get_spatial_object" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - name: "id" - in: "path" - description: "Id of the spatial object\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"id_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPATIAL_OBJECT" - uppercase_data_type: "SPATIALOBJECT" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "get_spatial_object" - uppercase_operation_id: "GET_SPATIAL_OBJECT" - path: "/datasets/:name/spatial_objects/:id" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS_ID" - hasPathParams: true - HttpMethod: "Get" - put: - tags: - - "Spatial Objects" - summary: "Create or update a spatial object `id` in the data set `name`.\n" - operationId: "put_spatial_object" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - name: "id" - in: "path" - description: "Id of the spatial object\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"id_example\".to_string()" - - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/SpatialObject" - uppercase_data_type: "SPATIALOBJECT" - refName: "SpatialObject" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPATIAL_OBJECT" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/SpatialObject" - current: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - uppercase_data_type: "INLINERESPONSE2002" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "put_spatial_object" - uppercase_operation_id: "PUT_SPATIAL_OBJECT" - path: "/datasets/:name/spatial_objects/:id" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS_ID" - hasPathParams: true - HttpMethod: "Put" - noClientExample: true - delete: - tags: - - "Spatial Objects" - summary: "Remove the spatial object `id` of the data set `name`.\n" - operationId: "delete_spatial_object" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - name: "id" - in: "path" - description: "Id of the spatial object\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"id_example\".to_string()" - responses: - 200: - description: "OK" - schema: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPATIAL_OBJECT" - uppercase_data_type: "SPATIALOBJECT" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "delete_spatial_object" - uppercase_operation_id: "DELETE_SPATIAL_OBJECT" - path: "/datasets/:name/spatial_objects/:id" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS_ID" - hasPathParams: true - HttpMethod: "Delete" - patch: - tags: - - "Spatial Objects" - summary: "Update the spatial object `id` of the data set `name`.\n" - operationId: "patch_spatial_object" - parameters: - - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - - name: "id" - in: "path" - description: "Id of the spatial object\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"id_example\".to_string()" - - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_2" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - consumesJson: true - responses: - 200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/SpatialObject" - current: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - uppercase_data_type: "INLINERESPONSE2002" - producesJson: true - 404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - 422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - default: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - operation_id: "patch_spatial_object" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - path: "/datasets/:name/spatial_objects/:id" - PATH_ID: "DATASETS_NAME_SPATIAL_OBJECTS_ID" - hasPathParams: true - HttpMethod: "Patch" -definitions: - Space: - type: "object" - properties: - name: - type: "string" - description: "Unique Id for the space, which can also be used to generate\ - \ a\n link to the user documentation describing the space,\n explaining\ - \ the semantic meaning of the values stored, as\n well as the definitions\ - \ of the axes.\n" - axes: - type: "array" - description: "The order of the axes matter and MUST be kept, as this is\n\ - \ also linked to the definition found in the documentation.\n\nCoordinate\ - \ of a point MUST always be expressed using the\n same order as defined\ - \ here.\n" - items: - $ref: "#/definitions/Axis" - title: "Reference Space" - description: "Definition of a space, in which objects are described.\n" - example: - name: "name" - axes: - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - upperCaseName: "SPACE" - SpatialObject: - type: "object" - properties: - shape: - type: "array" - description: "List of shapes, overlapping or not, which define the whole space\ - \ covered by this spatial object.\n" - items: - $ref: "#/definitions/Shape" - properties: - $ref: "#/definitions/Properties" - title: "Spatial Object" - description: "Collection of positions in a space, which share a common set of\n\ - \ properties.\n" - example: - shape: - - vertices: - - "" - - "" - type: "Point" - space: "space" - - vertices: - - "" - - "" - type: "Point" - space: "space" - properties: - id: "id" - type: "type" - upperCaseName: "SPATIALOBJECT" - DataSet: - type: "object" - properties: - name: - type: "string" - version: - type: "string" - scales: - type: "array" - description: "Scale factors used to generate less precise, coarser indexes\n\ - \ in order to speed up queries over large volumes of the\n space.\n\n\ - Values are expressed as powers of two, in the range [0;n].\n For each scale,\ - \ a whole vector providing values for each\n axis MUST be provided.\n\n\ - Values, which are equal, and whose coordinates gets merged\n are merged\ - \ as well, to reduce the number of results.\n\nDistinct values whose coordinates\ - \ are merged are recorded,\n thus allowing the user to move from one scale\ - \ factor to\n another, with a finer resolution smoothly.\n" - title: "Scale Vectors" - items: - type: "array" - items: - type: "number" - format: "int32" - minimum: 0 - title: "Data Set" - description: "Collection of Spatial Objects, stored in one or more Reference\n\ - \ Spaces.\n" - example: - scales: - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - name: "name" - version: "version" - upperCaseName: "DATASET" - Point: - title: "Multi-Dimensional point" - type: "array" - description: "One valid value for each axes of the reference space this point\n\ - \ is used in.\n" - items: - type: "number" - upperCaseName: "POINT" - Axis: - type: "object" - properties: - measurement_unit: - type: "string" - description: "Unit of the values, on this axis, for example [mm], [s],\n \ - \ [um].\n" - title: "Unit used on this axis" - coordinates: - $ref: "#/definitions/Valid numbers on this axis" - origin: - type: "array" - items: - $ref: "#/definitions/Point" - unit_vector: - type: "array" - items: - $ref: "#/definitions/Point" - title: "Coordinate Axis" - description: "Defines the properties of an axis. The origin and unit vectors\n\ - \ or defined within the universe space, but this does NOT imply\n a linear\ - \ conversion is possible, this only provide anchoring\n of the axis as well\ - \ as its absolute direction.\n" - example: - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - upperCaseName: "AXIS" - Shape: - type: "object" - properties: - type: - type: "string" - description: "Name of the shape class described by the vertices, this can\n\ - \ be used for specific types to reduce the number of\n vertices required\ - \ to define the shape.\n" - enum: - - "Point" - - "Hyperrectangle" - vertices: - type: "array" - description: "List of vertices composing the contour of the shape.\n" - items: - $ref: "#/definitions/Point" - space: - type: "string" - description: "Name of a valid reference space. This is the space in which\n\ - \ the vertices are defined\n" - title: "Geometric shape" - description: "Geometric shape defined in a reference space.\n" - example: - vertices: - - "" - - "" - type: "Point" - space: "space" - upperCaseName: "SHAPE" - Properties: - type: "object" - properties: - id: - type: "string" - type: - type: "string" - description: "Label defining the kind of the spatial object.\n" - description: "Properties tied to a shape, in other words properties valid for\n\ - \ the whole content of the shape.\n" - example: - id: "id" - type: "type" - upperCaseName: "PROPERTIES" - spaces: - type: "object" - properties: - list: - type: "array" - items: - $ref: "#/definitions/Space" - upperCaseName: "SPACES" - filters: - type: "object" - properties: - filter: - type: "string" - ids_only: - type: "boolean" - default: false - upperCaseName: "FILTERS" - spaces_1: - type: "object" - properties: - list: - type: "array" - items: - type: "string" - upperCaseName: "SPACES_1" - partial_update: - properties: - name: - type: "string" - description: "Identifier or name of the instance to update.\n" - attribute: - type: "string" - description: "Valid selector / attribute name of the instance.\n" - enum: - - "name" - - "axes" - value: - type: "string" - description: "JSON-serialized value to use to replace the value of the selected\ - \ attribute.\n" - upperCaseName: "PARTIAL_UPDATE" - inline_response_200: - properties: - previous: - $ref: "#/definitions/Space" - current: - $ref: "#/definitions/Space" - example: - current: - name: "name" - axes: - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - previous: - name: "name" - axes: - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - - unit_vector: - - "" - - "" - origin: - - "" - - "" - coordinates: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - measurement_unit: "measurement_unit" - upperCaseName: "INLINE_RESPONSE_200" - datasets: - type: "object" - properties: - list: - type: "array" - items: - $ref: "#/definitions/DataSet" - upperCaseName: "DATASETS" - datasets_1: - type: "object" - properties: - list: - type: "array" - items: - type: "string" - upperCaseName: "DATASETS_1" - partial_update_1: - properties: - name: - type: "string" - description: "Identifier or name of the instance to update.\n" - attribute: - type: "string" - description: "Valid selector / attribute name of the instance.\n" - enum: - - "name" - - "version" - - "scales" - value: - type: "string" - description: "JSON-serialized value to use to replace the value of the selected\ - \ attribute.\n" - upperCaseName: "PARTIAL_UPDATE_1" - inline_response_200_1: - properties: - previous: - $ref: "#/definitions/DataSet" - current: - $ref: "#/definitions/DataSet" - example: - current: - scales: - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - name: "name" - version: "version" - previous: - scales: - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - - - 0.080082819046101150206595775671303272247314453125 - - 0.080082819046101150206595775671303272247314453125 - name: "name" - version: "version" - upperCaseName: "INLINE_RESPONSE_200_1" - spatial_objects: - type: "object" - properties: - list: - type: "array" - items: - $ref: "#/definitions/SpatialObject" - upperCaseName: "SPATIAL_OBJECTS" - spatial_objects_1: - type: "object" - properties: - list: - type: "array" - items: - type: "string" - upperCaseName: "SPATIAL_OBJECTS_1" - partial_update_2: - properties: - id: - type: "string" - description: "Identifier or name of the instance to update.\n" - attribute: - type: "string" - description: "Valid selector / attribute name of the instance.\n" - enum: - - "shape" - - "shape.type" - - "shape.vertices" - - "shape.space" - - "properties" - - "properties.id" - - "properties.type" - value: - type: "string" - description: "JSON-serialized value to use to replace the value of the selected\ - \ attribute.\n" - upperCaseName: "PARTIAL_UPDATE_2" - inline_response_200_2: - properties: - previous: - $ref: "#/definitions/SpatialObject" - current: - $ref: "#/definitions/SpatialObject" - example: - current: - shape: - - vertices: - - "" - - "" - type: "Point" - space: "space" - - vertices: - - "" - - "" - type: "Point" - space: "space" - properties: - id: "id" - type: "type" - previous: - shape: - - vertices: - - "" - - "" - type: "Point" - space: "space" - - vertices: - - "" - - "" - type: "Point" - space: "space" - properties: - id: "id" - type: "type" - upperCaseName: "INLINE_RESPONSE_200_2" - Valid numbers on this axis: - properties: - set: - type: "string" - description: "Valid numbers as defined by the usual mathematical sets,\n for\ - \ example N=Natural, Z=Integers, Q=Rational, R=Real.\n" - enum: - - "N" - - "Z" - - "Q" - - "R" - minimum: - type: "number" - format: "float" - maximum: - type: "number" - format: "float" - steps: - type: "number" - format: "integer" - description: "Definition of the valid coordinate values which can be used\n on\ - \ this axis.\n" - example: - set: "N" - maximum: 6.0274563 - minimum: 0.8008282 - steps: 1.46581298050294517310021547018550336360931396484375 - upperCaseName: "VALID NUMBERS ON THIS AXIS" -parameters: - Space: - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/Space" - uppercase_data_type: "SPACE" - refName: "Space" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPACE" - consumesJson: true - Spaces: - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/spaces" - uppercase_data_type: "SPACES" - refName: "spaces" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPACES" - consumesJson: true - SpaceName: - name: "name" - in: "path" - description: "Name of the reference space\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - SpaceNames: - in: "body" - name: "spaces" - required: true - schema: - $ref: "#/definitions/spaces_1" - uppercase_data_type: "SPACES1" - refName: "spaces_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_SPACES" - consumesJson: true - SpacePartial: - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPACE" - consumesJson: true - DataSet: - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/DataSet" - uppercase_data_type: "DATASET" - refName: "DataSet" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_DATASET" - consumesJson: true - DataSets: - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/datasets" - uppercase_data_type: "DATASETS" - refName: "datasets" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_DATASETS" - consumesJson: true - DataSetName: - name: "name" - in: "path" - description: "Name of the data set\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"name_example\".to_string()" - DataSetNames: - in: "body" - name: "datasets" - required: true - schema: - $ref: "#/definitions/datasets_1" - uppercase_data_type: "DATASETS1" - refName: "datasets_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_DATASETS" - consumesJson: true - DataSetPartial: - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_1" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_DATASET" - consumesJson: true - SpatialObject: - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/SpatialObject" - uppercase_data_type: "SPATIALOBJECT" - refName: "SpatialObject" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPATIAL_OBJECT" - consumesJson: true - SpatialObjects: - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/spatial_objects" - uppercase_data_type: "SPATIALOBJECTS" - refName: "spatial_objects" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PUT_SPATIAL_OBJECTS" - consumesJson: true - SpatialObjectId: - name: "id" - in: "path" - description: "Id of the spatial object\n" - required: true - type: "string" - formatString: "\\\"{}\\\"" - example: "\"id_example\".to_string()" - SpatialObjectIds: - in: "body" - name: "spatial_objects" - required: true - schema: - $ref: "#/definitions/spatial_objects_1" - uppercase_data_type: "SPATIALOBJECTS1" - refName: "spatial_objects_1" - formatString: "{:?}" - example: "???" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "DELETE_SPATIAL_OBJECTS" - consumesJson: true - SpatialObjectPartial: - in: "body" - name: "partial_update" - required: true - schema: - type: "array" - items: - $ref: "#/definitions/partial_update_2" - formatString: "{:?}" - example: "&Vec::new()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - consumesJson: true - Filters: - in: "body" - name: "filters" - description: "Filter string to use to select the data.\nFor more about the filter\ - \ syntax, please refer to `FIXME: URL` http://repo/filters.g4.\nIf **ids_only**\ - \ is true, then a list of **unique identifiers** is returned, instead of the\ - \ whole, distinct, objects for the selected objects.\n" - required: false - schema: - $ref: "#/definitions/filters" - uppercase_data_type: "FILTERS" - refName: "filters" - formatString: "{:?}" - example: "None" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - consumesJson: true - Query: - in: "body" - name: "query" - description: "For more about the query syntax, please refer to `FIXME: URL` http://repo/queries.g4.\n" - required: true - schema: - type: "string" - upperCaseName: "QUERY" - refName: null - formatString: "\\\"{}\\\"" - example: "\"query_example\".to_string()" - model_key: "Valid numbers on this axis" - uppercase_operation_id: "QUERY" - consumesJson: true -responses: - Space200: - description: "OK" - schema: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPACE" - uppercase_data_type: "SPACE" - producesJson: true - SpaceUpdated200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/Space" - current: - $ref: "#/definitions/Space" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPACE" - uppercase_data_type: "INLINERESPONSE200" - producesJson: true - SpatialObject200: - description: "OK" - schema: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_SPATIAL_OBJECT" - uppercase_data_type: "SPATIALOBJECT" - producesJson: true - SpatialObjectUpdated200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/SpatialObject" - current: - $ref: "#/definitions/SpatialObject" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - uppercase_data_type: "INLINERESPONSE2002" - producesJson: true - DataSet200: - description: "OK" - schema: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "DELETE_DATASET" - uppercase_data_type: "DATASET" - producesJson: true - DataSetUpdated200: - description: "OK" - schema: - type: "object" - properties: - previous: - $ref: "#/definitions/DataSet" - current: - $ref: "#/definitions/DataSet" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "PATCH_DATASET" - uppercase_data_type: "INLINERESPONSE2001" - producesJson: true - Query200: - description: "OK" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "QUERY" - ArrayOfStrings: - description: "OK" - schema: - type: "array" - items: - type: "string" - x-responseId: "OK" - x-uppercaseResponseId: "OK" - uppercase_operation_id: "POST_SPATIAL_OBJECTS" - uppercase_data_type: "VEC" - producesJson: true - Standard422: - description: "Unprocessable Entity\n" - x-responseId: "UnprocessableEntity_" - x-uppercaseResponseId: "UNPROCESSABLE_ENTITY_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - Standard404: - description: "Object not found\n" - x-responseId: "ObjectNotFound_" - x-uppercaseResponseId: "OBJECT_NOT_FOUND_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - Standard400: - description: "Invalid or malformed request\n" - x-responseId: "InvalidOrMalformedRequest_" - x-uppercaseResponseId: "INVALID_OR_MALFORMED_REQUEST_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECT" - Standard200: - description: "OK\n" - x-responseId: "OK_" - x-uppercaseResponseId: "OK_" - uppercase_operation_id: "PATCH_SPATIAL_OBJECTS" diff --git a/src/main.rs b/src/main.rs index 9b14740..f86e0f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,10 @@ mod rest_api; mod shared_state; use std::process::exit; -use std::sync::Arc; use std::sync::RwLock; +use actix_web::web::Data; use mercator_db::json::model; -use mercator_db::json::storage; use mercator_db::DataBase; use shared_state::SharedState; @@ -54,8 +53,6 @@ fn main() { let hostname; let port; - let base; - let allowed_origins: Vec; //let data; match std::env::var("MERCATOR_HOST") { @@ -80,27 +77,6 @@ fn main() { } }; - match std::env::var("MERCATOR_BASE") { - Ok(val) => base = val, - Err(val) => { - error!("Could not fetch {} : `{}`", "MERCATOR_BASE", val); - exit(1); - } - }; - - match std::env::var("MERCATOR_ALLOWED_ORIGINS") { - Ok(val) => { - allowed_origins = val - .split(',') - .map(|s| s.trim().to_string()) - .collect::>() - } - Err(val) => { - error!("Could not fetch {} : `{}`", "MERCATOR_ALLOWED_ORIGINS", val); - exit(1); - } - }; - /* UNUSED FOR NOW match std::env::var("MERCATOR_DATA") { Ok(val) => data = val, @@ -134,13 +110,10 @@ fn main() { } // END of Temporary bloc } - let state = SharedState::new(db); rest_api::run( - hostname, + &hostname, port, - base, - allowed_origins, - Arc::new(RwLock::new(state)), + Data::new(RwLock::new(SharedState::new(db))), ); } diff --git a/src/rest_api/actions.rs b/src/rest_api/actions.rs index 5556276..2b67f11 100644 --- a/src/rest_api/actions.rs +++ b/src/rest_api/actions.rs @@ -1,47 +1,86 @@ -use actix_web::HttpRequest; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Json; use actix_web::HttpResponse; -use actix_web::Json; -use super::error_400; -use super::AppState; -use super::Filters; -use super::StringOrStaticFileResult; +use crate::shared_state::SharedState; -pub fn health(_req: &HttpRequest) -> HttpResponse { +use super::error_422; +use super::ok_200; +use super::HandlerResult; + +#[derive(Debug, Deserialize)] +pub struct Query { + query: String, +} + +// Also used for the root service. +pub fn health() -> HttpResponse { HttpResponse::Ok().finish() } -pub fn query( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn query((parameters, state): (Json, Data>)) -> HandlerResult { trace!("query Triggered!"); - error_400() + let context = state.read().unwrap(); + let query = ¶meters.query; + + if query.is_empty() { + error_422(format!("Invalid query in '{:?}'", query)) + } else { + // FIXME: MANAGE PROJECTIONS + let results = context + .db() + .core_keys() + .iter() + // FIXME: Specify from json output space + threshold volume + .flat_map(|core| match context.query(query, core, None, None) { + Err(_) => vec![], // FIXME: Return errors here instead!! + Ok(r) => { + let mut r = r.into_iter().map(|o| o.space_id).collect::>(); + r.sort_unstable(); + r.dedup(); + r + } + }) + .collect::>(); + + ok_200(&results) + } +} + +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service(web::resource("/health").route(web::get().to(health))); + cfg.service(web::resource("/query").route(web::post().to(query))); } #[cfg(test)] -mod tests { - use super::super::tests::*; +mod routing { + use super::super::tests_utils::*; #[test] fn health() { - let ep = get_path("/health".into()); - expect_200(http::Method::GET, ep.clone()); + let ep = &get_path("/health"); - expect_400(http::Method::POST, ep.clone()); - expect_400(http::Method::PUT, ep.clone()); - expect_400(http::Method::PATCH, ep.clone()); - expect_400(http::Method::DELETE, ep.clone()); + expect_200(Method::GET, ep); + + expect_405(Method::POST, ep); + expect_405(Method::PUT, ep); + expect_405(Method::PATCH, ep); + expect_405(Method::DELETE, ep); } #[test] fn query() { - let ep = get_path("/query".into()); - expect_200(http::Method::POST, ep.clone()); - expect_422(http::Method::POST, ep.clone()); + let ep = &get_path("/query"); - expect_400(http::Method::GET, ep.clone()); - expect_400(http::Method::PUT, ep.clone()); - expect_400(http::Method::PATCH, ep.clone()); - expect_400(http::Method::DELETE, ep.clone()); + expect_200(Method::POST, ep); + expect_422(Method::POST, ep); + + expect_405(Method::GET, ep); + expect_405(Method::PUT, ep); + expect_405(Method::PATCH, ep); + expect_405(Method::DELETE, ep); } } diff --git a/src/rest_api/core.rs b/src/rest_api/core.rs index a47ecec..9dde030 100644 --- a/src/rest_api/core.rs +++ b/src/rest_api/core.rs @@ -1,12 +1,15 @@ -use actix_web::HttpRequest; -use actix_web::Json; -use actix_web::Path; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Path; + +use crate::shared_state::SharedState; use super::error_400; use super::error_404; use super::ok_200; -use super::AppState; -use super::StringOrStaticFileResult; +use super::HandlerResult; #[derive(Clone, Deserialize, Serialize)] pub struct Core { @@ -15,92 +18,86 @@ pub struct Core { scales: Vec>, } -pub fn put( - (_path, _core, _state): (Path, Json, HttpRequest), -) -> StringOrStaticFileResult { - trace!("PUT Triggered!"); +fn put(path: Path) -> HandlerResult { + trace!("PUT Triggered on {}", path); error_400() } -pub fn get((core, state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn get((core, state): (Path, Data>)) -> HandlerResult { trace!("GET Triggered!"); let core = core.to_string(); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); match context.db().core(core) { Ok(core) => ok_200(&Core { name: core.name().clone(), version: core.version().clone(), scales: vec![vec![0, 0, 0]], - //FIXME: Report the actual values. Might need to change the format - // to per reference space. + // FIXME: Report the actual values. Might need to change the format + // to per reference space. }), Err(_) => error_404(), } } -pub fn patch( - (_path, _core, _state): (Path, Json, HttpRequest), -) -> StringOrStaticFileResult { - trace!("PATCH Triggered!"); +fn patch(path: Path) -> HandlerResult { + trace!("PATCH Triggered on {}", path); error_400() } -pub fn delete((_path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { - trace!("DELETE Triggered!"); +fn delete(path: Path) -> HandlerResult { + trace!("DELETE Triggered on {}", path); error_400() } +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/cores/{name}") + .route(web::get().to(get)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} + #[cfg(test)] -mod tests { - use super::super::tests::*; +mod routing { + use super::super::tests_utils::*; - const INSTANCE_EXISTS: &str = "/cores/42"; - const INSTANCE_INVALID: &str = "/cores/21"; + const INSTANCE_EXISTS: &str = CORE; + const INSTANCE_INVALID: &str = "/41-doesnotexists"; // FIXME: Add Body to request to see difference between (in)valid bodied requests #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_200( - http::Method::PUT, - get_path(INSTANCE_INVALID), - "".to_string(), - ); + json::expect_200(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string()); + json::expect_200(Method::PUT, &get_core(INSTANCE_INVALID), "".to_string()); } #[test] fn patch() { - json::expect_200( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - json::expect_422( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID)); + json::expect_200(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string()); + expect_404(Method::PATCH, &get_core(INSTANCE_INVALID)); } #[test] fn get() { - expect_200(http::Method::GET, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::GET, get_path(INSTANCE_INVALID)); + expect_200(Method::GET, &get_core(INSTANCE_EXISTS)); + expect_404(Method::GET, &get_core(INSTANCE_INVALID)); } #[test] fn delete() { - expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID)); + expect_200(Method::DELETE, &get_core(INSTANCE_EXISTS)); + expect_404(Method::DELETE, &get_core(INSTANCE_INVALID)); } #[test] fn post() { - expect_400(http::Method::POST, get_path(INSTANCE_EXISTS)); - expect_400(http::Method::POST, get_path(INSTANCE_INVALID)); + expect_405(Method::POST, &get_core(INSTANCE_EXISTS)); + expect_405(Method::POST, &get_core(INSTANCE_INVALID)); } } diff --git a/src/rest_api/cores.rs b/src/rest_api/cores.rs index d269f19..cd6f9ce 100644 --- a/src/rest_api/cores.rs +++ b/src/rest_api/cores.rs @@ -1,17 +1,19 @@ -use actix_web::HttpRequest; -use actix_web::Json; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Json; + +use crate::shared_state::SharedState; use super::error_400; use super::ok_200; -use super::AppState; use super::Filters; -use super::StringOrStaticFileResult; +use super::HandlerResult; -pub fn post( - (parameters, state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn post((parameters, state): (Option>, Data>)) -> HandlerResult { trace!("POST Triggered!"); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); let parameters = Filters::get(parameters); let mut results = match parameters.filters { @@ -20,8 +22,9 @@ pub fn post( .db() .core_keys() .iter() + //FIXME: Specify from json output space + threshold volume .filter_map(|core| match context.filter(&filter, core, None, None) { - Err(_) => None, //FIXME: Return errors here instead!! + Err(_) => None, // FIXME: Return errors here instead!! Ok(_) => Some(core.to_string()), }) .collect(), @@ -33,30 +36,34 @@ pub fn post( ok_200(&results) } -pub fn put( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn put() -> HandlerResult { trace!("PUT Triggered!"); error_400() } -pub fn patch( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn patch() -> HandlerResult { trace!("PATCH Triggered!"); error_400() } -pub fn delete( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn delete() -> HandlerResult { trace!("DELETE Triggered!"); error_400() } +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/cores") + .route(web::post().to(post)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} + #[cfg(test)] -mod tests { - use super::super::tests::*; +mod routing { + use super::super::tests_utils::*; const COLLECTION: &str = "/cores"; @@ -64,43 +71,43 @@ mod tests { #[test] fn post() { - expect_200(http::Method::POST, get_path(COLLECTION)); - json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string()); + expect_200(Method::POST, &get_core("")); + json::expect_200(Method::POST, &get_core(""), "".to_string()); - json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::POST, &get_core(""), "".to_string()); - expect_400(http::Method::POST, get_path(COLLECTION)); + expect_400(Method::POST, &get_core("")); } #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PUT, &get_core(""), "".to_string()); - json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PUT, &get_core(""), "".to_string()); - expect_400(http::Method::PUT, get_path(COLLECTION)); + expect_400(Method::PUT, &get_core("")); } #[test] fn patch() { - json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PATCH, &get_core(""), "".to_string()); - json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PATCH, &get_core(""), "".to_string()); - expect_400(http::Method::PATCH, get_path(COLLECTION)); + expect_400(Method::PATCH, &get_core("")); } #[test] fn delete() { - json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::DELETE, &get_core(""), "".to_string()); - json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::DELETE, &get_core(""), "".to_string()); - expect_400(http::Method::DELETE, get_path(COLLECTION)); + expect_400(Method::DELETE, &get_core("")); } #[test] fn get() { - expect_400(http::Method::GET, get_path(COLLECTION)); + expect_405(Method::GET, &get_core("")); } } diff --git a/src/rest_api/default.rs b/src/rest_api/default.rs deleted file mode 100644 index 6b3bc52..0000000 --- a/src/rest_api/default.rs +++ /dev/null @@ -1,101 +0,0 @@ -use super::AppState; - -use actix_web::fs; -use actix_web::http::StatusCode; -use actix_web::HttpRequest; -use actix_web::Path; -use actix_web::Result; - -pub fn page_400(_req: &HttpRequest) -> Result { - trace!("400 Triggered!"); - Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST)) -} - -pub fn page_400_no_state(_req: &HttpRequest) -> Result { - trace!("400 Triggered!"); - Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST)) -} -pub fn page_404(_req: &HttpRequest) -> Result { - trace!("404 Triggered!"); - Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND)) -} - -pub fn static_file((path, _req): (Path, HttpRequest)) -> Result { - trace!("static/{} Triggered!", path); - - match fs::NamedFile::open(format!("static/{}", path).as_str()) { - Ok(o) => Ok(o), - Err(_) => { - Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND)) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use std::sync::Arc; - use std::sync::RwLock; - - use actix_web::http; - use actix_web::test::TestRequest; - - #[test] - fn page_400() { - let response = TestRequest::with_state(AppState { - shared: Arc::new(RwLock::new(0)), - }) - .run(&super::page_400) - .unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - } - - #[test] - fn page_400_no_state() { - let response = TestRequest::default() - .run(&super::page_400_no_state) - .unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - } - #[test] - fn page_404() { - let response = TestRequest::default().run(&super::page_404).unwrap(); - assert_eq!(response.status(), http::StatusCode::NOT_FOUND); - } -} - -#[cfg(test)] -mod routing { - use super::super::tests::*; - - #[test] - fn default_no_path() { - expect_404(http::Method::GET, "".into()); - } - - #[test] - fn default_slash() { - expect_404(http::Method::GET, "/".into()); - expect_404(http::Method::GET, "//".into()); - expect_404(http::Method::GET, "/ /".into()); - expect_404(http::Method::GET, "/ //".into()); - expect_404(http::Method::GET, "// ".into()); - } - - #[test] - fn default_invalid_prefix() { - expect_404(http::Method::GET, "/test".into()); - expect_404(http::Method::GET, format!("{}test", PREFIX)); - } - - #[test] - fn default_prefix_no_slash() { - expect_400(http::Method::GET, PREFIX.into()); - } - - #[test] - fn default_prefix_final_slash() { - expect_400(http::Method::GET, format!("{}/", PREFIX)); - } -} diff --git a/src/rest_api/helpers.rs b/src/rest_api/helpers.rs new file mode 100644 index 0000000..a8efcef --- /dev/null +++ b/src/rest_api/helpers.rs @@ -0,0 +1,88 @@ +use std::fmt::Debug; +use std::io::Error; +use std::io::ErrorKind; + +use actix_files::NamedFile; +use actix_web::Either; +use actix_web::HttpResponse; +use serde::Serialize; + +use super::HandlerResult; +use super::*; + +pub fn ok_200(data: &T) -> HandlerResult +where + T: Serialize, +{ + match serde_json::to_string(data) { + Ok(response) => Ok(Either::A(HttpResponse::Ok().body(response))), + Err(e) => error_500(e), + } +} + +pub fn error_422(reason: S) -> HandlerResult +where + S: Debug, +{ + Ok(Either::A(HttpResponse::UnprocessableEntity().body( + format!("422 - Unprocessable Entity:\n{:?}", reason), + ))) +} + +pub fn error_500(reason: S) -> HandlerResult +where + S: Debug, +{ + Err(Error::new( + ErrorKind::Other, + format!("500 - Internal Server Error: {:?}", reason), + )) +} + +//pub fn page_400() -> HandlerResult { +// trace!("400 Triggered!"); +// error_400() +//} + +pub fn page_404() -> HandlerResult { + trace!("404 Triggered!"); + error_404() +} + +//pub fn page_405() -> HandlerResult { +// trace!("405 Triggered!"); +// error_405() +//} + +pub fn api(path: Path) -> Result { + trace!("api/{} Triggered!", path); + + match NamedFile::open(format!("static/api/{}", path).as_str()) { + Ok(o) => Ok(o), + Err(_) => { + Ok(NamedFile::open("static/errors/404.html")?.set_status_code(StatusCode::NOT_FOUND)) + } + } +} + +pub fn static_file(path: Path) -> Result { + trace!("static/{} Triggered!", path); + + match NamedFile::open(format!("static/{}", path).as_str()) { + Ok(o) => Ok(o), + Err(_) => { + Ok(NamedFile::open("static/errors/404.html")?.set_status_code(StatusCode::NOT_FOUND)) + } + } +} + +#[cfg(test)] +mod tests { + use super::super::tests_utils::*; + use super::*; + + #[test] + fn page_400() { + // expect_400(Method::PATCH, get_core(INSTANCE_INVALID)); + } +} diff --git a/src/rest_api/helpers_dynamic_pages.rs b/src/rest_api/helpers_dynamic_pages.rs new file mode 100644 index 0000000..c30ac45 --- /dev/null +++ b/src/rest_api/helpers_dynamic_pages.rs @@ -0,0 +1,23 @@ +#![cfg(not(feature = "static-error-pages"))] + +use actix_web::http::StatusCode; +use actix_web::Either; +use actix_web::HttpResponse; + +use super::HandlerResult; + +fn error(code: StatusCode) -> HandlerResult { + Ok(Either::A(HttpResponse::build(code).finish())) +} + +pub fn error_400() -> HandlerResult { + error(StatusCode::BAD_REQUEST) +} + +pub fn error_404() -> HandlerResult { + error(StatusCode::NOT_FOUND) +} + +//pub fn error_405() -> HandlerResult { +// error(StatusCode::METHOD_NOT_ALLOWED) +//} diff --git a/src/rest_api/helpers_static_pages.rs b/src/rest_api/helpers_static_pages.rs new file mode 100644 index 0000000..55bbf54 --- /dev/null +++ b/src/rest_api/helpers_static_pages.rs @@ -0,0 +1,25 @@ +#![cfg(feature = "static-error-pages")] + +use actix_files::NamedFile; +use actix_web::http::StatusCode; +use actix_web::Either; + +use super::HandlerResult; + +fn error(code: StatusCode) -> HandlerResult { + let path = format!("static/errors/{}.html", u16::from(code)); + + Ok(Either::B(NamedFile::open(path)?.set_status_code(code))) +} + +pub fn error_400() -> HandlerResult { + error(StatusCode::BAD_REQUEST) +} + +pub fn error_404() -> HandlerResult { + error(StatusCode::NOT_FOUND) +} + +//pub fn error_405() -> HandlerResult { +// error(StatusCode::METHOD_NOT_ALLOWED) +//} diff --git a/src/rest_api/mod.rs b/src/rest_api/mod.rs index c480f62..a749165 100644 --- a/src/rest_api/mod.rs +++ b/src/rest_api/mod.rs @@ -9,37 +9,46 @@ mod cores; mod spatial_object; mod spatial_objects; -mod default; +mod helpers; +mod helpers_dynamic_pages; +mod helpers_static_pages; -use std::sync::Arc; +use std::io::Error; +use std::process::exit; use std::sync::RwLock; -use actix_web::fs; +use actix_cors::Cors; +use actix_files::NamedFile; use actix_web::http; -use actix_web::http::Method; use actix_web::http::StatusCode; use actix_web::middleware; -use actix_web::middleware::cors::Cors; -use actix_web::pred; -use actix_web::server; -use actix_web::server::HttpHandler; -use actix_web::server::HttpHandlerTask; +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Json; +use actix_web::web::Path; use actix_web::App; use actix_web::Either; -use actix_web::Json; -use serde::Serialize; +use actix_web::HttpResponse; +use actix_web::HttpServer; + +#[cfg(feature = "static-error-pages")] +pub use helpers_static_pages::*; + +#[cfg(not(feature = "static-error-pages"))] +pub use helpers_dynamic_pages::*; + +pub use helpers::*; use crate::SharedState; -// Application shared state -pub struct AppState { - shared: Arc>, -} +pub type HandlerResult = Result, Error>; #[derive(Debug, Deserialize)] pub struct Filters { filters: Option, ids_only: Option, + // resolution: Option>, // None means automatic selection, based on ViewPort + // view_port: Option, } impl Filters { @@ -50,40 +59,13 @@ impl Filters { None => Filters { filters: None, ids_only: Some(true), + //resolution: None, }, Some(p) => p.0, } } } -type StringOrStaticFileResult = Either; - -pub fn ok_200(data: &T) -> StringOrStaticFileResult -where - T: Serialize, -{ - Either::A( - serde_json::to_string_pretty(data) - .unwrap_or_else(|e| format!("Internal Error 500: {:?}", e)), - ) -} - -pub fn error_400() -> StringOrStaticFileResult { - Either::B( - fs::NamedFile::open("static/400.html") - .unwrap() - .set_status_code(StatusCode::BAD_REQUEST), - ) -} - -pub fn error_404() -> StringOrStaticFileResult { - Either::B( - fs::NamedFile::open("static/404.html") - .unwrap() - .set_status_code(StatusCode::NOT_FOUND), - ) -} - // From: https://stackoverflow.com/a/52367953 fn into_static(s: S) -> &'static str where @@ -92,218 +74,252 @@ where Box::leak(s.into().into_boxed_str()) } -fn get_app( - prefix: S, - allowed_origins: &[&'static str], - state: Arc>, -) -> Vec>>> -where - S: Into, -{ - vec![ - App::with_state(AppState { shared: state }) - .prefix(into_static(prefix).to_string()) - .middleware(middleware::Logger::new( - r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T[s] %D[ms]"#, - )) - // ACTIONS ------------------------------------------------------------------- - .resource("/health", |r| { - r.method(Method::GET).f(actions::health); - r.route() - .filter(pred::Not(pred::Get())) - .f(default::page_400); - }) - // DEFAULT ------------------------------------------------------------------- - .default_resource(|r| { - r.f(default::page_400); - }) - // REQUIRES CORS Support --------------------------------------------------------------- - .configure(|app| { - let mut cors = Cors::for_app(app); - for origin in allowed_origins { - cors.allowed_origin(origin); - } - cors.allowed_methods(vec!["GET", "POST", "UPDATE", "PATCH", "DELETE", "OPTIONS"]) - .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) - .allowed_header(http::header::CONTENT_TYPE) - .max_age(600) - .resource("/queries", |r| { - r.method(Method::POST).with(actions::query); - r.route() - .filter(pred::Not(pred::Post())) - .f(default::page_400); - }) - // SPACES ------------------------------------------------------------------- - .resource("/spaces", |r| { - r.method(Method::POST).with(spaces::post); - r.method(Method::PUT).with(spaces::put); - r.method(Method::PATCH).with(spaces::patch); - r.method(Method::DELETE).with(spaces::delete); - }) - .resource("/spaces/{name}", |r| { - r.method(Method::PUT).with(space::put); - r.method(Method::PATCH).with(space::patch); - r.method(Method::GET).with(space::get); - r.method(Method::DELETE).with(space::delete); - }) - // DATASETS ------------------------------------------------------------------- - .resource("/cores", |r| { - r.method(Method::POST).with(&cores::post); - r.method(Method::PUT).with(&cores::put); - r.method(Method::PATCH).with(&cores::patch); - r.method(Method::DELETE).with(&cores::delete); - }) - .resource("/cores/{name}", |r| { - r.method(Method::PUT).with(core::put); - r.method(Method::GET).with(core::get); - r.method(Method::PATCH).with(core::patch); - r.method(Method::DELETE).with(core::delete); - }) - // SPATIAL OBJECTS ------------------------------------------------------------------- - .resource("/cores/{name}/spatial_objects", |r| { - r.method(Method::POST).with(spatial_objects::post); - r.method(Method::PUT).with(spatial_objects::put); - r.method(Method::PATCH).with(spatial_objects::patch); - r.method(Method::DELETE).with(spatial_objects::delete); - }) - .resource("/cores/{name}/spatial_objects/{id}", |r| { - r.method(Method::PUT).with(spatial_object::put); - r.method(Method::GET).with(spatial_object::get); - r.method(Method::PATCH).with(spatial_object::patch); - r.method(Method::DELETE).with(spatial_object::delete); - }) - .register() - }) - .boxed(), - App::new() - .resource("/static/{file}", |r| { - r.method(Method::GET).with(default::static_file) - }) - .default_resource(|r| { - // 404 for GET request - r.method(Method::GET).f(default::page_404); +fn config_v1(cfg: &mut web::ServiceConfig) { + // Warning: Order matters, as a more generic path would catch calls for a + // more specific one when registered first. + space::config(cfg); + spaces::config(cfg); - // all requests that are not `GET` - r.route() - .filter(pred::Not(pred::Get())) - .f(default::page_400_no_state); - }) - .boxed(), - ] + core::config(cfg); + cores::config(cfg); + + spatial_object::config(cfg); + spatial_objects::config(cfg); + + actions::config(cfg); + + cfg.route("/static/{file:.*}", web::get().to(static_file)); + cfg.route("/api/{file:.*}", web::get().to(api)); + cfg.route("/", web::to(page_404)); } -pub fn run( - host: S, - port: u16, - prefix: S, - allowed_origins: Vec, - state: Arc>, -) where - S: Into, -{ - info!("Initializing server..."); +pub fn config(cfg: &mut web::ServiceConfig) { + let prefix; - let sys = actix::System::new("spatial-search"); - let prefix = into_static(prefix); - let host = host.into(); + match std::env::var("MERCATOR_BASE") { + Ok(val) => prefix = val, + Err(val) => { + error!("Could not fetch {} : `{}`", "MERCATOR_BASE", val); + exit(1); + } + }; - let mut origins = Vec::with_capacity(allowed_origins.len()); - for origin in allowed_origins { - origins.push(into_static(origin)); + cfg.service(web::scope(into_static(format!("{}/v1", prefix))).configure(config_v1)) + .service(web::scope(into_static(prefix)).configure(config_v1)) + .route("/health", web::get().to(actions::health)) + .route("/static/{file:.*}", web::get().to(static_file)); +} + +pub fn get_cors() -> Cors { + // Setup CORS support. + let mut cors = Cors::new(); + + match std::env::var("MERCATOR_ALLOWED_ORIGINS") { + Ok(val) => { + let allowed_origins = val.split(',').map(|s| s.trim()).collect::>(); + + for origin in allowed_origins { + if !origin.is_empty() { + cors = cors.allowed_origin(into_static(origin)); + } + } + } + Err(val) => { + warn!( + "Could not fetch {} : `{}`, allowing all origins", + "MERCATOR_ALLOWED_ORIGINS", val + ); + } } - server::new(move || get_app(prefix, &origins, state.clone())) + cors.allowed_methods(vec!["GET", "POST", "UPDATE", "PATCH", "DELETE", "OPTIONS"]) + .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) + .allowed_header(http::header::CONTENT_TYPE) + .max_age(600) +} + +macro_rules! get_app { + ($state:expr) => { + App::new() + .register_data($state.clone()) + .wrap(middleware::Logger::new( + r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T[s] %D[ms]"#, + )) + .wrap(get_cors()) + .configure(config) + .default_service( + web::resource("/") + // 404 for GET request + .route(web::to(page_404)), + ) + }; +} + +pub fn run(host: &str, port: u16, state: Data>) { + info!("Starting http server: {}:{}", host, port); + + // Create & run the server. + match HttpServer::new(move || get_app!(state)) .bind(format!("{}:{}", host, port)) .unwrap() - .start(); - - info!("Started http server: {}:{}{}", host, port, prefix); - - let _ = sys.run(); + .run() + { + Ok(_) => info!("Server Stopped!"), + Err(e) => error!("Error running the server: {}", e), + }; } #[cfg(test)] -mod tests { - use super::get_app; - use super::{Arc, RwLock, SharedState}; +mod tests_utils { + use super::*; - pub use actix_web::http; - pub use actix_web::http::Method; - pub use actix_web::test::TestServer; + //use actix_server_config::ServerConfig; + //use actix_service::IntoNewService; + //use actix_service::NewService; + use actix_service::Service; + //use actix_web::dev::ServiceResponse; + use actix_web::test; + //use actix_web::test::TestRequest; + use mercator_db::DataBase; - pub const PREFIX: &str = "spatial-search"; + pub const CORE_ID: &str = "10k"; - fn get_start_state() -> Arc> { - Arc::new(RwLock::new(0)) - } + pub const PREFIX: &str = "/spatial-search"; + pub const CORE: &str = "/10k"; + pub const SPACE: &str = "/std"; + pub const SPATIAL_OBJECT: &str = "/oid0.44050628835072825"; - pub fn get_test_server() -> TestServer { - TestServer::with_factory(move || get_app(PREFIX, get_start_state().clone())) + pub enum Method { + GET, + POST, + PUT, + PATCH, + DELETE, } pub fn get_path(path: &str) -> String { format!("{}{}", PREFIX, path) } - pub fn expect_200(method: Method, path: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).finish().unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::OK, response.status()); + pub fn get_space(name: &str) -> String { + format!("{}{}", get_path("/spaces"), name) } - pub fn expect_400(method: Method, path: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).finish().unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::BAD_REQUEST, response.status()); + pub fn get_core(name: &str) -> String { + format!("{}{}", get_path("/cores"), name) } - pub fn expect_404(method: Method, path: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).finish().unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::NOT_FOUND, response.status()); + pub fn get_objects(name: &str) -> String { + format!("{}{}{}", get_core(CORE), "/spatial_objects", name) } - pub fn expect_422(method: Method, path: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).finish().unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status()); + pub fn expect(method: Method, path: &str, code: http::StatusCode) { + std::env::set_var("MERCATOR_BASE", PREFIX); + + let mut app = test::init_service(get_app!(Data::new(RwLock::new(SharedState::new( + DataBase::load(CORE_ID).unwrap() + ))))); + + let request = match method { + Method::GET => test::TestRequest::get(), + Method::POST => test::TestRequest::post(), + Method::PUT => test::TestRequest::put(), + Method::PATCH => test::TestRequest::patch(), + Method::DELETE => test::TestRequest::delete(), + }; + + let request = request.uri(&path).to_request(); + let response = test::block_on(app.call(request)).unwrap(); + + assert_eq!(response.status(), code); + } + + pub fn expect_200(method: Method, path: &str) { + expect(method, path, http::StatusCode::OK); + } + + pub fn expect_400(method: Method, path: &str) { + expect(method, path, http::StatusCode::BAD_REQUEST); + } + + pub fn expect_404(method: Method, path: &str) { + expect(method, path, http::StatusCode::NOT_FOUND); + } + + pub fn expect_405(method: Method, path: &str) { + expect(method, path, http::StatusCode::METHOD_NOT_ALLOWED); + } + + pub fn expect_422(method: Method, path: &str) { + expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY); } pub mod json { use super::*; - pub fn expect_200(method: Method, path: String, json: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).json(json).unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::OK, response.status()); + pub fn expect_200(method: Method, path: &str, json: String) { + expect(method, path, http::StatusCode::OK); } - /* - pub fn expect_400(method: Method, path: String, json: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).json(json).unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::BAD_REQUEST, response.status()); - } - */ - - pub fn expect_404(method: Method, path: String, json: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).json(json).unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::NOT_FOUND, response.status()); + pub fn expect_404(method: Method, path: &str, json: String) { + expect(method, path, http::StatusCode::NOT_FOUND); } - pub fn expect_422(method: Method, path: String, json: String) -> () { - let mut srv = get_test_server(); - let req = srv.client(method, path.as_str()).json(json).unwrap(); - let response = srv.execute(req.send()).unwrap(); - assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status()); + pub fn expect_422(method: Method, path: &str, json: String) { + expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY); } } } + +#[cfg(test)] +mod routing { + use std::panic; + + use super::tests_utils::*; + + #[test] + fn default_no_path() { + // FIXME: Currently the string is validated by the URI constructor which + // simply unwraps, thus we have to resort to this ugly workaround. + // The goal is to catch if that behavior changes in the future. + let result = panic::catch_unwind(|| { + expect_404(Method::GET, ""); + }); + assert!(result.is_err()); + } + + #[test] + fn default_slash() { + // We have to manually URL-encode spaces. + expect_404(Method::GET, "/"); + expect_404(Method::GET, "//"); + expect_404(Method::GET, "/%20/"); + expect_404(Method::GET, "/%20//"); + expect_404(Method::GET, "//%20"); + } + + #[test] + fn default_invalid_prefix() { + expect_404(Method::GET, "/test"); + expect_404(Method::GET, &format!("{}test", PREFIX)); + } + + #[test] + fn default_prefix_no_slash() { + expect_404(Method::PUT, PREFIX); + expect_404(Method::GET, PREFIX); + expect_404(Method::POST, PREFIX); + expect_404(Method::PATCH, PREFIX); + expect_404(Method::DELETE, PREFIX); + } + + #[test] + fn default_prefix_final_slash() { + let path = &format!("{}/", PREFIX); + expect_404(Method::PUT, path); + expect_404(Method::GET, path); + expect_404(Method::POST, path); + expect_404(Method::PATCH, path); + expect_404(Method::DELETE, path); + } +} diff --git a/src/rest_api/space.rs b/src/rest_api/space.rs index 5b2b924..1a9d3dc 100644 --- a/src/rest_api/space.rs +++ b/src/rest_api/space.rs @@ -1,23 +1,26 @@ -use actix_web::HttpRequest; -use actix_web::Path; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Path; use crate::model; +use crate::shared_state::SharedState; use super::error_400; use super::error_404; use super::ok_200; -use super::AppState; -use super::StringOrStaticFileResult; +use super::HandlerResult; -pub fn put((path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn put(path: Path) -> HandlerResult { trace!("PUT Triggered on {}", path); error_400() } -pub fn get((path, state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn get((path, state): (Path, Data>)) -> HandlerResult { trace!("GET Triggered on '{}'", path); let name = path.to_string(); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); match context.db().space(name) { Ok(space) => { @@ -28,66 +31,64 @@ pub fn get((path, state): (Path, HttpRequest)) -> StringOrStat } } -pub fn patch((path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn patch(path: Path) -> HandlerResult { trace!("PATCH Triggered on {}", path); error_400() } -pub fn delete((path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn delete(path: Path) -> HandlerResult { trace!("DELETE Triggered on {}", path); error_400() } -#[cfg(test)] -mod tests { - use super::super::tests::*; +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/spaces/{name}") + .route(web::get().to(get)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} - const INSTANCE_EXISTS: &str = "/spaces/42"; - const INSTANCE_INVALID: &str = "/spaces/21"; +#[cfg(test)] +mod routing { + use super::super::tests_utils::*; + + const INSTANCE_EXISTS: &str = SPACE; + const INSTANCE_INVALID: &str = "/21-doesnotexists"; // FIXME: Add Body to request to see difference between (in)valid bodied requests #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_200( - http::Method::PUT, - get_path(INSTANCE_INVALID), - "".to_string(), - ); + json::expect_200(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string()); + json::expect_200(Method::PUT, &get_space(INSTANCE_INVALID), "".to_string()); } #[test] fn patch() { - json::expect_200( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - json::expect_422( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID)); + json::expect_200(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string()); + expect_400(Method::PATCH, &get_space(INSTANCE_INVALID)); } #[test] fn get() { - expect_200(http::Method::GET, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::GET, get_path(INSTANCE_INVALID)); + expect_200(Method::GET, &get_space(INSTANCE_EXISTS)); + expect_404(Method::GET, &get_space(INSTANCE_INVALID)); } #[test] fn delete() { - expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID)); + expect_200(Method::DELETE, &get_space(INSTANCE_EXISTS)); + expect_404(Method::DELETE, &get_space(INSTANCE_INVALID)); } #[test] fn post() { - expect_400(http::Method::POST, get_path(INSTANCE_EXISTS)); - expect_400(http::Method::POST, get_path(INSTANCE_INVALID)); + expect_405(Method::POST, &get_space(INSTANCE_EXISTS)); + expect_405(Method::POST, &get_space(INSTANCE_INVALID)); } } diff --git a/src/rest_api/spaces.rs b/src/rest_api/spaces.rs index 630c550..22b8056 100644 --- a/src/rest_api/spaces.rs +++ b/src/rest_api/spaces.rs @@ -1,17 +1,19 @@ -use actix_web::HttpRequest; -use actix_web::Json; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Json; + +use crate::shared_state::SharedState; use super::error_400; use super::ok_200; -use super::AppState; use super::Filters; -use super::StringOrStaticFileResult; +use super::HandlerResult; -pub fn post( - (parameters, state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn post((parameters, state): (Option>, Data>)) -> HandlerResult { trace!("POST Triggered!"); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); let parameters = Filters::get(parameters); let mut results = match parameters.filters { @@ -20,8 +22,9 @@ pub fn post( .db() .core_keys() .iter() + // FIXME: Specify from json output space + threshold volume .flat_map(|core| match context.filter(&filter, core, None, None) { - Err(_) => vec![], //FIXME: Return errors here instead!! + Err(_) => vec![], // FIXME: Return errors here instead!! Ok(r) => { let mut r = r.into_iter().map(|o| o.space_id).collect::>(); r.sort_unstable(); @@ -37,74 +40,76 @@ pub fn post( ok_200(&results) } -pub fn put( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn put() -> HandlerResult { trace!("PUT Triggered!"); error_400() } -pub fn patch( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn patch() -> HandlerResult { trace!("PATCH Triggered!"); error_400() } -pub fn delete( - (_parameters, _state): (Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn delete() -> HandlerResult { trace!("DELETE Triggered!"); error_400() } -#[cfg(test)] -mod tests { - use super::super::tests::*; +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/spaces") + .route(web::post().to(post)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} - const COLLECTION: &str = "/spaces"; +#[cfg(test)] +mod routing { + use super::super::tests_utils::*; // FIXME: Add Body to request to see difference between (in)valid bodied requests #[test] fn post() { - expect_200(http::Method::POST, get_path(COLLECTION)); - json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string()); + expect_200(Method::POST, &get_space("")); + json::expect_200(Method::POST, &get_space(""), "".to_string()); - json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::POST, &get_space(""), "".to_string()); - expect_400(http::Method::POST, get_path(COLLECTION)); + expect_400(Method::POST, &get_space("")); } #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PUT, &get_space(""), "".to_string()); - json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PUT, &get_space(""), "".to_string()); - expect_400(http::Method::PUT, get_path(COLLECTION)); + expect_400(Method::PUT, &get_space("")); } #[test] fn patch() { - json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PATCH, &get_space(""), "".to_string()); - json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PATCH, &get_space(""), "".to_string()); - expect_400(http::Method::PATCH, get_path(COLLECTION)); + expect_400(Method::PATCH, &get_space("")); } #[test] fn delete() { - json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::DELETE, &get_space(""), "".to_string()); - json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::DELETE, &get_space(""), "".to_string()); - expect_400(http::Method::DELETE, get_path(COLLECTION)); + expect_400(Method::DELETE, &get_space("")); } #[test] fn get() { - expect_400(http::Method::GET, get_path(COLLECTION)); + expect_405(Method::GET, &get_space("")); } } diff --git a/src/rest_api/spatial_object.rs b/src/rest_api/spatial_object.rs index 59a2050..38e1342 100644 --- a/src/rest_api/spatial_object.rs +++ b/src/rest_api/spatial_object.rs @@ -1,104 +1,104 @@ -use actix_web::HttpRequest; -use actix_web::Json; -use actix_web::Path; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Path; + +use crate::model::to_spatial_objects; +use crate::shared_state::SharedState; use super::error_400; use super::error_404; use super::ok_200; -use super::AppState; -use super::StringOrStaticFileResult; -use crate::model::to_spatial_objects; +use super::HandlerResult; -pub fn put( - (core, id, state): (Path, Path, HttpRequest), -) -> StringOrStaticFileResult { - trace!("PUT Triggered!"); +fn put(path: Path) -> HandlerResult { + trace!("PUT Triggered on {}", path); error_400() } -pub fn get( - (path, state): (Path<(String, String)>, HttpRequest), -) -> StringOrStaticFileResult { +fn get((path, state): (Path<(String, String)>, Data>)) -> HandlerResult { trace!("GET Triggered!"); let (core, id) = path.into_inner(); let core = core.to_string(); let id = id.to_string(); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); let db = context.db(); match db.core(core) { Ok(core) => match core.get_by_id(db, &id, None, 0.0) { - Ok(objects) => ok_200(&to_spatial_objects(db, objects)), + Ok(objects) => { + let results = to_spatial_objects(db, objects); + if results.is_empty() { + error_404() + } else { + ok_200(&results) + } + } Err(_) => error_404(), }, Err(_) => error_404(), } } -pub fn patch( - (core, id, state): (Path, Path, HttpRequest), -) -> StringOrStaticFileResult { - trace!("PATCH Triggered!"); +fn patch(path: Path) -> HandlerResult { + trace!("PATCH Triggered on {}", path); error_400() } -pub fn delete( - (core, id, state): (Path, Path, HttpRequest), -) -> StringOrStaticFileResult { - trace!("DELETE Triggered!"); +fn delete(path: Path) -> HandlerResult { + trace!("DELETE Triggered on {}", path); error_400() } +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/cores/{name}/spatial_objects/{id}") + .route(web::get().to(get)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} + #[cfg(test)] -mod tests { - use super::super::tests::*; +mod routing { + use super::super::tests_utils::*; - const INSTANCE_EXISTS: &str = "/cores/42/spatial_objects/42"; - const INSTANCE_INVALID: &str = "/cores/42/spatial_objects/21"; + const INSTANCE_EXISTS: &str = SPATIAL_OBJECT; + const INSTANCE_INVALID: &str = "/21-doesnotexists"; // FIXME: Add Body to request to see difference between (in)valid bodied requests #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string()); - json::expect_200( - http::Method::PUT, - get_path(INSTANCE_INVALID), - "".to_string(), - ); + json::expect_200(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string()); + json::expect_200(Method::PUT, &get_objects(INSTANCE_INVALID), "".to_string()); } #[test] fn patch() { - json::expect_200( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - json::expect_422( - http::Method::PATCH, - get_path(INSTANCE_EXISTS), - "".to_string(), - ); - expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID)); + json::expect_200(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string()); + json::expect_422(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string()); + expect_400(Method::PATCH, &get_objects(INSTANCE_INVALID)); } #[test] fn get() { - expect_200(http::Method::GET, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::GET, get_path(INSTANCE_INVALID)); + expect_200(Method::GET, &get_objects(INSTANCE_EXISTS)); + expect_404(Method::GET, &get_objects(INSTANCE_INVALID)); } #[test] fn delete() { - expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS)); - expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID)); + expect_200(Method::DELETE, &get_objects(INSTANCE_EXISTS)); + expect_404(Method::DELETE, &get_objects(INSTANCE_INVALID)); } #[test] fn post() { - expect_400(http::Method::POST, get_path(INSTANCE_EXISTS)); - expect_400(http::Method::POST, get_path(INSTANCE_INVALID)); + expect_405(Method::POST, &get_objects(INSTANCE_EXISTS)); + expect_405(Method::POST, &get_objects(INSTANCE_INVALID)); } } diff --git a/src/rest_api/spatial_objects.rs b/src/rest_api/spatial_objects.rs index 5af361f..33e965d 100644 --- a/src/rest_api/spatial_objects.rs +++ b/src/rest_api/spatial_objects.rs @@ -1,20 +1,28 @@ -use actix_web::HttpRequest; -use actix_web::Json; -use actix_web::Path; +use std::sync::RwLock; + +use actix_web::web; +use actix_web::web::Data; +use actix_web::web::Json; +use actix_web::web::Path; + +use crate::shared_state::SharedState; use super::error_400; use super::error_404; use super::ok_200; -use super::AppState; use super::Filters; -use super::StringOrStaticFileResult; +use super::HandlerResult; -pub fn post( - (core_id, parameters, state): (Path, Option>, HttpRequest), -) -> StringOrStaticFileResult { +fn post( + (core_id, parameters, state): ( + Path, + Option>, + Data>, + ), +) -> HandlerResult { trace!("POST Triggered!"); let core = core_id.to_string(); - let context = state.state().shared.read().unwrap(); + let context = state.read().unwrap(); match context.db().core(core) { Ok(core) => { @@ -23,8 +31,9 @@ pub fn post( // Generate a list of oid. let mut results = match parameters.filters { None => core.keys().iter().map(|o| o.id().clone()).collect(), + // FIXME: Specify from json output space + threshold volume Some(filter) => match context.filter(&filter, &core_id, None, None) { - Err(_) => vec![], //FIXME: Return errors here instead!! + Err(_) => vec![], // FIXME: Return errors here instead!! Ok(objects) => objects.iter().map(|o| o.value.id().clone()).collect(), }, }; @@ -37,68 +46,76 @@ pub fn post( } } -pub fn put((_path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn put() -> HandlerResult { trace!("PUT Triggered!"); error_400() } -pub fn patch((_path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn patch() -> HandlerResult { trace!("PATCH Triggered!"); error_400() } -pub fn delete((_path, _state): (Path, HttpRequest)) -> StringOrStaticFileResult { +fn delete() -> HandlerResult { trace!("DELETE Triggered!"); error_400() } -#[cfg(test)] -mod tests { - use super::super::tests::*; +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service( + web::resource("/cores/{name}/spatial_objects") + .route(web::post().to(post)) + .route(web::put().to(put)) + .route(web::patch().to(patch)) + .route(web::delete().to(delete)), + ); +} - const COLLECTION: &str = "/cores/42/spatial_objects"; +#[cfg(test)] +mod routing { + use super::super::tests_utils::*; // FIXME: Add Body to request to see difference between (in)valid bodied requests #[test] fn post() { - expect_200(http::Method::POST, get_path(COLLECTION)); - json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string()); + expect_200(Method::POST, &get_objects("")); + json::expect_200(Method::POST, &get_objects(""), "".to_string()); - json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::POST, &get_objects(""), "".to_string()); - expect_400(http::Method::POST, get_path(COLLECTION)); + expect_400(Method::POST, &get_objects("")); } #[test] fn put() { - json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PUT, &get_objects(""), "".to_string()); - json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PUT, &get_objects(""), "".to_string()); - expect_400(http::Method::PUT, get_path(COLLECTION)); + expect_400(Method::PUT, &get_objects("")); } #[test] fn patch() { - json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::PATCH, &get_objects(""), "".to_string()); - json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::PATCH, &get_objects(""), "".to_string()); - expect_400(http::Method::PATCH, get_path(COLLECTION)); + expect_400(Method::PATCH, &get_objects("")); } #[test] fn delete() { - json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_200(Method::DELETE, &get_objects(""), "".to_string()); - json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string()); + json::expect_422(Method::DELETE, &get_objects(""), "".to_string()); - expect_400(http::Method::DELETE, get_path(COLLECTION)); + expect_400(Method::DELETE, &get_objects("")); } #[test] fn get() { - expect_400(http::Method::GET, get_path(COLLECTION)); + expect_405(Method::GET, &get_objects("")); } } diff --git a/api/spatial_search-v0.2.yaml b/static/api/v0.2.yaml similarity index 99% rename from api/spatial_search-v0.2.yaml rename to static/api/v0.2.yaml index 7aff319..4c4dd2e 100644 --- a/api/spatial_search-v0.2.yaml +++ b/static/api/v0.2.yaml @@ -34,13 +34,13 @@ paths: summary: > Health check of the service. description: > - Please note that making anything but a **GET** call is a bad request. + Please note that making anything but a **GET** call is a bad request, and will return a 405. operationId: get_health_check responses: '200': $ref: '#/responses/Standard200' default: - $ref: '#/responses/Standard400' + $ref: '#/responses/Standard405' /queries: post: @@ -58,7 +58,7 @@ paths: '422': $ref: '#/responses/Standard422' default: - $ref: '#/responses/Standard400' + $ref: '#/responses/Standard405' #-------------------------------------------------------------------- # SPACES QUERIES @@ -748,6 +748,9 @@ responses: Standard422: description: > Unprocessable Entity + Standard405: + description: > + Invalid Method Standard404: description: > Object not found diff --git a/static/400.html b/static/errors/400.html similarity index 100% rename from static/400.html rename to static/errors/400.html diff --git a/static/404.html b/static/errors/404.html similarity index 100% rename from static/404.html rename to static/errors/404.html diff --git a/static/errors/405.html b/static/errors/405.html new file mode 100644 index 0000000..4001a15 --- /dev/null +++ b/static/errors/405.html @@ -0,0 +1,8 @@ + + + 405 - Method not allowed + + +

405 - Method not allowed

+ +