Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macros for generating HTTP methods procs #6

Merged
merged 6 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ All procedures above return `Response` object with fields:
- `request` - object with request data processed by `yahttp`
- `url` - stores full url with query params
- `headers` - stores HTTP headers with `Authorization` for basic authorization
- `httpMethod` - HTTP method

`Response` object has some helper procedures:
- `Response.json()` - returns response body as JSON
Expand Down
2 changes: 1 addition & 1 deletion examples/examples.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ for catTag in catTags[0..4]:
echo "Working with tag " & tag
let catData = get("https://cataas.com/api/cats", query = {"tags": tag, "limit": "10"})

echo "Request URL: ", catData.request.url
echo "Request method and URL: ", catData.request.httpMethod, " ", catData.request.url
echo "Response status: ", catData.status
echo "Response headers: ", catData.headers
echo "Response body: ", catData.body
115 changes: 22 additions & 93 deletions src/yahttp.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import base64, httpclient, net, json, uri, strutils, tables

import yahttp/internal/utils

type
## Types without methods
QueryParam* = tuple[key: string, value: string] ## Type for URL query params
Expand All @@ -16,7 +18,7 @@ type
GET, PUT, POST, PATCH, DELETE, HEAD, OPTIONS


type
type
BasicAuth* = tuple[login: string, password: string] ## Basic auth type

proc basicAuthHeader(auth: BasicAuth): string =
Expand All @@ -27,6 +29,7 @@ type
## Type to store request infornation in response
url*: string
headers*: seq[tuple[key: string, val: string]]
httpMethod*: Method

Response* = object
## Type for HTTP response
Expand All @@ -35,13 +38,14 @@ type
headers*: TableRef[string, seq[string]]
request*: Request

proc toResp(response: httpclient.Response, requestUrl: string, requestHeaders: seq[tuple[key: string, val: string]]): Response =
proc toResp(response: httpclient.Response, requestUrl: string,
requestHeaders: seq[tuple[key: string, val: string]], requestHttpMethod: Method): Response =
## Convert httpclient.Response to yahttp.Response
return Response(
status: parseInt(response.status.strip()[0..2]),
headers: response.headers.table,
body: response.body,
request: Request(url: requestUrl, headers: requestHeaders)
request: Request(url: requestUrl, headers: requestHeaders, httpMethod: requestHttpMethod)
)

proc json*(response: Response): JsonNode =
Expand All @@ -61,7 +65,9 @@ const defaultEncodeQueryParams = EncodeQueryParams(usePlus: false, omitEq: true,


proc request*(url: string, httpMethod: Method = Method.GET, headers: openArray[
RequestHeader] = [], query: openArray[QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "",
RequestHeader] = [], query: openArray[QueryParam] = [],
encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams,
body: string = "",
auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
## Genreal proc to make HTTP request with every HTTP method

Expand All @@ -87,7 +93,9 @@ proc request*(url: string, httpMethod: Method = Method.GET, headers: openArray[

# Prepare url

let innerUrl = if query.len() > 0: url & "?" & encodeQuery(query, usePlus = encodeQueryParams.usePlus, omitEq = encodeQueryParams.omitEq, sep = encodeQueryParams.sep) else: url
let innerUrl = if query.len() > 0: url & "?" & encodeQuery(query,
usePlus = encodeQueryParams.usePlus, omitEq = encodeQueryParams.omitEq,
sep = encodeQueryParams.sep) else: url

# Prepare HTTP method

Expand All @@ -105,94 +113,15 @@ proc request*(url: string, httpMethod: Method = Method.GET, headers: openArray[
let response = client.request(innerUrl, httpMethod = innerMethod, body = body)
client.close()

return response.toResp(requestUrl = innerUrl, requestHeaders = innerHeaders)


# Deidcated procs for individual methods

proc get*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.GET,
headers = headers,
query = query,
auth = auth,
ignoreSsl = ignoreSsl
)

proc put*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "", auth: BasicAuth = ("", ""),
ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.PUT,
headers = headers,
query = query,
body = body,
auth = auth,
ignoreSsl = ignoreSsl
)

proc post*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "", auth: BasicAuth = ("", ""),
ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.POST,
headers = headers,
query = query,
body = body,
auth = auth,
ignoreSsl = ignoreSsl
)
return response.toResp(requestUrl = innerUrl, requestHeaders = innerHeaders, requestHttpMethod = httpMethod)

proc patch*(url: string, headers: openArray[RequestHeader] = [],
query: openArray[QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "",
auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.PATCH,
headers = headers,
query = query,
body = body,
auth = auth,
ignoreSsl = ignoreSsl
)

# Gnerating procs for individual HTTP methods

proc delete*(url: string, headers: openArray[RequestHeader] = [],
query: openArray[QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "",
auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.DELETE,
headers = headers,
query = query,
body = body,
auth = auth,
ignoreSsl = ignoreSsl
)

proc head*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.HEAD,
headers = headers,
query = query,
auth = auth,
ignoreSsl = ignoreSsl
)

proc options*(url: string, headers: openArray[RequestHeader] = [],
query: openArray[QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, auth: BasicAuth = ("", ""),
ignoreSsl = false): Response =
return request(
url = url,
httpMethod = Method.OPTIONS,
headers = headers,
query = query,
auth = auth,
ignoreSsl = ignoreSsl
)
http_method_no_body_gen get
http_method_no_body_gen head
http_method_no_body_gen options
http_method_gen put
http_method_gen post
http_method_gen patch
http_method_gen delete
38 changes: 38 additions & 0 deletions src/yahttp/internal/utils.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import macros, strutils, strformat

macro http_method_gen*(name: untyped): untyped =
let methodUpper = name.strVal().toUpper()
let methodName = newIdentNode(methodUpper)
let comment = newCommentStmtNode(fmt"Proc for {methodUpper} HTTP method")
quote do:
proc `name`*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, body: string = "", auth: BasicAuth = ("", ""),
ignoreSsl = false): Response =
`comment`
return request(
url = url,
httpMethod = Method.`methodName`,
headers = headers,
query = query,
body = body,
auth = auth,
ignoreSsl = ignoreSsl
)


macro http_method_no_body_gen*(name: untyped): untyped =
let methodUpper = name.strVal().toUpper()
let methodName = newIdentNode(methodUpper)
let comment = newCommentStmtNode(fmt"Proc for {methodUpper} HTTP method")
quote do:
proc `name`*(url: string, headers: openArray[RequestHeader] = [], query: openArray[
QueryParam] = [], encodeQueryParams: EncodeQueryParams = defaultEncodeQueryParams, auth: BasicAuth = ("", ""), ignoreSsl = false): Response =
`comment`
return request(
url = url,
httpMethod = Method.`methodName`,
headers = headers,
query = query,
auth = auth,
ignoreSsl = ignoreSsl
)