// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/TestConfigurationHolder.java
// @Data
// @Builder
// public class TestConfigurationHolder {
// 	public static final String TEST_COMMUNICATION_TYPE_HEADER_NAME = "TEST-COMMUNICATION-TYPE";
// 	public static final ThreadLocal<TestConfigurationHolder> TEST_CONFIG = new ThreadLocal<>();
// 
// 	private TestCommunicationType testCommunicationType;
// 
// 	public enum TestCommunicationType {
// 		FEIGN, REST_TEMPLATE
// 	}
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/Event.java
// @Data
// public class Event implements Serializable {
// 	private String processId;
// 	private EventType eventType;
// 	private LocalDateTime eventTime = LocalDateTime.now();
// 
// 	public Event(String processId, EventType eventType) {
// 		this.processId = processId;
// 		this.eventType = eventType;
// 	}
// 
// 	public Event() {}
// 
// 	public static EventBuilder builder() {
// 		return new EventBuilder();
// 	}
// 
// 	public static class EventBuilder {
// 
// 		private String processId;
// 		private EventType eventType;
// 
// 		public EventBuilder eventType(EventType eventType) {
// 			this.eventType = eventType;
// 			return this;
// 		}
// 
// 		public EventBuilder processId(String processId) {
// 			this.processId = processId;
// 			return this;
// 		}
// 
// 		public Event build() {
// 			return new Event(this.processId, this.eventType);
// 		}
// 
// 	}
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/EventGateway.java
// @MessagingGateway
// public interface EventGateway {
// 
// 	@Gateway(requestChannel=EventSource.OUTPUT)
// 	void emitEvent(Event event);
// 
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/EventType.java
// public enum EventType {
// 	INGREDIENTS_ORDERED,
// 	BREWING_STARTED,
// 	BEER_MATURED,
// 	BEER_BOTTLED
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Ingredient.java
// @Data
// public class Ingredient {
//     private IngredientType type;
//     private Integer quantity;
// 
//     public Ingredient(IngredientType type, Integer quantity) {
//         this.type = type;
//         this.quantity = quantity;
//     }
// 
//     public Ingredient() {
//     }
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Ingredients.java
// @ToString
// public class Ingredients {
// 
//     public Ingredients() {
//     }
// 
//     public Ingredients(Iterable<Ingredient> ingredients) {
//         this.ingredients = Lists.newArrayList(ingredients);
//     }
// 
//     public List<Ingredient> ingredients = Lists.newArrayList();
// 
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Order.java
// @Data
// public class Order {
//     private List<IngredientType> items = new ArrayList<>();
// }


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.instrument.async.TraceableExecutorService;
import org.springframework.stereotype.Component;
import io.spring.cloud.samples.brewery.common.TestConfigurationHolder;
import io.spring.cloud.samples.brewery.common.events.Event;
import io.spring.cloud.samples.brewery.common.events.EventGateway;
import io.spring.cloud.samples.brewery.common.events.EventType;
import io.spring.cloud.samples.brewery.common.model.Ingredient;
import io.spring.cloud.samples.brewery.common.model.Ingredients;
import io.spring.cloud.samples.brewery.common.model.Order;
import lombok.extern.slf4j.Slf4j;
        this.maturingUpdater = maturingServiceUpdater;
        this.tracer = tracer;
        this.eventGateway = eventGateway;
        this.traceKeys = traceKeys;
        this.spanNamer = spanNamer;
    }

    // TODO: Consider simplifying the case by removing the DB (always matches threshold)
    public Ingredients fetchIngredients(Order order, String processId, TestConfigurationHolder testConfigurationHolder) throws Exception {
        TestConfigurationHolder.TEST_CONFIG.set(testConfigurationHolder);
        log.info("Fetching ingredients for order [{}] , processId [{}], span [{}]", order, processId);
        /**
         * [SLEUTH] ParallelStreams won't work out of the box
         * - example of a completable future with our TraceableExecutorService
         * - makes little business sense here but that's just an example
         */
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
                    TestConfigurationHolder.TEST_CONFIG.set(testConfigurationHolder);
                    ingredientsCollector.collectIngredients(order, processId).stream()
                            .filter(ingredient -> ingredient != null)
                            .forEach((Ingredient ingredient) -> {
                                log.info("Adding an ingredient [{}] for order [{}] , processId [{}]", ingredient);
                                ingredientWarehouse.addIngredient(ingredient);
                            });
                    return null;
                }, new TraceableExecutorService(Executors.newFixedThreadPool(5),
                tracer, traceKeys, spanNamer, "fetchIngredients"));
        // block to perform the request (as I said the example is stupid)
        completableFuture.get();
        eventGateway.emitEvent(Event.builder().eventType(EventType.INGREDIENTS_ORDERED).processId(processId).build());
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/TestConfigurationHolder.java
// @Data
// @Builder
// public class TestConfigurationHolder {
// 	public static final String TEST_COMMUNICATION_TYPE_HEADER_NAME = "TEST-COMMUNICATION-TYPE";
// 	public static final ThreadLocal<TestConfigurationHolder> TEST_CONFIG = new ThreadLocal<>();
// 
// 	private TestCommunicationType testCommunicationType;
// 
// 	public enum TestCommunicationType {
// 		FEIGN, REST_TEMPLATE
// 	}
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/Event.java
// @Data
// public class Event implements Serializable {
// 	private String processId;
// 	private EventType eventType;
// 	private LocalDateTime eventTime = LocalDateTime.now();
// 
// 	public Event(String processId, EventType eventType) {
// 		this.processId = processId;
// 		this.eventType = eventType;
// 	}
// 
// 	public Event() {}
// 
// 	public static EventBuilder builder() {
// 		return new EventBuilder();
// 	}
// 
// 	public static class EventBuilder {
// 
// 		private String processId;
// 		private EventType eventType;
// 
// 		public EventBuilder eventType(EventType eventType) {
// 			this.eventType = eventType;
// 			return this;
// 		}
// 
// 		public EventBuilder processId(String processId) {
// 			this.processId = processId;
// 			return this;
// 		}
// 
// 		public Event build() {
// 			return new Event(this.processId, this.eventType);
// 		}
// 
// 	}
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/EventGateway.java
// @MessagingGateway
// public interface EventGateway {
// 
// 	@Gateway(requestChannel=EventSource.OUTPUT)
// 	void emitEvent(Event event);
// 
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/events/EventType.java
// public enum EventType {
// 	INGREDIENTS_ORDERED,
// 	BREWING_STARTED,
// 	BEER_MATURED,
// 	BEER_BOTTLED
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Ingredient.java
// @Data
// public class Ingredient {
//     private IngredientType type;
//     private Integer quantity;
// 
//     public Ingredient(IngredientType type, Integer quantity) {
//         this.type = type;
//         this.quantity = quantity;
//     }
// 
//     public Ingredient() {
//     }
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Ingredients.java
// @ToString
// public class Ingredients {
// 
//     public Ingredients() {
//     }
// 
//     public Ingredients(Iterable<Ingredient> ingredients) {
//         this.ingredients = Lists.newArrayList(ingredients);
//     }
// 
//     public List<Ingredient> ingredients = Lists.newArrayList();
// 
// }
// 
// Path: common/src/main/java/io/spring/cloud/samples/brewery/common/model/Order.java
// @Data
// public class Order {
//     private List<IngredientType> items = new ArrayList<>();
// }


