Skip to content

Commit

Permalink
feat: support net.http_delete with request body
Browse files Browse the repository at this point in the history
Addresses #77 (comment).
Now it's possible to do:

```sql
select net.http_delete(
    url  :='http://localhost:8080/delete_w_body'
,   body := '{"key": "val"}'
);
```

Backwards compatibility is tested. No body will be sent if
the body parameter is NULL (the default).
  • Loading branch information
steve-chavez committed Dec 12, 2024
1 parent 3cbe173 commit d312525
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 3 deletions.
9 changes: 9 additions & 0 deletions nix/nginx/conf/custom.conf
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ location /delete {
echo_duplicate 1 $echo_client_request_headers$is_args$query_string;
}

location /delete_w_body {
if ($request_method != 'DELETE'){
return 405;
}

echo_read_request_body;
echo $request_body;
}

location /redirect_me {
return 301 /to_here;
}
Expand Down
43 changes: 43 additions & 0 deletions sql/pg_net--0.14.0--0.15.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
drop function net.http_delete (text, jsonb, jsonb, integer);

create function net.http_delete(
-- url for the request
url text,
-- key/value pairs to be url encoded and appended to the `url`
params jsonb default '{}'::jsonb,
-- key/values to be included in request headers
headers jsonb default '{}'::jsonb,
-- the maximum number of milliseconds the request may take before being cancelled
timeout_milliseconds int default 5000,
-- optional body of the request
body jsonb default NULL
)
-- request_id reference
returns bigint
volatile
parallel safe
language plpgsql
as $$
declare
request_id bigint;
params_array text[];
begin
select coalesce(array_agg(net._urlencode_string(key) || '=' || net._urlencode_string(value)), '{}')
into params_array
from jsonb_each_text(params);

-- Add to the request queue
insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds)
values (
'DELETE',
net._encode_url_with_params_array(url, params_array),
headers,
convert_to(body::text, 'UTF8'),
timeout_milliseconds
)
returning id
into request_id;

return request_id;
end
$$;
8 changes: 5 additions & 3 deletions sql/pg_net.sql
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,12 @@ create or replace function net.http_delete(
-- key/values to be included in request headers
headers jsonb default '{}'::jsonb,
-- the maximum number of milliseconds the request may take before being cancelled
timeout_milliseconds int default 5000
timeout_milliseconds int default 5000,
-- optional body of the request
body jsonb default NULL
)
-- request_id reference
returns bigint
strict
volatile
parallel safe
language plpgsql
Expand All @@ -241,11 +242,12 @@ begin
from jsonb_each_text(params);

-- Add to the request queue
insert into net.http_request_queue(method, url, headers, timeout_milliseconds)
insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds)
values (
'DELETE',
net._encode_url_with_params_array(url, params_array),
headers,
convert_to(body::text, 'UTF8'),
timeout_milliseconds
)
returning id
Expand Down
3 changes: 3 additions & 0 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ static void init_curl_handle(CURLM *curl_mhandle, MemoryContext curl_memctx, int

if (strcasecmp(method, "DELETE") == 0) {
EREPORT_CURL_SETOPT(curl_ez_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
if (reqBody) {
EREPORT_CURL_SETOPT(curl_ez_handle, CURLOPT_POSTFIELDS, reqBody);
}
}

EREPORT_CURL_SETOPT(curl_ez_handle, CURLOPT_WRITEFUNCTION, body_cb);
Expand Down
32 changes: 32 additions & 0 deletions test/test_http_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,35 @@ def test_http_delete_positional_args(sess):
assert response is not None
assert response[0] == "SUCCESS"
assert response[1] == "ok"


def test_http_delete_with_body(sess):
"""delete with request body works"""

# Create a request
(request_id,) = sess.execute(text(
"""
select net.http_delete(
url :='http://localhost:8080/delete_w_body'
, body := '{"key": "val"}'
);
"""
)).fetchone()

# Commit so background worker can start
sess.commit()

# Collect the response, waiting as needed
(response_json,) = sess.execute(
text(
"""
select
((x.response).body)::jsonb body_json
from
net._http_collect_response(:request_id, async:=false) x;
"""
),
{"request_id": request_id},
).fetchone()

assert response_json["key"] == "val"

0 comments on commit d312525

Please sign in to comment.