@GET @Path("/") public void main(RakamHttpRequest request) { request.response("Rakam API is successfully installed! \n---------- \n" + "Visit app.rakam.io to register the API with Rakam BI or api.rakam.io for API documentation.") .end(); } }
private void sendNotModified(RakamHttpRequest request, File file) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED); setContentTypeHeader(response, file); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); Calendar time = new GregorianCalendar(); response.headers().set(DATE, dateFormatter.format(time.getTime())); request.response(response).end(); }
@Path("/login") @GET public void redirect(RakamHttpRequest request) { DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.wrappedBuffer(new byte[]{})); response.headers().add("Location", client.getIdentityProviderUrl()); request.response(response).end(); }
@Path("/check") @GET public void check(RakamHttpRequest request) { DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.wrappedBuffer(new byte[]{})); response.headers().add("Location", client.getIdentityProviderUrl()); request.response(response).end(); }
@Path("/") @GET public void proxy(RakamHttpRequest request, @QueryParam("u") String uri) throws InterruptedException { URI url = UriBuilder.fromUri(uri).build(); int port; if (url.getPort() != -1) { port = url.getPort(); } else if (url.getScheme().equals("http")) { port = 80; } else if (url.getScheme().equals("https")) { port = 443; } else { request.response("invalid scheme").end(); return; } Channel ch = (port == 443 ? sslBootstrap : bootstrap).connect(url.getHost(), port) .sync().channel(); ch.attr(CONNECTION_ATTR).set(request); HttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url.getRawPath()); req.headers().set(HttpHeaders.Names.HOST, url.getHost()); req.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); req.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); req.headers().set(HttpHeaders.Names.CACHE_CONTROL, HttpHeaders.Values.NO_CACHE); req.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); req.headers().set(HttpHeaders.Names.PRAGMA, HttpHeaders.Values.NO_CACHE); req.headers().set(HttpHeaders.Names.USER_AGENT, "rakam-ab-test-tool 0.1"); ch.writeAndFlush(req); }
@OPTIONS @Path("/*") public static void handle(RakamHttpRequest request) { DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.OK); response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS, "Origin, X-Requested-With, Content-Type, Accept, master_key, read_key, write_key"); response.headers().set(ACCESS_CONTROL_EXPOSE_HEADERS, "_auto_action"); response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS, PUT, DELETE"); request.response(response).end(); } }
public static void returnError(RakamHttpRequest request, String msg, HttpResponseStatus status) { ByteBuf byteBuf = Unpooled.wrappedBuffer(JsonHelper.encodeAsBytes(errorMessage(msg, status))); DefaultFullHttpResponse errResponse = new DefaultFullHttpResponse(HTTP_1_1, status, byteBuf); setBrowser(request, errResponse); request.response(errResponse).end(); }
@Path("/data") @GET @IgnoreApi public void data(RakamHttpRequest request) { Map<String, List<String>> params = request.params(); List<String> api_key = params.get("read_key"); if (api_key == null || api_key.isEmpty()) { request.response("\"read_key is missing\"", BAD_REQUEST).end(); return; } // since this endpoint is created for clients to read the ab-testing rule, // the permission is WRITE_KEY String project = apiKeyService.getProjectOfApiKey(api_key.get(0), ApiKeyService.AccessKeyType.WRITE_KEY); request.response(JsonHelper.encodeAsBytes(metadata.getReports(project))) .end(); }
@GET @ApiOperation(value = "Show webhook images", response = Integer.class) @Path("/image/*") public void image(RakamHttpRequest request) { String substring = request.path().substring("/ui/webhook/image".length() + 1); if (!substring.matches("^[A-Za-z0-9-]+$")) { throw new RakamException(FORBIDDEN); } URL resource = getClass().getResource("/webhook/" + substring + "/image.png"); if (resource == null) { throw new RakamException(NOT_FOUND); } byte[] script; try { script = ByteStreams.toByteArray(resource.openStream()); } catch (IOException e) { throw Throwables.propagate(e); } DefaultFullHttpResponse resp = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(script)); resp.headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); resp.headers().add(CACHE_CONTROL, "private, max-age=86400"); HttpHeaders.setContentLength(resp, script.length); resp.headers().set(CONTENT_TYPE, "image/png"); request.response(resp).end(); }
@Path("/check-sentry") @GET public void checkSentry(RakamHttpRequest request) { request.response(JsonHelper.encode(checkSentry()), OK).end(); }
@GET @ApiOperation(value = "List custom event mappers", response = Integer.class) @Path("/image/*") public void image(RakamHttpRequest request) { String substring = request.path().substring("/ui/custom-event-mapper/image".length() + 1); if (!substring.matches("^[A-Za-z0-9-]+$")) { throw new RakamException(FORBIDDEN); } URL resource = getClass().getResource("/custom-event-mapper/" + substring + "/image.png"); if (resource == null) { throw new RakamException(NOT_FOUND); } byte[] script; try { script = toByteArray(resource.openStream()); } catch (IOException e) { throw Throwables.propagate(e); } DefaultFullHttpResponse resp = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(script)); resp.headers().add(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); resp.headers().add(CACHE_CONTROL, "private, max-age=86400"); HttpHeaders.setContentLength(resp, script.length); resp.headers().set(CONTENT_TYPE, "image/png"); request.response(resp).end(); }
@ApiOperation(value = "Install recipe", response = SuccessMessage.class) @POST @ProtectEndpoint(writeOperation = true) @Path("/install") public void installUIRecipe(RakamHttpRequest request, @Named("user_id") Project project) { String contentType = request.headers().get(CONTENT_TYPE); ExportType exportType = Arrays.stream(ExportType.values()) .filter(f -> f.contentType.equals(contentType)) .findAny() .orElse(ExportType.YAML); boolean override = ImmutableList.of(TRUE.toString()).equals(request.params().get("override")); request.bodyHandler(body -> { UIRecipe recipe; try { recipe = exportType.mapper.readValue(body, UIRecipe.class); } catch (IOException e) { returnError(request, e.getMessage(), HttpResponseStatus.BAD_REQUEST); return; } try { UIRecipeHandler.RecipeResult install = installer.install(recipe, project.userId, project.project, override); request.response(JsonHelper.encode(install)).end(); } catch (Exception e) { returnError(request, "Error loading recipe: " + e.getMessage(), HttpResponseStatus.BAD_REQUEST); } }); }
@Path("/check-configuration") @GET public void checkConfiguration(RakamHttpRequest request) { request.response(JsonHelper.encode(ImmutableMap.of( "sentry", checkSentry(), "scheduledEmailEnabled", config.getScreenCaptureService() != null, "disableTracking", config.getDisableTracking())), OK).end(); }
@ApiOperation(value = "Export recipe", response = UIRecipe.class, authorizations = @Authorization(value = "master_key") ) @GET @Path("/export") public void exportUIRecipe(@HeaderParam("Accept") String contentType, @Named("user_id") Project project, RakamHttpRequest request) throws JsonProcessingException { request.bodyHandler(s -> { UIRecipe export = installer.export(project.userId, project.project); ExportType exportType = Arrays.stream(ExportType.values()) .filter(f -> f.contentType.equals(contentType)) .findAny() .orElse(ExportType.YAML); ByteBuf buffer; try { buffer = Unpooled.wrappedBuffer(exportType.mapper.writeValueAsBytes(export)); } catch (JsonProcessingException e) { throw Throwables.propagate(e); } DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buffer); response.headers().add(CONTENT_TYPE, exportType.contentType); if (request.headers().contains(ORIGIN)) { response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, request.headers().get(ORIGIN)); } request.response(response).end(); }); }
@ApiOperation(value = "Export recipe", response = Recipe.class, authorizations = @Authorization(value = "master_key") ) @GET @Path("/export") public void exportRecipe(@HeaderParam("Accept") String contentType, @Named("project") RequestContext context, RakamHttpRequest request) throws JsonProcessingException { request.bodyHandler(s -> { Recipe export = installer.export(context.project); ExportType exportType = Arrays.stream(ExportType.values()) .filter(f -> f.contentType.equals(contentType)) .findAny() .orElse(YAML); ByteBuf buffer; try { buffer = Unpooled.wrappedBuffer(exportType.mapper.writeValueAsBytes(export)); } catch (JsonProcessingException e) { throw Throwables.propagate(e); } DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buffer); response.headers().add(CONTENT_TYPE, exportType.contentType); if (request.headers().contains(ORIGIN)) { response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, request.headers().get(ORIGIN)); } request.response(response).end(); }); }
@Path("/display/*") @GET public void display(RakamHttpRequest request) { if (!database.isPresent()) { throw new RakamException("Custom page feature is not implemented", NOT_IMPLEMENTED); } String path = request.path().substring(21); String[] projectCustomPage = path.split("/", 3); byte[] bytes; try { InputStream file = database.get().getFile(Integer.parseInt(projectCustomPage[0]), projectCustomPage[1], projectCustomPage[2]); if (file == null) { request.response(NOT_FOUND.reasonPhrase(), NOT_FOUND).end(); } bytes = ByteStreams.toByteArray(file); } catch (IOException e) { throw Throwables.propagate(e); } HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); response.headers().set(CONTENT_TYPE, "text/html"); HttpHeaders.setContentLength(response, bytes.length); request.context().write(response); request.context().write(Unpooled.wrappedBuffer(bytes)); ChannelFuture lastContentFuture = request.context().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (!HttpHeaders.isKeepAlive(request)) { lastContentFuture.addListener(ChannelFutureListener.CLOSE); } }
@Override public void operationComplete(Future<Object> future) throws Exception { if (future.await(1, TimeUnit.SECONDS)) { Object body = future.getNow(); if (body == null) { return; } if (!(body instanceof ScriptObjectMirror)) { returnError(request, "The script must return an object or array {collection: '', properties: {}}", BAD_REQUEST); } ScriptObjectMirror json = (ScriptObjectMirror) ((ScriptObjectMirror) body).eval("JSON"); Object stringify = json.callMember("stringify", body); request.response(stringify.toString()).end(); } else { byte[] bytes = JsonHelper.encodeAsBytes(errorMessage("Webhook code timeouts.", INTERNAL_SERVER_ERROR)); request.response(bytes, INTERNAL_SERVER_ERROR).end(); } } });
@ApiOperation(value = "Batch operation on a single user properties", request = SingleUserBatchOperationRequest.class, response = Integer.class, authorizations = {@Authorization(value = "write_key")}) @ApiResponses(value = {@ApiResponse(code = 404, message = "User does not exist.")}) @Path("/batch") @JsonRequest public void batchSingleUserOperations(RakamHttpRequest request) { request.bodyHandler(s -> { SingleUserBatchOperationRequest req; try { req = JsonHelper.read(s, SingleUserBatchOperationRequest.class); } catch (Exception e) { returnError(request, e.getMessage(), BAD_REQUEST); return; } String project = apiKeyService.getProjectOfApiKey(req.api.apiKey, WRITE_KEY); InetAddress socketAddress = ((InetSocketAddress) request.context().channel() .remoteAddress()).getAddress(); DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, wrappedBuffer(OK_MESSAGE)); List<Cookie> cookies = mapEvent(mapper -> mapper.map(project, req.data, new HttpRequestParams(request), socketAddress)); service.batch(project, req.data); setBrowser(request, response); if (cookies != null && !cookies.isEmpty()) { response.headers().add(SET_COOKIE, STRICT.encode(cookies)); } request.response(response).end(); }); }
@JsonRequest @ApiOperation(value = "Merge user with anonymous id", authorizations = @Authorization(value = "write_key")) @ApiResponses(value = {@ApiResponse(code = 404, message = "User does not exist.")}) @Path("/merge") @IgnoreApi @AllowCookie public void mergeUser(RakamHttpRequest request, @BodyParam MergeRequest mergeRequest) { // TODO: what if a user sends real user ids instead of its previous anonymous id? if (!config.getEnableUserMapping()) { throw new RakamException("The feature is not supported", PRECONDITION_FAILED); } Object anonymousId = mergeRequest.anonymousId; DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, wrappedBuffer(OK_MESSAGE)); setBrowser(request, response); if (anonymousId == null) { throw new RakamException("Anonymous id is required", BAD_REQUEST); } String project = apiKeyService.getProjectOfApiKey(mergeRequest.api.apiKey, WRITE_KEY); service.merge(project, mergeRequest.id, anonymousId, Instant.ofEpochMilli(mergeRequest.createdAt), Instant.ofEpochMilli(mergeRequest.mergedAt)); request.response(response).end(); }
@JsonRequest @ApiOperation(value = "Set user properties once", request = User.class, response = Integer.class) @ApiResponses(value = {@ApiResponse(code = 404, message = "User does not exist.")}) @Path("/set_properties_once") public void setPropertiesOnce(RakamHttpRequest request) { request.bodyHandler(s -> { User req; try { req = JsonHelper.readSafe(s, User.class); } catch (IOException e) { returnError(request, e.getMessage(), BAD_REQUEST); return; } String project = apiKeyService.getProjectOfApiKey(req.api.apiKey, WRITE_KEY); DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, wrappedBuffer(OK_MESSAGE)); response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, request.headers().get(ORIGIN)); List<Cookie> cookies = mapProperties(project, req, request); if (cookies != null) { response.headers().add(SET_COOKIE, STRICT.encode(cookies)); } String headerList = getHeaderList(response.headers().iterator()); if (headerList != null) { response.headers().set(ACCESS_CONTROL_EXPOSE_HEADERS, headerList); } // TODO: we may cache these values and reduce the db hit. service.setUserPropertiesOnce(project, req.id, req.properties); request.response(OK_MESSAGE).end(); }); }