Attribute Macro conjure_endpoints

Source
#[conjure_endpoints]
Expand description

Creates a Conjure service type wrapping types implementing the annotated trait.

For a trait named MyService, the macro will create a type named MyServiceEndpoints which implements the conjure Service trait.

The attribute has a parameter:

  • name - The value returned from the EndpointMetadata::service_name method. Defaults to the trait name.

§Parameters

The trait can optionally be declared generic over the request body and response writer types by using the #[request_body] and #[response_writer] annotations on the type parameters.

§Endpoints

Each method corresponds to a separate HTTP endpoint, and is expected to take &self and return Result<T, Error>. Each must be annotated with #[endpoint], which has several parameters:

  • method - The HTTP method (e.g. GET). Required.
  • path - The HTTP path template. Path parameters should be identified by {name} and must make up an entire path component. Required.
  • name - The value returned from the EndpointMetadata::name method. Defaults to the method name.
  • produces - A type implementing SerializeResponse which will be used to convert the value returned by the method into a response. Defaults to EmptyResponseSerializer.

Each method argument must have an annotation describing the type of parameter. One of:

  • #[path] - A path parameter.

    Parameters:

    • name - The name of the path template parameter. Defaults to the argument name.
    • decoder - A type implementing DecodeParam which will be used to decode the value. Defaults to FromStrDecoder.
    • safe - If set, the parameter will be added to the SafeParams response extension.
    • log_as - The name of the parameter used in request logging and error reporting. Defaults to the argument name.
  • #[query] - A query parameter.

    Parameters:

    • name - The string used as the key in the encoded URI. Required.
    • decoder - A type implementing DecodeParam which will be used to decode the value. Defaults to FromStrDecoder.
    • safe - If set, the parameter will be added to the SafeParams response extension.
    • log_as - The name of the parameter used in request logging and error reporting. Defaults to the argument name.
  • #[auth] - A BearerToken used to authenticate the request.

    Parameters:

    • cookie_name - The name of the cookie if the token is to be parsed from a Cookie header. If unset, it will be parsed from an Authorization header instead.
  • #[header] - A header parameter.

    Parameters:

    • name - The header name. Required.
    • decoder - A type implementing DecodeHeader which will be used to decode the value. Defaults to FromStrDecoder.
    • safe - If set, the parameter will be added to the SafeParams response extension.
    • log_as - The name of the parameter used in request logging and error reporting. Defaults to the argument name.
  • #[body] - The request body.

    Parameters:

    • deserializer - A type implementing DeserializeRequest which will be used to deserialize the request body into a value. Defaults to StdRequestDeserializer.
    • safe - If set, the parameter will be added to the SafeParams response extension.
    • log_as - The name of the parameter used in request logging and error reporting. Defaults to the argument name.
  • #[context] - A RequestContext which provides lower level access to the request.

§Async

Both blocking and async services are supported. For technical reasons, async method definitions will be rewritten by the macro to require the returned future be Send.

§Examples

use conjure_error::Error;
use conjure_http::{conjure_endpoints, endpoint};
use conjure_http::server::{
    ConjureRuntime, DeserializeRequest, FromStrOptionDecoder, ResponseBody, SerializeResponse,
    StdResponseSerializer, WriteBody,
};
use conjure_object::BearerToken;
use http::Response;
use http::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
use std::io::Write;

#[conjure_endpoints]
trait MyService {
    #[endpoint(method = GET, path = "/yaks/{yak_id}", produces = StdResponseSerializer)]
    fn get_yak(
        &self,
        #[auth] auth: BearerToken,
        #[path(safe)] yak_id: i32,
    ) -> Result<String, Error>;

    #[endpoint(method = POST, path = "/yaks")]
    fn create_yak(
        &self,
        #[auth] auth: BearerToken,
        #[query(name = "parentName", decoder = FromStrOptionDecoder)] parent_id: Option<String>,
        #[body] yak: String,
    ) -> Result<(), Error>;
}

#[conjure_endpoints]
trait AsyncMyService {
    #[endpoint(method = GET, path = "/yaks/{yak_id}", produces = StdResponseSerializer)]
    async fn get_yak(
        &self,
        #[auth] auth: BearerToken,
        #[path(safe)] yak_id: i32,
    ) -> Result<String, Error>;

    #[endpoint(method = POST, path = "/yaks")]
    async fn create_yak(
        &self,
        #[auth] auth: BearerToken,
        #[query(name = "parentName", decoder = FromStrOptionDecoder)] parent_id: Option<String>,
        #[body] yak: String,
    ) -> Result<(), Error>;
}

#[conjure_endpoints]
trait MyStreamingService<#[request_body] I, #[response_writer] O>
where
    O: Write,
{
    #[endpoint(method = POST, path = "/streamData")]
    fn receive_stream(
        &self,
        #[body(deserializer = StreamingRequestDeserializer)] body: I,
    )  -> Result<(), Error>;

    #[endpoint(method = GET, path = "/streamData", produces = StreamingResponseSerializer)]
    fn stream_response(&self) -> Result<StreamingResponse, Error>;
}

struct StreamingRequestDeserializer;

impl<I> DeserializeRequest<I, I> for StreamingRequestDeserializer {
    fn deserialize(
        _runtime: &ConjureRuntime,
        _headers: &HeaderMap,
        body: I,
    ) -> Result<I, Error> {
        Ok(body)
    }
}

struct StreamingResponse;

impl<O> WriteBody<O> for StreamingResponse
where
    O: Write,
{
    fn write_body(self: Box<Self>, w: &mut O) -> Result<(), Error> {
        // ...
        Ok(())
    }
}

struct StreamingResponseSerializer;

impl<O> SerializeResponse<StreamingResponse, O> for StreamingResponseSerializer
where
    O: Write,
{
    fn serialize(
        _runtime: &ConjureRuntime,
        _request_headers: &HeaderMap,
        body: StreamingResponse,
    ) -> Result<Response<ResponseBody<O>>, Error> {
        let mut response = Response::new(ResponseBody::Streaming(Box::new(body)));
        response.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_static("text/plain"));
        Ok(response)
    }
}