com.spotify.apollo.route.Versions Maven / Gradle / Ivy
The newest version!
/*
* -\-\-
* Spotify Apollo Extra
* --
* Copyright (C) 2013 - 2015 Spotify AB
* --
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -/-/-
*/
package com.spotify.apollo.route;
import com.google.common.annotations.VisibleForTesting;
import com.spotify.apollo.Response;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import okio.ByteString;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* Allows you to expand a stream of VersionedRoutes to a stream of Routes. Expanding a single
* com.spotify.apollo.route.VersionedRoute to a Stream of Routes means prefixing the base Route's URI with '/vN' for each N
* that the in the range [startVersion, lastVersion] where the com.spotify.apollo.route.VersionedRoute is valid and returning
* new Routes with the new URIs.
*
* Example:
*
* {@code
* com.spotify.apollo.route.Versions versions = com.spotify.apollo.route.Versions.from(0).to(2);
*
* com.spotify.apollo.route.VersionedRoute vr = com.spotify.apollo.route.VersionedRoute.of(route).removedIn(2);
*
* environment.routingEngine()
* .registerRoutes(versions.expand(Stream.of(vr)))
* .registerRoutes(versions.expand(myResource.versionedRoutes()));
* }
*/
public class Versions {
private final int startVersion;
private final int lastVersion;
public Versions(int startVersion, int lastVersion) {
this.startVersion = startVersion;
this.lastVersion = lastVersion;
}
public Stream>>> expand(
Stream versionedRouteStream) {
List>>> routes = expandToRoutes(versionedRouteStream);
sanityCheck(routes);
return routes.stream();
}
private List>>> expandToRoutes(
Stream versionedRouteStream) {
return versionedRouteStream.flatMap(
versionedRoute -> {
int lowerBound = Math.max(startVersion, versionedRoute.validFrom());
int upperBoundExclusive = versionedRoute.removedIn().orElse(lastVersion + 1);
Route>> route = versionedRoute.route();
return IntStream.range(lowerBound, upperBoundExclusive).mapToObj(
i ->
route.copy(
route.method(),
"/v" + String.valueOf(i) + (route.uri().startsWith("/") ? "" : "/") + route
.uri(),
route.handler(),
route.docString().orElse(null)))
;
}
).collect(toList());
}
@VisibleForTesting
static String methodUri(Route> route) {
return route.method() + " " + route.uri();
}
private void sanityCheck(List>>> routes) {
Map methodUriCounts = routes.stream()
.collect(Collectors.groupingBy(Versions::methodUri, counting()));
Set overlappingMethodUris =
methodUriCounts.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(toSet());
if (!overlappingMethodUris.isEmpty()) {
throw new IllegalArgumentException(
"versioned routes overlap for the following method/uris: " + overlappingMethodUris);
}
}
public static NeedsTo from(int startVersionInclusive) {
return new NeedsTo(startVersionInclusive);
}
public static class NeedsTo {
private final int startVersion;
public NeedsTo(int startVersion) {
this.startVersion = startVersion;
}
public Versions to(int lastVersionInclusive) {
return new Versions(startVersion, lastVersionInclusive);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy