Skip to content

Commit 6db3cda

Browse files
committed
Make inner HTTP client configurable
1 parent a276e1c commit 6db3cda

15 files changed

+350
-32
lines changed

Makefile

+11
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,14 @@ generate: tools ${OPENAPI_SPEC} ## Generate the Horreum client
7878
set -e ;\
7979
${PROJECT_BIN}/kiota generate -l python -c HorreumRawClient -n raw_client -d ${OPENAPI_PATH}/openapi.yaml -o ${GENERATED_CLIENT_PATH} ;\
8080
}
81+
82+
83+
##@ Example
84+
85+
.PHONY: run-basic-example
86+
run-basic-example: ## Run basic example
87+
cd examples && python basic_example.py
88+
89+
.PHONY: run-read-only-example
90+
run-read-only-example: ## Run read-only example
91+
cd examples && python read_only_example.py

docs/GET_STARTED.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ Here a very simple example:
5757
>>> import asyncio
5858

5959
# Import the constructor function
60-
>>> from horreum.horreum_client import new_horreum_client
60+
>>> from horreum.horreum_client import new_horreum_client, HorreumCredentials
6161

6262
# Initialize the client
63-
>>> client = await new_horreum_client(base_url="http://localhost:8080", username="..", password="..")
63+
>>> client = await new_horreum_client(base_url="http://localhost:8080", credentials=HorreumCredentials(username=username, password=password))
6464

6565
# Call the api using the underlying raw client, in this case retrieve the Horreum server version
6666
>>> await client.raw_client.api.config.version.get()
@@ -72,7 +72,7 @@ The previous api call is equivalent to the following `cURL`:
7272
curl --silent -X 'GET' 'http://localhost:8080/api/config/version' -H 'accept: application/json' | jq '.'
7373
```
7474

75-
Other examples can be found in the [test folder](../test), for instance:
75+
Other examples can be found in the [examples folder](../examples), for instance:
7676

7777
```bash
7878
# Import Horreum Test model

