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

com.github.tonivade.zeromock.api.HttpServiceK Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2024, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.zeromock.api;

import static com.github.tonivade.purefun.core.Function1.fail;
import static com.github.tonivade.purefun.core.Matcher1.never;
import static com.github.tonivade.purefun.core.Precondition.checkNonNull;
import static com.github.tonivade.zeromock.api.Matchers.all;
import static com.github.tonivade.zeromock.api.Matchers.startsWith;
import static com.github.tonivade.zeromock.api.PreFilterK.filter;
import static com.github.tonivade.zeromock.api.Responses.notFound;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.core.Function1;
import com.github.tonivade.purefun.core.Matcher1;
import com.github.tonivade.purefun.core.PartialFunction1;
import com.github.tonivade.purefun.type.Either;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.OptionOf;
import com.github.tonivade.purefun.typeclasses.For;
import com.github.tonivade.purefun.typeclasses.Instances;
import com.github.tonivade.purefun.typeclasses.Monad;

public final class HttpServiceK> implements HttpRouteBuilderK> {

  private final String name;
  private final Monad monad;
  private final PartialFunction1> mappings;
  private final Function1>> preFilters;
  private final Function1> postFilters;

  public HttpServiceK(String name, Monad monad) {
    this(name, monad,
        PartialFunction1.of(never(), fail(IllegalStateException::new)),
        request -> monad.pure(Either.right(request)),
        monad::pure);
  }

  private HttpServiceK(String name, Monad monad,
                       PartialFunction1> mappings,
                       Function1>> preFilters,
                       Function1> postFilters) {
    this.name = checkNonNull(name);
    this.monad = checkNonNull(monad);
    this.mappings = checkNonNull(mappings);
    this.preFilters = checkNonNull(preFilters);
    this.postFilters = checkNonNull(postFilters);
  }

  public String name() {
    return name;
  }

  Monad monad() {
    return monad;
  }

  public HttpServiceK mount(String path, HttpServiceK other) {
    checkNonNull(path);
    checkNonNull(other);
    return addMapping(
        startsWith(path).and(req -> other.mappings.isDefinedAt(req.dropOneLevel())),
        req -> monad.map(other.execute(req.dropOneLevel()), option -> option.getOrElse(notFound())));
  }

  public HttpServiceK exec(RequestHandlerK handler) {
    return addMapping(all(), handler);
  }

  @Override
  public ThenStepK> when(Matcher1 matcher) {
    return new ThenStepK<>(monad, handler -> addMapping(matcher, handler));
  }

  public ThenStepK> preFilter(Matcher1 matcher) {
    return new ThenStepK<>(monad, handler -> addPreFilter(matcher, handler));
  }

  public HttpServiceK preFilter(PreFilterK filter) {
    return addPreFilter(checkNonNull(filter));
  }

  public HttpServiceK postFilter(PostFilterK filter) {
    return addPostFilter(checkNonNull(filter));
  }

  public Kind> execute(HttpRequest request) {
    Function1>> mappingsWithPostFilters =
        mappings.andThen(value -> monad.flatMap(value, postFilters::apply)).lift();

    return For.with(monad)
        .then(preFilters.apply(request))
        .flatMap(either -> either.fold(
            res -> monad.pure(Option.some(res)),
            mappingsWithPostFilters.andThen(option -> Instances.>traverse().sequence(monad, option))))
        .map(OptionOf::toOption)
        .run();
  }

  public HttpServiceK combine(HttpServiceK other) {
    checkNonNull(other);
    return new HttpServiceK<>(
        this.name + "+" + other.name,
        this.monad,
        this.mappings.orElse(other.mappings),
        this.preFilters.andThen(
            value -> monad.flatMap(value,
                either -> either.fold(
                    response -> monad.pure(Either.left(response)), other.preFilters))),
        this.postFilters.andThen(value -> monad.flatMap(value, other.postFilters))::apply
    );
  }

  public HttpServiceK addMapping(Matcher1 matcher, RequestHandlerK handler) {
    checkNonNull(matcher);
    checkNonNull(handler);
    return new HttpServiceK<>(
        this.name,
        this.monad,
        this.mappings.orElse(PartialFunction1.of(matcher, handler::apply)),
        this.preFilters,
        this.postFilters
    );
  }

  public HttpServiceK addPreFilter(Matcher1 matcher, RequestHandlerK handler) {
    return addPreFilter(filter(monad, matcher, handler));
  }

  private HttpServiceK addPreFilter(PreFilterK filter) {
    checkNonNull(filter);
    return new HttpServiceK<>(
        this.name,
        this.monad,
        this.mappings,
        this.preFilters.andThen(
            value -> monad.flatMap(value,
                either -> either.fold(
                    response -> monad.pure(Either.left(response)), filter))),
        this.postFilters
    );
  }

  private HttpServiceK addPostFilter(PostFilterK filter) {
    checkNonNull(filter);
    return new HttpServiceK<>(
        this.name,
        this.monad,
        this.mappings,
        this.preFilters,
        this.postFilters.andThen(value -> monad.flatMap(value, filter))::apply
    );
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy