View Source Craftgate.Adapter (Craftgate v1.0.42)
Represents an adapter that can be used to interact with a group of related endpoints.
Contains the endpoint/2
macro which can be used to quickly declare methods to access the said endpoints.
To import this macro simply use the Craftgate.Adapter
module in your module, e.g.:
defmodule MyAdapter do
use Craftgate.Adapter
endpoint retrieve_foo(id: integer()), get: "/foo/v1/foos/:id", return: FooResponse
end
Link to this section Summary
Functions
Declares a method pair that can be used to execute a request against the specified endpoint.
Link to this section Functions
Declares a method pair that can be used to execute a request against the specified endpoint.
Of the generated methods, one carries over the specified name as-is and returns an OK/error tuple with the
desired response type (e.g. {:ok, %Craftgate.Response.PaymentResponse{...}}
or {:error, %Craftgate.Error{...}}
),
and the other contains its "bangified" implementation, which either raises an error or returns the desired type.
Both methods will have their typespecs declared automatically, and the un-bangified method will retain its @doc
statement.
Also, both methods will have an additional, optional parameter in the end called options
which can be used to specify
custom options on a particular request.
So to retrieve a resource of type Foo
with the given ID and parse it as a FooResponse
you can do:
defmodule FooAdapter do
use Adapter
endpoint retrieve_foo(id: integer()), get: "/foo/v1/foos/:id", return: FooResponse
end
Which then gets expanded to the following Elixir code
@spec retrieve_foo(integer(), keyword()) :: {:ok, FooResponse} | {:error, any()}
def retrieve_foo(id, options \\ []) do
...
end
@doc "Bangified version of the method with the same name
"
@spec retrieve_foo!(integer(), keyword()) :: FooResponse | no_return
def retrieve_foo!(id, options \\ []) do
...
end
Note that arguments are specified as a keyword list where the keys represent the argument names (which can then be expanded into
the URL via the :<arg_name>
syntax) and the values represent the types of each argument, the HTTP method and the destination
URL are specified by the get: "..."
options, and the expected successful response type is specified as the return
option.
Which will allow you to call the FooAdapter.retrieve_foo/1
method like this:
# retrieving the result as a tuple
{:ok, %FooResponse{...}} = FooAdapter.retrieve_foo(42)
# or having the errors be raised
%FooResponse{...} = FooAdapter.retrieve_foo!(42)
Note that in both cases, the argument id
will be expanded into the :id
path segment specified in the URL and the get
key will
expand into the HTTP method GET
, so in the end a GET
request will be sent to /foo/v1/foos/42
. Once the request executes, successful
results will be parsed as the struct FooResponse
, and failed results will be parsed as Craftgate.Error
.
Similarly, the body
argument will be expanded into the request body, so to create a Foo
we can declare:
defmodule FooAdapter do
...
endpoint create_foo(body: CreateFooRequest.t()), post: "/foo/v1/foos", return: FooResponse.t()
end
Which in turn can be called like as follows:
{:ok, foo_response} = FooAdapter.create_foo(%CreateFooRequest{...})
Similarly, the params
argument will be expanded into the query string:
defmodule FooAdapter do
...
endpoint search_foos(params: keyword()), get: "/foo/v1/foos", return: SearchFoosResponse.t()
end
%{items: foos} = FooAdapter.search_foos!(name: "foo", age: 42)
You can also use function blocks in the options to specify custom request parameter or bodies. Note that these functions will have the same arity as the original definition and will receive the exact same arguments as the ones passed into the function call.
This can be particularly useful to providing convenience methods to consumers without expecting them to construct full request bodies.
...
endpoint set_foo_status(id: integer(), status: FooStatus.t()),
put: "/foos/v1/foos/:id/status",
body: fn _, status -> %UpdateFooStatusRequest{new_status: status} end
...
Finally, the return
option can be left out to capture empty responses while still being able to identify failures as Craftgate.Error
structs.
This can be particularly useful for DELETE endpoints which typically return an empty response body.
So in the end, a set of CRUD operations for a resource Foo
can easily be declares as follows:
defmodule FooAdapter do
use Adapter
endpoint create_foo(body: CreateFooRequest.t()), post: "/foo/v1/foos", return: FooResponse.t()
endpoint retrieve_foo(id: integer()), get: "/foo/v1/foos/:id", return: FooResponse.t()
endpoint update_foo(id: integer(), body: UpdateFooRequest.t()), put: "/foo/v1/foos/:id", return: FooResponse.t()
endpoint set_foo_status(id: integer(), status: FooStatus.t()),
put: "/foos/v1/foos/:id/status",
body: fn _, status -> %UpdateFooStatusRequest{new_status: status} end
endpoint delete_foo(id: integer()), delete: "/foo/v1/foos/:id"
endpoint search_foos(params: keyword()), get: "/foo/v1/foos", return: SearchFoosResponse.t()
end