examples/basic_example.py

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import asyncio
2+
import json
3+
4+
from kiota_abstractions.base_request_configuration import RequestConfiguration
5+
6+
from horreum import HorreumCredentials, new_horreum_client
7+
from horreum.horreum_client import HorreumClient
8+
from horreum.raw_client.api.run.test.test_request_builder import TestRequestBuilder
9+
from horreum.raw_client.models.extractor import Extractor
10+
from horreum.raw_client.models.run import Run
11+
from horreum.raw_client.models.schema import Schema
12+
from horreum.raw_client.models.test import Test
13+
from horreum.raw_client.models.transformer import Transformer
14+
15+
base_url = "http://localhost:8080"
16+
username = "user"
17+
password = "secret"
18+
19+
cleanup_data = True
20+
21+
22+
async def create_schema(client: HorreumClient, data_path: str) -> int:
23+
print(f"creating schema from {data_path}")
24+
schema_data = json.load(open(data_path), object_hook=lambda d: Schema(**d))
25+
print(schema_data)
26+
27+
schema_id = await client.raw_client.api.schema.post(schema_data)
28+
assert schema_id > 0
29+
return schema_id
30+
31+
32+
async def create_schema_transformers(client: HorreumClient, schema_id: int, data_path: str,
33+
extractors_data_path: str) -> int:
34+
print(f"creating transformer from {data_path}")
35+
transformer_data = json.load(open(data_path), object_hook=lambda d: Transformer(**d))
36+
print(transformer_data)
37+
38+
print(f"creating extractors from {extractors_data_path}")
39+
extractors_data = json.load(open(extractors_data_path),
40+
object_hook=lambda d: Extractor(**d))
41+
print(extractors_data)
42+
43+
transformer_data.extractors = extractors_data
44+
45+
transformer_id = await client.raw_client.api.schema.by_id_id(schema_id).transformers.post(transformer_data)
46+
assert transformer_id > 0
47+
return transformer_id
48+
49+
50+
async def create_test(client: HorreumClient, data_path: str) -> Test:
51+
print(f"creating test from {data_path}")
52+
53+
test_data = json.load(open(data_path), object_hook=lambda d: Test(**d))
54+
print(test_data)
55+
56+
test = await client.raw_client.api.test.post(test_data)
57+
assert test.id > 0
58+
return test
59+
60+
61+
async def set_test_transformers(client: HorreumClient, test_id: int, transformers: list[int]):
62+
await client.raw_client.api.test.by_id(test_id).transformers.post(transformers)
63+
64+
65+
async def upload_run(client: HorreumClient, test_id: int, run_path: str, run_data_path: str):
66+
print(f"uploading run from {run_path}")
67+
68+
run = json.load(open(run_path), object_hook=lambda d: Run(**d))
69+
run_data = json.load(open(run_data_path))
70+
run.data = json.dumps(run_data)
71+
print(run)
72+
73+
query_params = TestRequestBuilder.TestRequestBuilderPostQueryParameters(test=str(test_id))
74+
config = RequestConfiguration(query_parameters=query_params)
75+
await client.raw_client.api.run.test.post(run, config)
76+
77+
78+
async def setup_roadrunner_test(client: HorreumClient):
79+
print("creating roadrunner test")
80+
81+
acme_benchmark_schema_id = await create_schema(client, "./data/acme_benchmark_schema.json")
82+
acme_horreum_schema_id = await create_schema(client, "./data/acme_horreum_schema.json")
83+
84+
acme_transformers_id = await create_schema_transformers(client, acme_benchmark_schema_id,
85+
"./data/acme_transformer.json",
86+
"./data/acme_transformer_extractors.json")
87+
88+
roadrunner_test = await create_test(client, "./data/roadrunner_test.json")
89+
await set_test_transformers(client, roadrunner_test.id, [acme_transformers_id])
90+
91+
await upload_run(client, roadrunner_test.id, "./data/roadrunner_run.json", "./data/roadrunner_run_data.json")
92+
93+
94+
async def delete_all(client: HorreumClient):
95+
""" cleanup all Horreum data """
96+
97+
print("cleaning up tests")
98+
get_tests = await client.raw_client.api.test.get()
99+
for t in get_tests.tests:
100+
await client.raw_client.api.test.by_id(t.id).delete()
101+
102+
get_tests = await client.raw_client.api.test.get()
103+
assert get_tests.count == 0
104+
105+
print("cleaning up schemas")
106+
get_schemas = await client.raw_client.api.schema.get()
107+
for s in get_schemas.schemas:
108+
await client.raw_client.api.schema.by_id_id(s.id).delete()
109+
110+
get_schemas = await client.raw_client.api.schema.get()
111+
assert get_schemas.count == 0
112+
113+
114+
async def example():
115+
client = await new_horreum_client(base_url, credentials=HorreumCredentials(username=username, password=password))
116+
117+
if cleanup_data:
118+
await delete_all(client)
119+
120+
await setup_roadrunner_test(client)
121+
122+
# check data is properly injected in the server
123+
get_schemas = await client.raw_client.api.schema.get()
124+
assert get_schemas.count == 2
125+
126+
get_tests = await client.raw_client.api.test.get()
127+
assert get_tests.count == 1
128+
129+
get_runs = await client.raw_client.api.run.list_.get()
130+
assert get_runs.total == 1
131+
132+
133+
if __name__ == '__main__':
134+
asyncio.run(example())
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "ACME Benchmark Schema",
3+
"description": "Data produced by benchmarking tool",
4+
"owner": "dev-team",
5+
"access": "PUBLIC",
6+
"uri": "urn:acme:benchmark:0.1"
7+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "ACME Horreum Schema",
3+
"description": "Used in Datasets",
4+
"owner": "dev-team",
5+
"access": "PUBLIC",
6+
"uri": "urn:acme:horreum:0.1"
7+
}