// Path: brewing/src/main/java/io/spring/cloud/samples/brewery/aggregating/IngredientsAggregator.java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.TraceKeys;
import org.springframework.cloud.sleuth.instrument.async.TraceableExecutorService;
import org.springframework.stereotype.Component;
import io.spring.cloud.samples.brewery.common.TestConfigurationHolder;
import io.spring.cloud.samples.brewery.common.events.Event;
import io.spring.cloud.samples.brewery.common.events.EventGateway;
import io.spring.cloud.samples.brewery.common.events.EventType;
import io.spring.cloud.samples.brewery.common.model.Ingredient;
import io.spring.cloud.samples.brewery.common.model.Ingredients;
import io.spring.cloud.samples.brewery.common.model.Order;
import lombok.extern.slf4j.Slf4j;

        this.maturingUpdater = maturingServiceUpdater;
        this.tracer = tracer;
        this.eventGateway = eventGateway;
        this.traceKeys = traceKeys;
        this.spanNamer = spanNamer;
    }

    // TODO: Consider simplifying the case by removing the DB (always matches threshold)
    public Ingredients fetchIngredients(Order order, String processId, TestConfigurationHolder testConfigurationHolder) throws Exception {
        TestConfigurationHolder.TEST_CONFIG.set(testConfigurationHolder);
        log.info("Fetching ingredients for order [{}] , processId [{}], span [{}]", order, processId);
        /**
         * [SLEUTH] ParallelStreams won't work out of the box
         * - example of a completable future with our TraceableExecutorService
         * - makes little business sense here but that's just an example
         */
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
                    TestConfigurationHolder.TEST_CONFIG.set(testConfigurationHolder);
                    ingredientsCollector.collectIngredients(order, processId).stream()
                            .filter(ingredient -> ingredient != null)
                            .forEach((Ingredient ingredient) -> {
                                log.info("Adding an ingredient [{}] for order [{}] , processId [{}]", ingredient);
                                ingredientWarehouse.addIngredient(ingredient);
                            });
                    return null;
                }, new TraceableExecutorService(Executors.newFixedThreadPool(5),
                tracer, traceKeys, spanNamer, "fetchIngredients"));
        // block to perform the request (as I said the example is stupid)
        completableFuture.get();
        eventGateway.emitEvent(Event.builder().eventType(EventType.INGREDIENTS_ORDERED).processId(processId).build());
        Ingredients ingredients = ingredientWarehouse.getCurrentState();