Registers a new node, service or check

        Parameters:
            node (Object): Node definition
            check (Object): Check definition
            service (Object or ObjectID): Service
            write_token (ObjectID): Token ID
        Returns:
            bool: ``True`` on success

        .. note:: it is usually preferrable instead to use the agent
                  endpoints for registration as they are simpler and
                  perform anti-entropy.

        **Node** body must look something like::

            {
                "Datacenter": "dc1",
                "Node": "foobar",
                "Address": "192.168.10.10",
                "TaggedAddresses": {
                    "lan": "192.168.10.10",
                    "wan": "10.0.10.10"
                }
            }

        **Service** body must look something like::

            {
                "ID": "redis1",
                "Service": "redis",
                "Tags": [
                    "master",
                    "v1"
                ],
                "Address": "127.0.0.1",
                "Port": 8000
            }

        **Check** body must look something like::

            {
                "Node": "foobar",
                "CheckID": "service:redis1",
                "Name": "Redis health check",
                "Notes": "Script based health check",
                "Status": "passing",
                "ServiceID": "redis1"
            }

        The behavior of the endpoint depends on what parameters are provided.

        **Node** and **Address** fields are required, while
        **Datacenter** will be defaulted to match that of the agent.
        If only those are provided, the endpoint will register the
        node with the catalog.
        **TaggedAddresses** can be used in conjunction with the
        translate_wan_addrs configuration option and the "wan" address.

        If the **Service** key is provided, the service will also be
        registered. If **ID** is not provided, it will be defaulted to the
        value of the **Service.Service** property. Only one service with a
        given **ID** may be present per node. The service **Tags**,
        **Address**, and **Port** fields are all optional.

        If the **Check** key is provided, a health check will also be
        registered.

        .. note:: This register API manipulates the health check entry in
                  the Catalog, but it does not setup the script, **TTL**,
                  or **HTTP** check to monitor the node's health. To truly
                  enable a new health check, the check must either be provided
                  in agent configuration or set via the agent endpoint.

        The **CheckID** can be omitted and will default to the value of
        **Name**. As with **Service.ID**, the **CheckID** must be unique on
        this node. **Notes** is an opaque field that is meant to hold
        human-readable text. If a **ServiceID** is provided that matches the
        **ID** of a service on that node, the check is treated as a service
        level health check, instead of a node level health check. The
        **Status** must be one of ``passing``, ``warning``, or ``critical``.

        Multiple checks can be provided by replacing **Check** with **Checks**
        and sending a list of Check objects.

        It is important to note that **Check** does not have to be provided
        with **Service** and vice versa. A catalog entry can have either,
        neither, or both.

        An optional ACL token may be provided to perform the registration by
        including a **WriteRequest** block in the query, like this::

            {
                "WriteRequest": {
                    "Token": "foo"
                }
            }

async def register(self, node, *,
                       check=None, service=None, write_token=None):
        """Registers a new node, service or check

        Parameters:
            node (Object): Node definition
            check (Object): Check definition
            service (Object or ObjectID): Service
            write_token (ObjectID): Token ID
        Returns:
            bool: ``True`` on success

        .. note:: it is usually preferrable instead to use the agent
                  endpoints for registration as they are simpler and
                  perform anti-entropy.

        **Node** body must look something like::

            {
                "Datacenter": "dc1",
                "Node": "foobar",
                "Address": "192.168.10.10",
                "TaggedAddresses": {
                    "lan": "192.168.10.10",
                    "wan": "10.0.10.10"
                }
            }

        **Service** body must look something like::

            {
                "ID": "redis1",
                "Service": "redis",
                "Tags": [
                    "master",
                    "v1"
                ],
                "Address": "127.0.0.1",
                "Port": 8000
            }

        **Check** body must look something like::

            {
                "Node": "foobar",
                "CheckID": "service:redis1",
                "Name": "Redis health check",
                "Notes": "Script based health check",
                "Status": "passing",
                "ServiceID": "redis1"
            }

        The behavior of the endpoint depends on what parameters are provided.

        **Node** and **Address** fields are required, while
        **Datacenter** will be defaulted to match that of the agent.
        If only those are provided, the endpoint will register the
        node with the catalog.
        **TaggedAddresses** can be used in conjunction with the
        translate_wan_addrs configuration option and the "wan" address.

        If the **Service** key is provided, the service will also be
        registered. If **ID** is not provided, it will be defaulted to the
        value of the **Service.Service** property. Only one service with a
        given **ID** may be present per node. The service **Tags**,
        **Address**, and **Port** fields are all optional.

        If the **Check** key is provided, a health check will also be
        registered.

        .. note:: This register API manipulates the health check entry in
                  the Catalog, but it does not setup the script, **TTL**,
                  or **HTTP** check to monitor the node's health. To truly
                  enable a new health check, the check must either be provided
                  in agent configuration or set via the agent endpoint.

        The **CheckID** can be omitted and will default to the value of
        **Name**. As with **Service.ID**, the **CheckID** must be unique on
        this node. **Notes** is an opaque field that is meant to hold
        human-readable text. If a **ServiceID** is provided that matches the
        **ID** of a service on that node, the check is treated as a service
        level health check, instead of a node level health check. The
        **Status** must be one of ``passing``, ``warning``, or ``critical``.

        Multiple checks can be provided by replacing **Check** with **Checks**
        and sending a list of Check objects.

        It is important to note that **Check** does not have to be provided
        with **Service** and vice versa. A catalog entry can have either,
        neither, or both.

        An optional ACL token may be provided to perform the registration by
        including a **WriteRequest** block in the query, like this::

            {
                "WriteRequest": {
                    "Token": "foo"
                }
            }
        """
        node_id, node = prepare_node(node)
        service_id, service = prepare_service(service)
        _, check = prepare_check(check)
        if check and node_id:
            check["Node"] = node_id
        if check and service_id:
            check["ServiceID"] = service_id
        token = extract_attr(write_token, keys=["ID"])
        entry = node
        if service:
            entry["Service"] = service
        if check:
            entry["Check"] = check
        if token:
            entry["WriteRequest"] = {
                "Token": token
            }
        response = await self._api.put("/v1/catalog/register", data=entry)
        return response.status == 200