package combo; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriTemplate; import java.net.URI; import java.util.Map; import java.util.stream.Stream; import static java.util.stream.Stream.generate; final class HttpCombo implements Combo { private final FactProvider factProvider; private final FactPublisher factPublisher; private final TopicSubscriber topicSubscriber; HttpCombo(final RestTemplate restTemplate) { this.factProvider = new FactProvider(restTemplate); this.factPublisher = new FactPublisher(restTemplate); this.topicSubscriber = new TopicSubscriber(restTemplate); } public <T> Stream<T> facts(final String topicName, final Class<? extends T> factClass) { final SubscriptionId subscriptionId = topicSubscriber.subscribeTo(topicName); return generate(() -> factProvider.nextFact(subscriptionId, factClass)); } public <T> void publishFact(final String topicName, final T fact) { factPublisher.publishFact(topicName, fact); } private static final class FactProvider { private final RestTemplate restTemplate; private FactProvider(final RestTemplate restTemplate) { this.restTemplate = restTemplate; } private <T> T nextFact(final SubscriptionId subscriptionId, final Class<? extends T> classOfFact) { final ResponseEntity<? extends T> response = restTemplate.getForEntity(Paths.nextFact(subscriptionId), classOfFact); if (response.getStatusCode() == HttpStatus.NO_CONTENT) { return nextFact(subscriptionId, classOfFact); } return response.getBody(); } } private static final class TopicSubscriber { private final RestTemplate restTemplate; private TopicSubscriber(final RestTemplate restTemplate) { this.restTemplate = restTemplate; } private SubscriptionId subscribeTo(final String topicName) { final ResponseEntity<Map> response = restTemplate.postForEntity(Paths.subscriptions(topicName), "", Map.class); final String comboId = (String) response.getBody().get("subscription_id"); return new SubscriptionId(topicName, comboId); } } private static final class FactPublisher { private final RestTemplate restTemplate; private FactPublisher(final RestTemplate restTemplate) { this.restTemplate = restTemplate; } private <T> void publishFact(final String topicName, final T fact) { restTemplate.postForEntity(Paths.facts(topicName), fact, Void.class); } } private static final class SubscriptionId { private final String topicName; private final String comboId; private SubscriptionId(final String topicName, final String comboId) { this.topicName = topicName; this.comboId = comboId; } private String topicName() { return topicName; } private String comboId() { return comboId; } } private static final class Paths { private static URI subscriptions(final String topicName) { return new UriTemplate("/topics/{topicName}/subscriptions").expand(topicName); } private static URI nextFact(final SubscriptionId subscriptionId) { return new UriTemplate("/topics/{topicName}/subscriptions/{subscriptionId}/next") .expand(subscriptionId.topicName(), subscriptionId.comboId()); } public static URI facts(final String topicName) { return new UriTemplate("/topics/{topicName}/facts").expand(topicName); } } }