examples/data/acme_transformer.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "Acme Transformer",
3+
"description": "Transformer for converting complex runs into individual datasets",
4+
"owner": "dev-team",
5+
"access": "PUBLIC",
6+
"target_schema_uri": "urn:acme:horreum:0.1",
7+
"function": "({results, hash}) => results.map(r => ({ ...r, hash }))"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"name": "hash",
4+
"jsonpath": "$.buildHash",
5+
"isarray": false
6+
},
7+
{
8+
"name": "results",
9+
"jsonpath": "$.results",
10+
"isarray": false
11+
}
12+
]

examples/data/roadrunner_run.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"description": "Example run of Roadrunner",
3+
"owner": "dev-team",
4+
"access": "PUBLIC",
5+
"start": 1669388931000,
6+
"stop": 1669388932000
7+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "urn:acme:benchmark:0.1",
3+
"something": "This gets lost by the transformer",
4+
"buildHash": "defec8eddeadbeafcafebabeb16b00b5",
5+
"results": [
6+
{
7+
"test": "Foo",
8+
"requests": 123,
9+
"duration": 10
10+
},
11+
{
12+
"test": "Bar",
13+
"requests": 456,
14+
"duration": 20
15+
}
16+
]
17+
}

examples/data/roadrunner_test.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "Roadrunner Test",
3+
"description": "acme.com benchmark",
4+
"owner": "dev-team",
5+
"access": "PUBLIC",
6+
"fingerprint_labels": [ "benchmark_test" ]
7+
}

examples/read_only_example.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import asyncio
2+
3+
import httpx
4+
5+
from horreum import new_horreum_client, ClientConfiguration
6+
7+
DEFAULT_CONNECTION_TIMEOUT: int = 30
8+
DEFAULT_REQUEST_TIMEOUT: int = 100
9+
10+
base_url = "http://localhost:8080"
11+
username = "user"
12+
password = "secret"
13+
14+
expected_server_version = "0.13.0"
15+
expected_n_schemas = 2
16+
expected_n_tests = 1
17+
enable_assertions = False
18+
19+
20+
async def example():
21+
timeout = httpx.Timeout(DEFAULT_REQUEST_TIMEOUT, connect=DEFAULT_CONNECTION_TIMEOUT)
22+
http_client = httpx.AsyncClient(timeout=timeout, http2=True, verify=False)
23+
client = await new_horreum_client(base_url, client_config=ClientConfiguration(http_client=http_client))
24+
25+
server_version = await client.raw_client.api.config.version.get()
26+
print(server_version)
27+
if enable_assertions:
28+
assert server_version.version == expected_server_version
29+
30+
get_schemas = await client.raw_client.api.schema.get()
31+
print(get_schemas.count)
32+
if enable_assertions:
33+
assert get_schemas.count == expected_n_schemas
34+
35+
get_tests = await client.raw_client.api.test.get()
36+
print(get_tests.count)
37+
if enable_assertions:
38+
assert get_tests.count == expected_n_tests
39+
40+
41+
if __name__ == '__main__':
42+
asyncio.run(example())

src/horreum/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
from horreum.configs import HorreumCredentials, ClientConfiguration
12
from horreum.horreum_client import new_horreum_client
23

34
__all__ = [
4-
new_horreum_client
5+
new_horreum_client,
6+
HorreumCredentials,
7+
ClientConfiguration
58
]

src/horreum/configs.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from dataclasses import dataclass
2+
from typing import Optional
3+
4+
import httpx
5+
from kiota_abstractions.request_option import RequestOption
6+
7+
8+
@dataclass(frozen=True)
9+
class HorreumCredentials:
10+
username: str = None
11+
password: str = None
12+
13+
14+
@dataclass
15+
class ClientConfiguration:
16+
# inner http async client that will be used to perform raw requests
17+
http_client: Optional[httpx.AsyncClient] = None
18+
# if true, set default middleware on the provided client
19+
use_default_middlewares: bool = True
20+
# if set use these options for default middlewares
21+
options: Optional[dict[str, RequestOption]] = None

0 commit comments

Comments
 (0)