All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.zero88.msa.bp.micro.discovery.GatewayServiceInvoker Maven / Gradle / Ivy

package io.github.zero88.msa.bp.micro.discovery;

import java.util.Optional;
import java.util.function.Function;

import io.github.zero88.msa.bp.dto.ErrorMessage;
import io.github.zero88.msa.bp.dto.converter.ErrorMessageConverter;
import io.github.zero88.msa.bp.dto.msg.RequestData;
import io.github.zero88.msa.bp.event.EventAction;
import io.github.zero88.msa.bp.event.EventMessage;
import io.github.zero88.msa.bp.event.EventPattern;
import io.github.zero88.msa.bp.exceptions.BlueprintException;
import io.github.zero88.msa.bp.micro.ServiceGatewayIndex.Params;
import io.github.zero88.msa.bp.micro.ServiceKind;
import io.github.zero88.msa.bp.micro.ServiceScope;
import io.github.zero88.msa.bp.micro.filter.ByPredicate;
import io.github.zero88.msa.bp.micro.metadata.ServiceNotFoundException;
import io.github.zero88.msa.bp.micro.transfomer.RecordOutput;
import io.github.zero88.msa.bp.micro.transfomer.RecordTransformer.RecordView;
import io.github.zero88.utils.Strings;
import io.reactivex.Single;
import io.vertx.core.json.JsonObject;
import io.vertx.servicediscovery.Status;

import lombok.NonNull;

/**
 * Helps invoking event service by delegating to {@code service gateway} finds service by name then executes with given
 * {@code event action} and {@code request data}
 *
 * @see Remote procedure call
 * @since 1.0.0
 */
public interface GatewayServiceInvoker extends RemoteServiceInvoker {

    /**
     * Gateway index address
     *
     * @return gateway index address
     * @since 1.0.0
     */
    @NonNull String gatewayAddress();

    /**
     * Defines service scope to discover
     *
     * @return service scope. Default: {@link ServiceScope#ALL}
     * @since 1.0.0
     */
    @NonNull
    default ServiceScope scope() {
        return ServiceScope.ALL;
    }

    /**
     * Defines service kind to discover
     *
     * @return service kind. Default: {@link ServiceKind#LOCAL}
     * @since 1.0.0
     */
    @NonNull
    default ServiceKind kind() {
        return ServiceKind.LOCAL;
    }

    /**
     * Destination name
     *
     * @return destination name
     * @since 1.0.0
     */
    @NonNull String destination();

    /**
     * Destination service pattern
     *
     * @return service pattern. Default: {@link EventPattern#REQUEST_RESPONSE}
     * @since 1.0.0
     */
    @NonNull
    default EventPattern pattern() {
        return EventPattern.REQUEST_RESPONSE;
    }

    /**
     * Do search {@code destination service}.
     *
     * @param action event action to execute
     * @return destination address
     * @since 1.0.0
     */
    default Single search(@NonNull EventAction action) {
        final RequestData searchReq = RequestData.builder()
                                                 .body(new JsonObject().put(Params.IDENTIFIER, destination()))
                                                 .filter(new JsonObject().put(Params.BY, ByPredicate.BY_NAME)
                                                                         .put(Params.STATUS, Status.UP)
                                                                         .put(Params.SCOPE, scope())
                                                                         .put(Params.KIND, kind())
                                                                         .put(Params.VIEW, RecordView.TECHNICAL)
                                                                         .put(Params.ACTION, action.action()))
                                                 .build();
        final Single invoker = invoke(gatewayAddress(), EventAction.GET_ONE, searchReq);
        return invoker.flatMap(out -> out.isError()
                                      ? Single.error(notFound().apply(out.getError()))
                                      : Single.just(Optional.ofNullable(out.getData()).orElse(new JsonObject())))
                      .map(json -> json.getString(RecordOutput.Fields.location))
                      .filter(Strings::isNotBlank)
                      .switchIfEmpty(Single.error(new ServiceNotFoundException(
                          "Not found destination address of service name '" + destination() + "'")));
    }

    /**
     * Do invoke remote service
     *
     * @param action given event action
     * @param data   given request body data
     * @return single result from remote service
     * @apiNote result can be single error if remote service not found or not compatible with given event action
     * @apiNote It is equivalent to call {@link #execute(EventAction, RequestData)} with given {@code data} will be
     *     {@code body} of {@code request data}
     * @see EventAction
     * @see RequestData
     * @since 1.0.0
     */
    default Single execute(@NonNull EventAction action, JsonObject data) {
        return execute(action, RequestData.builder().body(data).build());
    }

    /**
     * Do invoke remote service
     *
     * @param action  given event action
     * @param reqData given request data
     * @return json result can be single error if remote service not found or not compatible with given event action
     * @apiNote Search destination address via {@link #search(EventAction)} then {@link #execute(String,
     *     EventAction, RequestData)}
     * @see EventAction
     * @see RequestData
     * @since 1.0.0
     */
    default Single execute(@NonNull EventAction action, @NonNull RequestData reqData) {
        return search(action).flatMap(address -> execute(address, action, reqData));
    }

    /**
     * Do invoke remote service.
     *
     * @param address the address
     * @param action  the action
     * @param reqData the req data
     * @return json result
     * @since 1.0.0
     */
    default Single execute(@NonNull String address, @NonNull EventAction action,
                                       @NonNull RequestData reqData) {
        return invoke(address, action, reqData).map(out -> out.isError() ? out.getError().toJson() : out.getData());
    }

    /**
     * Not found converter function.
     *
     * @return converter function
     * @since 1.0.0
     */
    default Function notFound() {
        return msg -> ErrorMessageConverter.override(msg, ServiceNotFoundException.CODE,
                                                     RemoteServiceInvoker.notFoundMessage(serviceLabel()));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy