private static Route createRoute(final ActorSystem actorSystem, final ActorRef healthCheckingActor) { final StatusRoute statusRoute = new StatusRoute(new ClusterStatusSupplier(Cluster.get(actorSystem)), healthCheckingActor, actorSystem); return logRequest("http-request", () -> logResult("http-response", statusRoute::buildStatusRoute)); }
/** * Builds the {@code /status} route. * * @return the {@code /status} route. */ public Route buildStatusRoute() { return pathPrefix(PATH_STATUS, () -> // /status/* get(() -> // GET route( pathEndOrSingleSlash(() -> // /status complete( HttpResponse.create().withStatus(StatusCodes.OK) .withEntity(ContentTypes.APPLICATION_JSON, Status.provideStaticStatus().toString()) ) ), path(PATH_HEALTH, healthRouteSupplier), // /status/health path(PATH_CLUSTER, () -> complete( // /status/cluster HttpResponse.create().withStatus(StatusCodes.OK) .withEntity(ContentTypes.APPLICATION_JSON, clusterStateSupplier.get().toJson().toString())) ) ) ).orElse(complete(StatusCodes.METHOD_NOT_ALLOWED))); }
/** * Builds the {@code /status} route. * * @return the {@code /status} route. */ public Route buildOverallStatusRoute() { return rawPathPrefix(mergeDoubleSlashes().concat(PATH_OVERALL), () -> // /overall/* authenticateDevopsBasic(REALM_DEVOPS, get(() -> // GET // /overall/status // /overall/status/health // /overall/status/cluster rawPathPrefix(mergeDoubleSlashes().concat(PATH_STATUS), () -> route( // /status pathEndOrSingleSlash( () -> completeWithFuture(createOverallStatusResponse())), // /status/health path(PATH_HEALTH, () -> completeWithFuture(createOverallHealthResponse())), path(PATH_CLUSTER, () -> complete( // /status/cluster HttpResponse.create().withStatus(StatusCodes.OK) .withEntity(ContentTypes.APPLICATION_JSON, clusterStateSupplier.get().toJson().toString())) ) )) ))); }
/** * Builds the {@code /health} route. * * @return the {@code /health} route. */ public Route buildHealthRoute() { return path(PATH_HEALTH, () -> // /health get(() -> // GET completeWithFuture(createOverallHealthResponse()) ) ); }
/** * Builds the {@code /ws} route. * * @return the {@code /ws} route. */ public Route buildWebsocketRoute(final Integer version, final String correlationId, final AuthorizationContext connectionAuthContext, final DittoHeaders additionalHeaders, final ProtocolAdapter chosenProtocolAdapter) { return extractUpgradeToWebSocket(upgradeToWebSocket -> complete( createWebsocket(upgradeToWebSocket, version, correlationId, connectionAuthContext, additionalHeaders, chosenProtocolAdapter) ) ); }
public Route buildStatsRoute(final String correlationId) { return Directives.rawPathPrefix(CustomPathMatchers.mergeDoubleSlashes().concat(STATISTICS_PATH_PREFIX), () -> // /stats/* extractRequestContext(ctx -> get(() -> // GET buildSubRoutes(ctx, correlationId) ) ) ); }
private Route thingsEntryPolicyId(final RequestContext ctx, final DittoHeaders dittoHeaders, final String thingId) { return path(PATH_POLICY_ID, () -> // /things/<thingId>/policyId route( get(() -> // GET /things/<thingId>/policyId handlePerRequest(ctx, RetrievePolicyId.of(thingId, dittoHeaders)) ), put(() -> // GET /things/<thingId>/policyId extractDataBytes(payloadSource -> handlePerRequest(ctx, dittoHeaders, payloadSource, policyIdJson -> ModifyPolicyId.of(thingId, Optional.of(JsonFactory.readFrom(policyIdJson)) .filter(JsonValue::isString) .map(JsonValue::asString) .orElse(policyIdJson), dittoHeaders) ) ) ) ) ); }
logRequest("request", Logging.InfoLevel(), () -> path(segment("products").slash(longSegment()), (productId) -> get(() -> onComplete(lookupProduct(productId), (Try<DbActor.ProductResult> result) -> { if (result.isFailure()) { return complete(StatusCodes.SERVICE_UNAVAILABLE); } else { final DbActor.ProductResult productResult = result.get(); if (productResult.product.isPresent()) { return completeOK(productResult.product.get(), productMarshaller); } else { return complete(StatusCodes.NOT_FOUND);
final Supplier<Route> innerWithAkkaLoggingRoute = () -> logRequest("http-request", () -> logResult("http-response", inner)); return extractRequestContext(requestContext -> { final HttpRequest request = requestContext.getRequest(); final String requestMethod = request.method().name(); final String requestUri = request.getUri().toRelative().toString(); return mapRouteResult( routeResult -> DirectivesLoggingUtils.enhanceLogWithCorrelationId(correlationId, () -> {
private Route things(final RequestContext ctx, final DittoHeaders dittoHeaders) { return pathEndOrSingleSlash(() -> route( // get(() -> // GET /things?ids=<idsString>&fields=<fieldsString> buildRetrieveThingsRoute(ctx, dittoHeaders) ), post(() -> // POST /things extractDataBytes(payloadSource -> handlePerRequest(ctx, dittoHeaders, payloadSource, thingJson -> CreateThing.of(createThingForPost(thingJson), createInlinePolicyJson(thingJson), getCopyPolicyFrom(thingJson), dittoHeaders) ) ) ) ) ); }
/** * Enables CORS - Cross-Origin Resource Sharing - for the wrapped {@code inner} Route. * * @param inner the inner route to be wrapped with the CORS enabling * @return the new route wrapping {@code inner} with the CORS enabling */ public static Route enableCors(final Supplier<Route> inner) { return extractActorSystem(actorSystem -> { final boolean enableCors = actorSystem.settings().config().getBoolean(ConfigKeys.ENABLE_CORS); if (enableCors) { return Directives.optionalHeaderValueByType(AccessControlRequestHeaders.class, corsRequestHeaders -> { final ArrayList<HttpHeader> newHeaders = new ArrayList<>(CORS_HEADERS); corsRequestHeaders.ifPresent(toAdd -> newHeaders.add(AccessControlAllowHeaders.create( StreamSupport.stream(toAdd.getHeaders().spliterator(), false) .toArray(String[]::new)) ) ); return route( options(() -> complete(HttpResponse.create().withStatus(StatusCodes.OK).addHeaders(newHeaders)) ), respondWithHeaders(newHeaders, inner) ); }); } else { return inner.get(); } }); }
/** * Builds a Route for a HTTP Prometheus endpoint where Prometheus can scrape metrics from. * * @param prometheusReporter the PrometheusReporter to retrieve the {@code scrapeData} from * @return the Prometheus Route */ public static Route buildPrometheusReporterRoute(final PrometheusReporter prometheusReporter) { return get(() -> complete(HttpResponse.create() .withStatus(StatusCodes.OK) .withEntity(CONTENT_TYPE, ByteString.fromString(buildMetricsString(prometheusReporter))) ) ); }
private Route buildSubRoutes(final RequestContext ctx, final String correlationId) { return route( pathPrefix(THINGS_PATH, () -> // /stats/things route( path(DETAILS_PATH, () -> authenticateDevopsBasic(REALM_DEVOPS, handleDevOpsPerRequest(ctx, RetrieveStatisticsDetails.of( buildDevOpsDittoHeaders(correlationId)))) ), pathEndOrSingleSlash(() -> handleDevOpsPerRequest(ctx, RetrieveStatistics.of( buildDevOpsDittoHeaders(correlationId))) ) ) ), path(SEARCH_PATH, () -> // /stats/search handleSudoCountThingsPerRequest(ctx, SudoCountThings.of( buildDevOpsDittoHeaders(correlationId))) ) ); }
/** * Describes {@code /things} SSE route. * * @return {@code /things} SSE route. */ @SuppressWarnings("squid:S1172") // allow unused ctx-Param in order to have a consistent route-"interface" public Route buildThingsSseRoute(final RequestContext ctx, final Supplier<DittoHeaders> dittoHeadersSupplier) { return rawPathPrefix(mergeDoubleSlashes().concat(PATH_THINGS), () -> pathEndOrSingleSlash(() -> get(() -> headerValuePF(AcceptHeaderExtractor.INSTANCE, accept -> doBuildThingsSseRoute(dittoHeadersSupplier.get()) ) ) ) ); }
/** * Builds the {@code /search} route. * * @return the {@code /search}} route. */ public Route buildSearchRoute(final RequestContext ctx, final DittoHeaders dittoHeaders) { return Directives.rawPathPrefix(CustomPathMatchers.mergeDoubleSlashes().concat(PATH_SEARCH), () -> Directives.rawPathPrefix(CustomPathMatchers.mergeDoubleSlashes().concat(PATH_THINGS), () -> // /search/things Directives.route( // /search/things/count path(PATH_COUNT, () -> countThings(ctx, dittoHeaders)), // /search/things pathEndOrSingleSlash(() -> searchThings(ctx, dittoHeaders)) ) ) ); }
@Override public Route unauthorized(final String correlationId) { return Directives.complete(StatusCodes.UNAUTHORIZED); }
/** * Authenticates with the Basic Authentication. * * @param realm the realm to apply * @param inner the inner route, which will be performed on successful authentication * @return the inner route wrapped with authentication */ public static Route authenticateDevopsBasic(final String realm, final Route inner) { return Directives.extractActorSystem(actorSystem -> { final Config config = actorSystem.settings().config(); if (REALM_DEVOPS.equals(realm)) { final boolean devopsSecureStatus = config.getBoolean(ConfigKeys.DEVOPS_SECURE_STATUS); if (!devopsSecureStatus) { LOGGER.warn("DevOps resource is not secured by BasicAuth"); return inner; } final String devOpsPassword = config.getString(ConfigKeys.SECRETS_DEVOPS_PASSWORD); LOGGER.debug("Devops authentication is enabled."); return Directives.authenticateBasic(REALM_DEVOPS, new Authenticator(USER_DEVOPS, devOpsPassword), userName -> inner); } else { LOGGER.warn("Did not know realm '{}'. NOT letting the inner Route pass ..", realm); return Directives.complete(StatusCodes.UNAUTHORIZED); } }); }
private Route countThings(final RequestContext ctx, final DittoHeaders dittoHeaders) { return get(() -> // GET things/count?filter=<filterString>&namespaces=<namespacesString> parameterOptional(ThingSearchParameter.FILTER.toString(), filterString -> parameterOptional(ThingSearchParameter.NAMESPACES.toString(), namespacesString -> handlePerRequest(ctx, CountThings.of(calculateFilter(filterString), calculateNamespaces(namespacesString), dittoHeaders)) ) ) ); }
private Route claimMessages(final RequestContext ctx, final DittoHeaders dittoHeaders, final String thingId) { return rawPathPrefix(mergeDoubleSlashes().concat(PATH_INBOX), () -> // /inbox rawPathPrefix(mergeDoubleSlashes().concat(PATH_CLAIM), () -> // /inbox/claim post(() -> pathEndOrSingleSlash(() -> parameterOptional(Unmarshaller.sync(Long::parseLong), TIMEOUT_PARAMETER, optionalTimeout -> withCustomRequestTimeout(optionalTimeout, this::checkClaimTimeout, defaultClaimTimeout, timeout -> extractDataBytes(payloadSource -> handleMessage(ctx, payloadSource, buildSendClaimMessage( ctx, dittoHeaders, thingId, timeout ) ) ) ) ) ) ) ) ); }
public static Route ensureEncoding(final String correlationId, final Supplier<Route> inner) { return extractRequestContext(requestContext -> enhanceLogWithCorrelationId(correlationId, () -> { final Uri uri = requestContext.getRequest().getUri(); try { // per default, Akka evaluates the query params "lazily" in the routes and throws an IllegalUriException // in case of error; we evaluate the query params explicitly here to be able to handle this error at // a central location uri.query(); } catch (final IllegalUriException e) { LOGGER.debug("URI parsing failed", e); final String rawRequestUri = HttpUtils.getRawRequestUri(requestContext.getRequest()); final String message = MessageFormat.format(URI_INVALID_TEMPLATE, rawRequestUri); return complete(StatusCodes.BAD_REQUEST, message); } return inner.get(); })); } }