org.rakam.ui.DashboardService.java Source code

Java tutorial

Introduction

Here is the source code for org.rakam.ui.DashboardService.java

Source

/*
 * 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;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.rakam.analysis.JDBCPoolDataSource;
import org.rakam.server.http.HttpService;
import org.rakam.server.http.annotations.ApiOperation;
import org.rakam.server.http.annotations.ApiParam;
import org.rakam.server.http.annotations.IgnoreApi;
import org.rakam.server.http.annotations.JsonRequest;
import org.rakam.ui.UIPermissionParameterProvider.Project;
import org.rakam.util.AlreadyExistsException;
import org.rakam.util.JsonHelper;
import org.rakam.util.SuccessMessage;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.util.IntegerMapper;

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Path;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;

// todo check permissions
@Path("/ui/dashboard")
@IgnoreApi
public class DashboardService extends HttpService {
    private final DBI dbi;

    @Inject
    public DashboardService(@Named("ui.metadata.jdbc") JDBCPoolDataSource dataSource) {
        dbi = new DBI(dataSource);
    }

    @JsonRequest
    @ApiOperation(value = "Create dashboard")
    @Path("/create")
    @ProtectEndpoint(writeOperation = true)
    public Dashboard create(@Named("user_id") Project project, @ApiParam("name") String name,
            @ApiParam(value = "options", required = false) Map<String, Object> options) {
        try (Handle handle = dbi.open()) {
            int id;
            try {
                id = handle.createQuery(
                        "INSERT INTO dashboard (project_id, name, options) VALUES (:project, :name, :options) RETURNING id")
                        .bind("project", project.project).bind("options", JsonHelper.encode(options))
                        .bind("name", name).map(IntegerMapper.FIRST).first();
            } catch (Exception e) {
                if (handle.createQuery("SELECT 1 FROM dashboard WHERE (project_id, name) = (:project, :name)")
                        .bind("project", project.project).bind("name", name).first() != null) {
                    throw new AlreadyExistsException("Dashboard", HttpResponseStatus.BAD_REQUEST);
                }

                throw e;
            }
            return new Dashboard(id, name, options);
        }
    }

    @JsonRequest
    @ApiOperation(value = "Create dashboard")
    @Path("/delete")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage delete(@Named("user_id") Project project, @ApiParam(value = "name") String name) {
        try (Handle handle = dbi.open()) {
            handle.createStatement("DELETE FROM dashboard WHERE project_id = :project AND name = :name")
                    .bind("project", project.project).bind("name", name).execute();
        }
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Get Report")
    @Path("/get")
    public List<DashboardItem> get(@Named("user_id") Project project, @ApiParam("name") String name) {
        try (Handle handle = dbi.open()) {
            return handle.createQuery("SELECT id, name, directive, options, refresh_interval, last_updated,"
                    + "(case when now() - last_updated > refresh_interval * INTERVAL '1 second' then null else data end)"
                    + " FROM dashboard_items WHERE dashboard = (SELECT id FROM dashboard WHERE project_id = :project AND name = :name)")
                    .bind("project", project.project).bind("name", name).map((i, r, statementContext) -> {
                        return new DashboardItem(r.getInt(1), r.getString(2), r.getString(3),
                                JsonHelper.read(r.getString(4), Map.class), Duration.ofSeconds(r.getInt(5)),
                                r.getTimestamp(6) != null ? r.getTimestamp(6).toInstant() : null, r.getBytes(7));
                    }).list();
        }
    }

    @JsonRequest
    @ApiOperation(value = "Cache report data")
    @Path("/cache-item-data")
    public SuccessMessage cache(@Named("user_id") Project project, @ApiParam("item_id") int item_id,
            @ApiParam("data") byte[] data) {
        try (Handle handle = dbi.open()) {
            handle.createStatement(
                    "UPDATE dashboard_items SET data = :data, last_updated = now() WHERE id = :id AND"
                            + " (SELECT project_id FROM dashboard_items item JOIN dashboard ON (dashboard.id = item.dashboard) WHERE item.id = :id AND dashboard.project_id = :project) is not null")
                    .bind("id", item_id).bind("project", project.project).bind("data", data).execute();
            return SuccessMessage.success();
        }
    }

    @JsonRequest
    @ApiOperation(value = "List Report")
    @Path("/list")
    public List<Dashboard> list(@Named("user_id") Project project) {
        try (Handle handle = dbi.open()) {
            return handle
                    .createQuery("SELECT id, name, options FROM dashboard WHERE project_id = :project ORDER BY id")
                    .bind("project", project.project).map((i, resultSet, statementContext) -> {
                        Map options = JsonHelper.read(resultSet.getString(3), Map.class);
                        return new Dashboard(resultSet.getInt(1), resultSet.getString(2),
                                options == null ? null : options);
                    }).list();
        }
    }

    public static class Dashboard {
        public final int id;
        public final String name;
        public final Map<String, Object> options;

        @JsonCreator
        public Dashboard(int id, String name, Map<String, Object> options) {
            this.id = id;
            this.name = name;
            this.options = options;
        }
    }

    public static class DashboardItem {
        public final Integer id;
        public final Map options;
        public final Duration refreshInterval;
        public final String directive;
        public final String name;
        public final Instant lastUpdated;
        public final byte[] data;

        @JsonCreator
        public DashboardItem(@JsonProperty("id") Integer id, @JsonProperty("name") String name,
                @JsonProperty("directive") String directive, @JsonProperty("options") Map options,
                @JsonProperty("refreshInterval") Duration refreshInterval, @JsonProperty("data") byte[] data) {
            this(id, name, directive, options, refreshInterval, null, data);
        }

        public DashboardItem(Integer id, String name, String directive, Map options, Duration refreshInterval,
                Instant lastUpdated, byte[] data) {
            this.id = id;
            this.options = options;
            this.refreshInterval = refreshInterval;
            this.directive = directive;
            this.name = name;
            this.lastUpdated = lastUpdated;
            this.data = data;
        }
    }

    @JsonRequest
    @ApiOperation(value = "Add item to dashboard")
    @Path("/add_item")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage addToDashboard(@Named("user_id") Project project, @ApiParam("dashboard") int dashboard,
            @ApiParam("name") String itemName, @ApiParam("directive") String directive,
            @ApiParam(value = "refresh_interval", required = false) Duration refreshInterval,
            @ApiParam("options") Map options) {
        try (Handle handle = dbi.open()) {
            handle.createStatement(
                    "INSERT INTO dashboard_items (dashboard, name, directive, options, refresh_interval) VALUES (:dashboard, :name, :directive, :options, :refreshInterval)")
                    .bind("project", project.project).bind("dashboard", dashboard).bind("name", itemName)
                    .bind("directive", directive)
                    .bind("refreshInterval",
                            Optional.ofNullable(refreshInterval).map(e -> e.getSeconds()).orElse(null))
                    .bind("options", JsonHelper.encode(options)).execute();
        }
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Update dashboard items")
    @Path("/update_dashboard_items")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage updateDashboard(@Named("user_id") Project project, @ApiParam("dashboard") int dashboard,
            @ApiParam("items") List<DashboardItem> items) {

        dbi.inTransaction((handle, transactionStatus) -> {
            for (DashboardItem item : items) {
                // TODO: verify dashboard is in project
                handle.createStatement(
                        "UPDATE dashboard_items SET name = :name, directive = :directive, options = :options WHERE id = :id")
                        .bind("id", item.id).bind("name", item.name).bind("directive", item.directive)
                        .bind("options", JsonHelper.encode(item.options)).execute();
            }
            return null;
        });
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Update dashboard options")
    @Path("/update_dashboard_options")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage updateDashboardOptions(@Named("user_id") Project project,
            @ApiParam("dashboard") int dashboard, @ApiParam("options") Map<String, Object> options) {

        dbi.inTransaction((handle, transactionStatus) -> {
            handle.createStatement("UPDATE dashboard SET options = :options WHERE id = :id AND project = :project")
                    .bind("id", dashboard).bind("options", JsonHelper.encode(options))
                    .bind("project", project.project).execute();
            return null;
        });
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Rename dashboard item")
    @Path("/rename_item")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage renameDashboardItem(@Named("user_id") Project project,
            @ApiParam("dashboard") int dashboard, @ApiParam("id") int id, @ApiParam("name") String name) {
        try (Handle handle = dbi.open()) {
            // todo: check project
            handle.createStatement("UPDATE dashboard_items SET name = :name WHERE id = :id").bind("id", id)
                    .bind("name", name).execute();
        }
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Delete dashboard item")
    @Path("/delete_item")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage removeFromDashboard(@Named("user_id") Project project,
            @ApiParam("dashboard") int dashboard, @ApiParam("id") int id) {
        try (Handle handle = dbi.open()) {
            handle.createStatement("DELETE FROM dashboard_items " + "WHERE dashboard = :dashboard AND id = :id")
                    .bind("project", project.project).bind("dashboard", dashboard).bind("id", id).execute();
        }
        return SuccessMessage.success();
    }

    @JsonRequest
    @ApiOperation(value = "Delete dashboard item")
    @Path("/delete")
    @ProtectEndpoint(writeOperation = true)
    public SuccessMessage delete(@Named("user_id") Project project, @ApiParam("dashboard") int dashboard) {
        try (Handle handle = dbi.open()) {
            handle.createStatement("DELETE FROM dashboard WHERE id = :id and project_id = :project")
                    .bind("id", dashboard).bind("project", project.project).execute();
        }
        return SuccessMessage.success();
    }
}