/*
* 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 org.rakam.ui.customreport;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import org.rakam.config.EncryptionConfig;
import org.rakam.server.http.HttpService;
import org.rakam.server.http.RakamHttpRequest;
import org.rakam.server.http.annotations.Api;
import org.rakam.server.http.annotations.ApiOperation;
import org.rakam.server.http.annotations.ApiParam;
import org.rakam.server.http.annotations.Authorization;
import org.rakam.server.http.annotations.BodyParam;
import org.rakam.server.http.annotations.HeaderParam;
import org.rakam.server.http.annotations.IgnoreApi;
import org.rakam.server.http.annotations.JsonRequest;
import org.rakam.ui.ProtectEndpoint;
import org.rakam.ui.UIPermissionParameterProvider;
import org.rakam.ui.page.CustomPageDatabase;
import org.rakam.ui.user.WebUserService;
import org.rakam.util.RakamException;
import org.rakam.util.SuccessMessage;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_IMPLEMENTED;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
@Path("/ui/custom-page")
@IgnoreApi
@Api(value = "/ui/custom-page", tags = "rakam-ui", authorizations = @Authorization(value = "read_key"))
public class CustomPageHttpService
extends HttpService
{
private final Optional<CustomPageDatabase> database;
@Inject
public CustomPageHttpService(Optional<CustomPageDatabase> database)
{
this.database = database;
}
@Path("/frame")
@GET
public void frame(RakamHttpRequest request)
{
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
String data = "<!DOCTYPE html> \n" +
"<html>\n" +
" <body>\n" +
" \t <script>\n" +
" var frame;\n" +
" window.onerror = function(message, url, lineNumber) {\n" +
" window.parent.postMessage({\n" +
" type: 'error',\n" +
" message: message,\n" +
" url: url,\n" +
" lineNumber: lineNumber\n" +
" }, '*');\n" +
" };\n" +
" window.addEventListener('message', function(e) {\n" +
" var data = JSON.parse(e.data);\n" +
" if (data.html) {\n" +
" if(frame) document.body.removeChild(frame);\n" +
" frame = document.createElement('iframe');\n" +
" frame.setAttribute('style', 'border:none;width:100%;height:100%;margin:0;padding:0;position:absolute;');\n" +
" frame.setAttribute('sandbox', 'allow-forms allow-popups allow-scripts allow-same-origin');\n" +
" document.body.appendChild(frame);\n" +
" frame.contentWindow.document.write(data.html);\n" +
" frame.contentWindow.document.close();\n" +
" }\n" +
" });\n" +
"\t </script>\n" +
" </body>\n" +
"</html>";
response.headers().set(CONTENT_TYPE, "text/html");
HttpHeaders.setContentLength(response, data.length());
request.context().write(response);
request.context().write(Unpooled.wrappedBuffer(data.getBytes()));
ChannelFuture lastContentFuture = request.context().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!HttpHeaders.isKeepAlive(request)) {
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
}
}
@Path("/save")
@POST
@JsonRequest
@ProtectEndpoint(writeOperation = true)
@ApiOperation(value = "Save Report", authorizations = @Authorization(value = "read_key"),
response = SuccessMessage.class, request = CustomPageDatabase.Page.class)
public SuccessMessage save(
@Named("user_id") UIPermissionParameterProvider.Project project, @BodyParam CustomPageDatabase.Page report)
{
database.get().save(project.userId, project.project, report);
return SuccessMessage.success();
}
@Path("/delete")
@ApiOperation(value = "Delete Report", authorizations = @Authorization(value = "read_key"))
@JsonRequest
@ProtectEndpoint(writeOperation = true)
public SuccessMessage delete(@com.google.inject.name.Named("user_id") UIPermissionParameterProvider.Project project,
@ApiParam("name") String name)
{
if (!database.isPresent()) {
throw new RakamException("Custom page feature is not implemented", NOT_IMPLEMENTED);
}
database.get().delete(project.project, name);
return SuccessMessage.success();
}
@Path("/check")
@ApiOperation(value = "Check feature exists", authorizations = @Authorization(value = "read_key"))
@GET
public boolean check()
{
return !database.isPresent();
}
@Path("/get")
@ApiOperation(value = "Get Report", authorizations = @Authorization(value = "read_key"))
@JsonRequest
public Map<String, String> get(@com.google.inject.name.Named("user_id") UIPermissionParameterProvider.Project project,
@ApiParam("slug") String slug)
{
if (!database.isPresent()) {
throw new RakamException("Custom page feature is not implemented", NOT_IMPLEMENTED);
}
return database.get().get(project.project, slug);
}
@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);
}
}
@Path("/list")
@ApiOperation(value = "Get Report", authorizations = @Authorization(value = "read_key"))
@JsonRequest
public List<CustomPageDatabase.Page> list(@Named("user_id") UIPermissionParameterProvider.Project project)
{
if (!database.isPresent()) {
return null;
}
return database.get().list(project.project);
}
}