Consuming API¶
Now the client has been instanciated, registered resources can be consumed.
Consume what is registered¶
Reusing the previously registered resource below, an instanciated client can consume only the registered route of that resource.
import blacksmith
class SearchItem(blacksmith.Request):
name_like = blacksmith.QueryStringField(alias="~name")
class Item(blacksmith.Response):
name: str
blacksmith.register(
client_name="api",
resource="item",
service="datastore",
version="v1",
path="/search",
collection_contract={
"GET": (SearchItem, Item),
},
)
It means that here that che client can consume the collection_get and
the get method of the resource. The typing system will proposed other
methods, such as post but the "POST"
schema has not been regitered,
and will raise an error.
In REST, the ~
is widely used to do a like operator, to produce a
querystring such as ?~name=…, the parameter SearchItem
as below:
items = await cli.item.collection_get({"~name": "..."})
Note
The kwargs syntax SearchItem(**{"~name": "..."})
can also be used.
Using default¶
All fields may use a default or a default factory that are serialized in http request.
class Dummy(Request):
x_message_type: str = HeaderField(default="Foo", alias="X-Message-Type")
created_at: float = PostBodyField(default_factory=time.time, alias="X-Timestamp")
In this code, the header X-Timestamp
will contains float of, time.time()
result.
And the X-Message-Type is sent with foo.
Dealing with null values¶
Lots of api will use an explicit null to patch a resource, and missing values
are not patched at all. In blacksmith, explicit None
are converted with
null, but implicit None
are not serialized to http request.
See example below:
class PatchDummy(Request):
name: str = PathInfoField()
state: Optional[str] = PostBodyField(None)
country: Optional[str] = PostBodyField(None)
blacksmith.register(
client_name="api",
resource="dummy",
service="api",
version="v1",
path="/dummies/{name}",
contract={
"PATCH": (PatchDummy, None),
},
)
sd = SyncStaticDiscovery({("api", None): "http://srv:8000/"})
cli = SyncClientFactory(sd)
api = cli("api")
# this call api call will patch the state to `null`
# but the ``country`` is not send in the request.
api.patch({"name": "foo", "state": None})
Important
Default values are always sent, so imagine the following example
class PatchDummy(Request):
name: str = PathInfoField()
state: Optional[str] = PostBodyField()
country: Optional[str] = PostBodyField(default="FR")
In that case, the PATCH will always sent a default country.
api.patch({"name": "foo", "state": None, "country": None})
will
sent a None
value for country.
default values has to be used sparingly.