From 6defd70a2df0975ee22b09018a9dc709a7933200 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 6 Apr 2022 09:51:31 +0300 Subject: feat! add chunk server API version to HTTP paths What was /chunks is now /v1/chunks. This is the minimal step to start supporting multiple API versions. Also, /v1/chunks/foo/bar is no longer supported. Sponsored-by: author --- obnam.md | 34 +++++++++++++++++----------------- src/bin/obnam-server.rs | 8 ++++++++ src/client.rs | 2 +- subplot/server.py | 8 ++++---- subplot/server.yaml | 10 +++++----- 5 files changed, 35 insertions(+), 27 deletions(-) diff --git a/obnam.md b/obnam.md index a06717a..a02aacb 100644 --- a/obnam.md +++ b/obnam.md @@ -925,11 +925,11 @@ not to the server. The server has the following API for managing chunks: -* `POST /chunks` — store a new chunk (and its metadata) on the +* `POST /v1/chunks` — store a new chunk (and its metadata) on the server, return its randomly chosen identifier -* `GET /chunks/` — retrieve a chunk (and its metadata) from +* `GET /v1/chunks/` — retrieve a chunk (and its metadata) from the server, given a chunk identifier -* `GET /chunks?label=xyzzy` — find chunks on the server whose +* `GET /v1/chunks?label=xyzzy` — find chunks on the server whose metadata has a specific value for a label. HTTP status codes are used to indicate if a request succeeded or not, @@ -1092,7 +1092,7 @@ storage of backed up data. ~~~scenario given a working Obnam system and a file data.dat containing some random data -when I POST data.dat to /chunks, with chunk-meta: {"label":"abc"} +when I POST data.dat to /v1/chunks, with chunk-meta: {"label":"abc"} then HTTP status code is 201 and content-type is application/json and the JSON body has a field chunk_id, henceforth ID @@ -1102,7 +1102,7 @@ and server has 1 chunks We must be able to retrieve it. ~~~scenario -when I GET /chunks/ +when I GET /v1/chunks/ then HTTP status code is 200 and content-type is application/octet-stream and chunk-meta is {"label":"abc"} @@ -1112,7 +1112,7 @@ and the body matches file data.dat We must also be able to find it based on metadata. ~~~scenario -when I GET /chunks?label=abc +when I GET /v1/chunks?label=abc then HTTP status code is 200 and content-type is application/json and the JSON body matches {"":{"label":"abc"}} @@ -1122,13 +1122,13 @@ Finally, we must be able to delete it. After that, we must not be able to retrieve it, or find it using metadata. ~~~scenario -when I DELETE /chunks/ +when I DELETE /v1/chunks/ then HTTP status code is 200 -when I GET /chunks/ +when I GET /v1/chunks/ then HTTP status code is 404 -when I GET /chunks?label=abc +when I GET /v1/chunks?label=abc then HTTP status code is 200 and content-type is application/json and the JSON body matches {} @@ -1141,7 +1141,7 @@ not exist. ~~~scenario given a working Obnam system -when I try to GET /chunks/any.random.string +when I try to GET /v1/chunks/any.random.string then HTTP status code is 404 ~~~ @@ -1151,7 +1151,7 @@ We must get an empty result if searching for chunks that don't exist. ~~~scenario given a working Obnam system -when I GET /chunks?label=abc +when I GET /v1/chunks?label=abc then HTTP status code is 200 and content-type is application/json and the JSON body matches {} @@ -1163,7 +1163,7 @@ We must get the right error when deleting a chunk that doesn't exist. ~~~scenario given a working Obnam system -when I try to DELETE /chunks/any.random.string +when I try to DELETE /v1/chunks/any.random.string then HTTP status code is 404 ~~~ @@ -1178,7 +1178,7 @@ First, create a chunk. ~~~scenario given a working Obnam system and a file data.dat containing some random data -when I POST data.dat to /chunks, with chunk-meta: {"label":"abc"} +when I POST data.dat to /v1/chunks, with chunk-meta: {"label":"abc"} then HTTP status code is 201 and content-type is application/json and the JSON body has a field chunk_id, henceforth ID @@ -1194,7 +1194,7 @@ given a running chunk server Can we still find it by its metadata? ~~~scenario -when I GET /chunks?label=abc +when I GET /v1/chunks?label=abc then HTTP status code is 200 and content-type is application/json and the JSON body matches {"":{"label":"abc"}} @@ -1203,7 +1203,7 @@ and the JSON body matches {"":{"label":"abc"}} Can we still retrieve it by its identifier? ~~~scenario -when I GET /chunks/ +when I GET /v1/chunks/ then HTTP status code is 200 and content-type is application/octet-stream and chunk-meta is {"label":"abc"} @@ -1220,14 +1220,14 @@ server more chatty. ~~~scenario given a working Obnam system and a file data1.dat containing some random data -when I POST data1.dat to /chunks, with chunk-meta: {"label":"qwerty"} +when I POST data1.dat to /v1/chunks, with chunk-meta: {"label":"qwerty"} then the JSON body has a field chunk_id, henceforth ID and chunk server's stderr doesn't contain "Obnam server starting up" and chunk server's stderr doesn't contain "created chunk " given a running chunk server with environment {"OBNAM_SERVER_LOG": "info"} and a file data2.dat containing some random data -when I POST data2.dat to /chunks, with chunk-meta: {"label":"xyz"} +when I POST data2.dat to /v1/chunks, with chunk-meta: {"label":"xyz"} then the JSON body has a field chunk_id, henceforth ID and chunk server's stderr contains "Obnam server starting up" and chunk server's stderr contains "created chunk " diff --git a/src/bin/obnam-server.rs b/src/bin/obnam-server.rs index 5be2cee..6cf4122 100644 --- a/src/bin/obnam-server.rs +++ b/src/bin/obnam-server.rs @@ -47,27 +47,35 @@ async fn main() -> anyhow::Result<()> { debug!("Configuration: {:#?}", config); let create = warp::post() + .and(warp::path("v1")) .and(warp::path("chunks")) + .and(warp::path::end()) .and(store.clone()) .and(warp::header("chunk-meta")) .and(warp::filters::body::bytes()) .and_then(create_chunk); let fetch = warp::get() + .and(warp::path("v1")) .and(warp::path("chunks")) .and(warp::path::param()) + .and(warp::path::end()) .and(store.clone()) .and_then(fetch_chunk); let search = warp::get() + .and(warp::path("v1")) .and(warp::path("chunks")) + .and(warp::path::end()) .and(warp::query::>()) .and(store.clone()) .and_then(search_chunks); let delete = warp::delete() + .and(warp::path("v1")) .and(warp::path("chunks")) .and(warp::path::param()) + .and(warp::path::end()) .and(store.clone()) .and_then(delete_chunk); diff --git a/src/client.rs b/src/client.rs index 5b13cb7..c5d66c1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -131,7 +131,7 @@ impl BackupClient { } fn chunks_url(&self) -> String { - format!("{}/chunks", self.base_url()) + format!("{}/v1/chunks", self.base_url()) } /// Does the server have a chunk? diff --git a/subplot/server.py b/subplot/server.py index de63836..1f4506f 100644 --- a/subplot/server.py +++ b/subplot/server.py @@ -53,7 +53,7 @@ def stop_chunk_server(ctx, env=None): def post_file(ctx, filename=None, path=None, header=None, json=None): - url = f"{ctx['server_url']}/chunks" + url = f"{ctx['server_url']}/v1/chunks" headers = {header: json} data = open(filename, "rb").read() _request(ctx, requests.post, url, headers=headers, data=data) @@ -65,12 +65,12 @@ def get_chunk_via_var(ctx, var=None): def get_chunk_by_id(ctx, chunk_id=None): - url = f"{ctx['server_url']}/chunks/{chunk_id}" + url = f"{ctx['server_url']}/v1/chunks/{chunk_id}" _request(ctx, requests.get, url) def find_chunks_with_label(ctx, sha=None): - url = f"{ctx['server_url']}/chunks?label={sha}" + url = f"{ctx['server_url']}/v1/chunks?label={sha}" _request(ctx, requests.get, url) @@ -80,7 +80,7 @@ def delete_chunk_via_var(ctx, var=None): def delete_chunk_by_id(ctx, chunk_id=None): - url = f"{ctx['server_url']}/chunks/{chunk_id}" + url = f"{ctx['server_url']}/v1/chunks/{chunk_id}" _request(ctx, requests.delete, url) diff --git a/subplot/server.yaml b/subplot/server.yaml index 7b7d461..ac45cac 100644 --- a/subplot/server.yaml +++ b/subplot/server.yaml @@ -21,28 +21,28 @@ python: function: post_file -- when: "I GET /chunks/<{var}>" +- when: "I GET /v1/chunks/<{var}>" impl: python: function: get_chunk_via_var -- when: "I try to GET /chunks/{chunk_id}" +- when: "I try to GET /v1/chunks/{chunk_id}" impl: python: function: get_chunk_by_id -- when: "I GET /chunks?label={sha}" +- when: "I GET /v1/chunks?label={sha}" regex: false impl: python: function: find_chunks_with_label -- when: "I DELETE /chunks/<{var}>" +- when: "I DELETE /v1/chunks/<{var}>" impl: python: function: delete_chunk_via_var -- when: "I try to DELETE /chunks/{chunk_id}" +- when: "I try to DELETE /v1/chunks/{chunk_id}" impl: python: function: delete_chunk_by_id -- cgit v1.2.1