eagle-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From h..@apache.org
Subject eagle git commit: [EAGLE-957] Add dashboard metadata entities and services
Date Wed, 15 Mar 2017 09:07:56 GMT
Repository: eagle
Updated Branches:
  refs/heads/master 3fe637eb5 -> ab5bf1076


[EAGLE-957] Add dashboard metadata entities and services

# Add dashboard metadata entities and services
* org.apache.eagle.metadata.model.DashboardEntity
* org.apache.eagle.metadata.service.DashboardEntityService (Memory/JDBC)
* org.apache.eagle.metadata.resource.DashboardResource

# API

    DELETE  /rest/dashboards/{uuid} (org.apache.eagle.metadata.resource.DashboardResource)
    GET     /rest/dashboards (org.apache.eagle.metadata.resource.DashboardResource)
    GET     /rest/dashboards/{uuidOrName} (org.apache.eagle.metadata.resource.DashboardResource)
    POST    /rest/dashboards (org.apache.eagle.metadata.resource.DashboardResource)

# Example
* Create dashboard

	 	POST    /rest/dashboards

		{
		  "name": "Sample Dashboard",
		  "description": "This is a sample dashboard",
		  "settings": {
		  },
		  "charts": [
		    "{chartType: LINE}",
		    "{chartType: PIE}"
		  ]
		}

* Update dashboard

		POST    /rest/dashboards
		{
		  "uuid": "e24fd133-adc2-4dd2-b7aa-ebf4890b145a"
		  "name": "Sample Dashboard 2",
		  "description": "This is another sample dashboard"
		}

Author: Hao Chen <hao@apache.org>

Closes #873 from haoch/EAGLE-957.


Project: http://git-wip-us.apache.org/repos/asf/eagle/repo
Commit: http://git-wip-us.apache.org/repos/asf/eagle/commit/ab5bf107
Tree: http://git-wip-us.apache.org/repos/asf/eagle/tree/ab5bf107
Diff: http://git-wip-us.apache.org/repos/asf/eagle/diff/ab5bf107

Branch: refs/heads/master
Commit: ab5bf1076e3d199edfe58659fb9b14904d63ac8f
Parents: 3fe637e
Author: Hao Chen <hao@apache.org>
Authored: Wed Mar 15 17:07:41 2017 +0800
Committer: Hao Chen <hao@apache.org>
Committed: Wed Mar 15 17:07:41 2017 +0800

----------------------------------------------------------------------
 .../eagle/metadata/model/DashboardEntity.java   | 135 +++++++++++
 .../metadata/resource/DashboardResource.java    |  68 ++++++
 .../service/DashboardEntityService.java         |  42 ++++
 .../DashboardEntityServiceMemoryImpl.java       | 119 ++++++++++
 .../service/memory/MemoryMetadataStore.java     |   2 +
 .../TestDashboardEntityServiceMemoryImpl.java   | 109 +++++++++
 .../TestSiteEntityServiceMemoryImpl.java        |   3 +-
 .../JDBCMetadataMetadataStoreServiceImpl.java   |  75 +++++++
 .../store/jdbc/JDBCMetadataQueryService.java    |  11 +-
 .../metadata/store/jdbc/JDBCMetadataStore.java  |   3 +
 .../jdbc/provider/JDBCDataSourceProvider.java   |   2 +-
 .../service/DashboardEntityServiceJDBCImpl.java | 224 +++++++++++++++++++
 .../DashboardEntityServiceJDBCImplTest.java     | 112 ++++++++++
 .../jdbc/SiteEntityServiceJDBCImplTest.java     |   3 +-
 .../src/test/resources/init.sql                 |  35 ++-
 .../src/main/bin/metadata-ddl.sql               |  36 ++-
 16 files changed, 954 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/DashboardEntity.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/DashboardEntity.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/DashboardEntity.java
new file mode 100644
index 0000000..d8939ab
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/DashboardEntity.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.model;
+
+import com.google.common.base.Preconditions;
+import org.apache.eagle.metadata.persistence.PersistenceEntity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Dashboard Config Entity.
+ */
+public class DashboardEntity extends PersistenceEntity {
+
+    private String name;
+    private String description;
+    private String author;
+    /**
+     * Dashboard Level Settings.
+     */
+    private Map<String,Object> settings;
+
+    /**
+     * Charts configurations.
+     */
+    private List<String> charts;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        this.author = author;
+    }
+
+    public Map<String, Object> getSettings() {
+        return settings;
+    }
+
+    public void setSettings(Map<String, Object> settings) {
+        this.settings = settings;
+    }
+
+    public List<String> getCharts() {
+        return charts;
+    }
+
+    public void setCharts(List<String> charts) {
+        this.charts = charts;
+    }
+
+    @Override
+    public void ensureDefault() {
+        Preconditions.checkNotNull(this.name, "name should not be null");
+        Preconditions.checkNotNull(this.description, "description should not be null");
+        Preconditions.checkNotNull(this.author, "author should not be null");
+        if (this.settings == null) {
+            this.settings = new HashMap<>();
+        }
+        if (this.charts == null) {
+            this.charts = new ArrayList<>(0);
+        }
+        super.ensureDefault();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DashboardEntity entity = (DashboardEntity) o;
+
+        if (name != null ? !name.equals(entity.name) : entity.name != null) {
+            return false;
+        }
+        if (description != null ? !description.equals(entity.description) : entity.description != null) {
+            return false;
+        }
+        if (author != null ? !author.equals(entity.author) : entity.author != null) {
+            return false;
+        }
+        if (settings != null ? !settings.equals(entity.settings) : entity.settings != null) {
+            return false;
+        }
+        return charts != null ? charts.equals(entity.charts) : entity.charts == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (description != null ? description.hashCode() : 0);
+        result = 31 * result + (author != null ? author.hashCode() : 0);
+        result = 31 * result + (settings != null ? settings.hashCode() : 0);
+        result = 31 * result + (charts != null ? charts.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/resource/DashboardResource.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/resource/DashboardResource.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/resource/DashboardResource.java
new file mode 100644
index 0000000..c22e840
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/resource/DashboardResource.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.resource;
+
+
+import com.google.inject.Inject;
+import io.dropwizard.auth.Auth;
+import org.apache.eagle.common.rest.RESTResponse;
+import org.apache.eagle.common.security.RolesAllowed;
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.service.DashboardEntityService;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import java.util.Collection;
+
+@Path("/dashboards")
+public class DashboardResource {
+    @Inject
+    private DashboardEntityService dashboardEntityService;
+
+    @GET
+    @Path("/")
+    @Produces(MediaType.APPLICATION_JSON)
+    public RESTResponse<Collection<DashboardEntity>> listAllDashboards() {
+        return RESTResponse.async(() -> dashboardEntityService.findAll()).get();
+    }
+
+    @GET
+    @Path("/{uuidOrName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public RESTResponse<DashboardEntity> getDashboardByUUIDOrName(@PathParam("uuidOrName") String uuidOrName) {
+        return RESTResponse.async(() -> dashboardEntityService.getByUUIDOrName(uuidOrName, uuidOrName)).get();
+    }
+
+    @POST
+    @Path("/")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @RolesAllowed({User.Role.USER, User.Role.ADMINISTRATOR})
+    public RESTResponse<DashboardEntity> createOrUpdateDashboard(DashboardEntity dashboardEntity, @Auth User user) {
+        return RESTResponse.async(() -> dashboardEntityService.createOrUpdate(dashboardEntity, user)).get();
+    }
+
+    @DELETE
+    @Path("/{uuid}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @RolesAllowed({User.Role.USER, User.Role.ADMINISTRATOR})
+    public RESTResponse<DashboardEntity> deleteDashboard(String uuid, @Auth User user) {
+        return RESTResponse.async(() -> dashboardEntityService.deleteByUUID(uuid, user)).get();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/DashboardEntityService.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/DashboardEntityService.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/DashboardEntityService.java
new file mode 100644
index 0000000..d0b575a
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/DashboardEntityService.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.service;
+
+import com.google.common.base.Preconditions;
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.persistence.PersistenceService;
+
+public interface DashboardEntityService extends PersistenceService<DashboardEntity> {
+    DashboardEntity update(DashboardEntity dashboardEntity, User user) throws EntityNotFoundException;
+
+    DashboardEntity getByUUIDOrName(String uuid, String name) throws EntityNotFoundException;
+
+    DashboardEntity deleteByUUID(String uuid, User user) throws EntityNotFoundException;
+
+    default DashboardEntity createOrUpdate(DashboardEntity dashboardEntity, User user) throws EntityNotFoundException {
+        Preconditions.checkNotNull(dashboardEntity, "DashboardEntity should not be null");
+        if (dashboardEntity.getUuid() == null) {
+            Preconditions.checkArgument(dashboardEntity.getAuthor() == null, "author is immutable");
+            dashboardEntity.setAuthor(user.getName());
+            return create(dashboardEntity);
+        } else {
+            return update(dashboardEntity, user);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/DashboardEntityServiceMemoryImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/DashboardEntityServiceMemoryImpl.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/DashboardEntityServiceMemoryImpl.java
new file mode 100644
index 0000000..79608b9
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/DashboardEntityServiceMemoryImpl.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.service.memory;
+
+import com.google.common.base.Preconditions;
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.service.DashboardEntityService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DashboardEntityServiceMemoryImpl implements DashboardEntityService {
+    private final Map<String, DashboardEntity> dashboardEntityMap = new HashMap<>();
+    private static final Logger LOGGER = LoggerFactory.getLogger(DashboardEntityServiceMemoryImpl.class);
+
+    @Override
+    public synchronized Collection<DashboardEntity> findAll() {
+        return dashboardEntityMap.values();
+    }
+
+    @Override
+    public synchronized DashboardEntity getByUUID(String uuid) throws EntityNotFoundException {
+        Preconditions.checkNotNull(uuid, "uuid should not be null");
+        if (!dashboardEntityMap.containsKey(uuid)) {
+            throw new EntityNotFoundException("uuid " + uuid + "not exist");
+        }
+        return dashboardEntityMap.get(uuid);
+    }
+
+    @Override
+    public synchronized DashboardEntity create(DashboardEntity entity) {
+        Preconditions.checkNotNull(entity, "DashboardEntity is null");
+        Preconditions.checkArgument(entity.getUuid() == null, "Dashboard Entity uuid should be null");
+        entity.ensureDefault();
+        try {
+            Preconditions.checkArgument(getByUUIDOrName(entity.getUuid(), entity.getName()) == null, "Duplicated dashboard name");
+        } catch (EntityNotFoundException e) {
+            // ignore
+        }
+        dashboardEntityMap.put(entity.getUuid(), entity);
+        return entity;
+    }
+
+    @Override
+    public synchronized DashboardEntity update(DashboardEntity entity, User user) throws EntityNotFoundException {
+        Preconditions.checkNotNull(entity, "Entity should not be null");
+        Preconditions.checkNotNull(entity.getUuid(), "uuid should not be null");
+        DashboardEntity current = getByUUID(entity.getUuid());
+
+        Preconditions.checkArgument(user.isInRole(User.Role.ADMINISTRATOR)
+            || current.getAuthor().equals(user.getName()), "UPDATE operation is not allowed");
+
+        if (entity.getName() != null) {
+            current.setName(entity.getName());
+        }
+        if (entity.getDescription() != null) {
+            current.setDescription(entity.getDescription());
+        }
+        if (entity.getAuthor() != null) {
+            current.setAuthor(entity.getAuthor());
+        }
+        if (entity.getCharts() != null) {
+            current.setCharts(entity.getCharts());
+        }
+        if (entity.getSettings() != null) {
+            current.setSettings(entity.getSettings());
+        }
+        if (entity.getCreatedTime() > 0) {
+            LOGGER.warn("createdTime  is not updatable but provided: {}, ignore", current.getCreatedTime());
+        }
+        if (entity.getModifiedTime() > 0) {
+            LOGGER.warn("modifiedTime is not updatable but provided: {}, ignore", current.getModifiedTime());
+        }
+        current.ensureDefault();
+        dashboardEntityMap.put(current.getUuid(), current);
+        return current;
+    }
+
+    @Override
+    public synchronized DashboardEntity getByUUIDOrName(String uuid, String name) throws EntityNotFoundException {
+        if (uuid != null) {
+            return getByUUID(uuid);
+        } else if (name != null) {
+            return dashboardEntityMap.values().stream()
+                .filter((dashboardEntity -> dashboardEntity.getName().equals(name))).findAny()
+                .orElseThrow(() -> new EntityNotFoundException("Dashboard named: " + name + " not found"));
+        }
+        throw new IllegalArgumentException("Both uuid and name are null");
+    }
+
+    @Override
+    public synchronized DashboardEntity deleteByUUID(String uuid, User user) throws EntityNotFoundException {
+        Preconditions.checkNotNull(uuid, "UUID should not be null");
+        DashboardEntity current = this.getByUUID(uuid);
+        Preconditions.checkArgument(user.isInRole(User.Role.ADMINISTRATOR)
+            || current.getAuthor().equals(user.getName()), "DELETE operation is not allowed for user " + user.getName());
+        dashboardEntityMap.remove(uuid);
+        return current;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/MemoryMetadataStore.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/MemoryMetadataStore.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/MemoryMetadataStore.java
index 747c3be..7cc076a 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/MemoryMetadataStore.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/service/memory/MemoryMetadataStore.java
@@ -21,6 +21,7 @@ import org.apache.eagle.alert.metadata.IMetadataDao;
 import org.apache.eagle.alert.metadata.impl.InMemMetadataDaoImpl;
 import org.apache.eagle.metadata.persistence.MetadataStore;
 import org.apache.eagle.metadata.service.ApplicationEntityService;
+import org.apache.eagle.metadata.service.DashboardEntityService;
 import org.apache.eagle.metadata.service.SiteEntityService;
 
 public class MemoryMetadataStore extends MetadataStore {
@@ -29,5 +30,6 @@ public class MemoryMetadataStore extends MetadataStore {
         bind(SiteEntityService.class).to(SiteEntityEntityServiceMemoryImpl.class).in(Singleton.class);
         bind(ApplicationEntityService.class).to(ApplicationEntityServiceMemoryImpl.class).in(Singleton.class);
         bind(IMetadataDao.class).to(InMemMetadataDaoImpl.class).in(Singleton.class);
+        bind(DashboardEntityService.class).to(DashboardEntityServiceMemoryImpl.class).in(Singleton.class);
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestDashboardEntityServiceMemoryImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestDashboardEntityServiceMemoryImpl.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestDashboardEntityServiceMemoryImpl.java
new file mode 100644
index 0000000..a731e92
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestDashboardEntityServiceMemoryImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.service;
+
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.service.memory.DashboardEntityServiceMemoryImpl;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+
+public class TestDashboardEntityServiceMemoryImpl  {
+    private DashboardEntityService dashboardEntityService = new DashboardEntityServiceMemoryImpl();
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void testDashboardCRUD() throws EntityNotFoundException {
+        User user1 = new User();
+        user1.setName("user1");
+        user1.setRoles(Collections.singleton(User.Role.USER));
+
+        User user2 = new User();
+        user2.setName("user2");
+        user2.setRoles(Collections.singleton(User.Role.USER));
+
+        User admin  = new User();
+        admin.setName("admin");
+        admin.setRoles(Collections.singleton(User.Role.ADMINISTRATOR));
+
+        DashboardEntity entity = new DashboardEntity();
+        {
+            entity.setName("Sample Dashboard");
+            entity.setDescription("A sample dashboard for unit test");
+            entity.setAuthor(user1.getName());
+            entity.setSettings(new HashMap<String, Object>() {
+                {
+                    put("Stringkey", "SettingValue");
+                }
+            });
+            entity.setCharts(Arrays.asList(
+                "{chartType: 'LINE'}",
+                "{chartType}: 'PIE'"
+            ));
+        }
+
+        {
+            Assert.assertEquals(0, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity createdEntity;
+        {
+            createdEntity = dashboardEntityService.create(entity);
+            Assert.assertNotNull(createdEntity.getUuid());
+            Assert.assertTrue(createdEntity.getCreatedTime() > 0);
+            Assert.assertTrue(createdEntity.getModifiedTime() > 0);
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity updatedEntity;
+        {
+            DashboardEntity entityToUpdate = new DashboardEntity();
+            entityToUpdate.setUuid(createdEntity.getUuid());
+            entityToUpdate.setName("Sample Dashboard (Updated)");
+            updatedEntity = dashboardEntityService.update(entityToUpdate, user1);
+            Assert.assertEquals(createdEntity.getUuid(), updatedEntity.getUuid());
+            Assert.assertEquals("Sample Dashboard (Updated)", updatedEntity.getName());
+            Assert.assertEquals(createdEntity.getCreatedTime(), updatedEntity.getCreatedTime());
+            Assert.assertEquals(createdEntity.getAuthor(),updatedEntity.getAuthor());
+            Assert.assertEquals(createdEntity.getCharts(), updatedEntity.getCharts());
+            Assert.assertEquals(createdEntity.getSettings(), updatedEntity.getSettings());
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity deletedEntity;
+
+        {
+            expectedException.expect(IllegalArgumentException.class);
+            dashboardEntityService.deleteByUUID(updatedEntity.getUuid(), user2);
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        {
+            deletedEntity = dashboardEntityService.deleteByUUID(updatedEntity.getUuid(), admin);
+            Assert.assertEquals(updatedEntity, deletedEntity);
+            Assert.assertEquals(0, dashboardEntityService.findAll().size());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestSiteEntityServiceMemoryImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestSiteEntityServiceMemoryImpl.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestSiteEntityServiceMemoryImpl.java
index 645d18a..c0225e2 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestSiteEntityServiceMemoryImpl.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/test/java/org/apache/eagle/metadata/service/TestSiteEntityServiceMemoryImpl.java
@@ -16,10 +16,11 @@
  */
 package org.apache.eagle.metadata.service;
 
-import junit.framework.Assert;
+
 import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
 import org.apache.eagle.metadata.model.SiteEntity;
 import org.apache.eagle.metadata.service.memory.SiteEntityEntityServiceMemoryImpl;
+import org.junit.Assert;
 import org.junit.Test;
 
 /**

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataMetadataStoreServiceImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataMetadataStoreServiceImpl.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataMetadataStoreServiceImpl.java
index e62ebe4..e7a50cd 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataMetadataStoreServiceImpl.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataMetadataStoreServiceImpl.java
@@ -18,6 +18,7 @@ package org.apache.eagle.metadata.store.jdbc;
 
 
 import com.google.inject.Inject;
+import org.apache.eagle.common.function.ThrowableConsumer;
 import org.apache.eagle.common.function.ThrowableConsumer2;
 import org.apache.eagle.common.function.ThrowableFunction;
 import org.slf4j.Logger;
@@ -65,6 +66,35 @@ public class JDBCMetadataMetadataStoreServiceImpl implements JDBCMetadataQuerySe
     }
 
     @Override
+    public <T, E extends Throwable> boolean execute(String sql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws SQLException, E {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        try {
+            connection = dataSource.getConnection();
+            statement = connection.prepareStatement(sql);
+            mapper.accept(statement,entity);
+            return statement.executeUpdate() > 0;
+        } catch (SQLException e) {
+            throw e;
+        } finally {
+            if (statement != null) {
+                try {
+                    statement.close();
+                } catch (SQLException e) {
+                    LOGGER.error(e.getMessage(), e);
+                }
+            }
+            if (connection != null) {
+                try {
+                    connection.close();
+                } catch (SQLException e) {
+                    LOGGER.error(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    @Override
     public boolean dropTable(String tableName) throws SQLException {
         LOGGER.debug("Dropping table {}", tableName);
         return execute(String.format("DROP TABLE %s", tableName));
@@ -207,6 +237,51 @@ public class JDBCMetadataMetadataStoreServiceImpl implements JDBCMetadataQuerySe
     }
 
     @Override
+    public <T, E extends Throwable> List<T> queryWithCond(String querySql,
+                                                          ThrowableConsumer<PreparedStatement, SQLException> preparer,
+                                                          ThrowableFunction<ResultSet, T, E> mapper) throws SQLException, E {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        ResultSet resultSet = null;
+        try {
+            connection = dataSource.getConnection();
+            statement = connection.prepareStatement(querySql);
+            preparer.accept(statement);
+            resultSet = statement.executeQuery();
+            List<T> result = new LinkedList<>();
+            while (resultSet.next()) {
+                result.add(mapper.apply(resultSet));
+            }
+            return result;
+        } catch (SQLException e) {
+            LOGGER.error("Error to query cond: {}", querySql, e);
+            throw e;
+        } finally {
+            if (resultSet != null) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    LOGGER.error(e.getMessage(), e);
+                }
+            }
+            if (statement != null) {
+                try {
+                    statement.close();
+                } catch (SQLException e) {
+                    LOGGER.error(e.getMessage(), e);
+                }
+            }
+            if (connection != null) {
+                try {
+                    connection.close();
+                } catch (SQLException e) {
+                    LOGGER.error(e.getMessage(), e);
+                }
+            }
+        }
+    }
+
+    @Override
     public <T, E extends Throwable> int update(String updateSql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws SQLException, E {
         Connection connection = null;
         PreparedStatement statement = null;

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataQueryService.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataQueryService.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataQueryService.java
index d869305..843e609 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataQueryService.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataQueryService.java
@@ -16,6 +16,7 @@
  */
 package org.apache.eagle.metadata.store.jdbc;
 
+import org.apache.eagle.common.function.ThrowableConsumer;
 import org.apache.eagle.common.function.ThrowableConsumer2;
 import org.apache.eagle.common.function.ThrowableFunction;
 
@@ -24,10 +25,13 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collection;
 import java.util.List;
+import java.util.function.Consumer;
 
 public interface JDBCMetadataQueryService {
     boolean execute(String sql) throws SQLException;
 
+    <T, E extends Throwable> boolean execute(String sql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws SQLException, E;
+
     boolean dropTable(String tableName) throws SQLException;
 
     <T, E extends Throwable> int insert(String insertSql, Collection<T> entities, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws E, SQLException;
@@ -36,8 +40,13 @@ public interface JDBCMetadataQueryService {
 
     <T, E extends Throwable> List<T> query(String querySql, ThrowableFunction<ResultSet, T, E> mapper) throws SQLException, E;
 
+
+    /**
+     * @see #queryWithCond(String, ThrowableConsumer, ThrowableFunction)
+     */
     <T, E extends Throwable> List<T> queryWithCond(String querySql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper1, ThrowableFunction<ResultSet, T, E> mapper) throws SQLException, E;
 
-    <T, E extends Throwable> int update(String updateSql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws SQLException, E;
+    <T, E extends Throwable> List<T> queryWithCond(String querySql, ThrowableConsumer<PreparedStatement, SQLException> preparer, ThrowableFunction<ResultSet, T, E> mapper) throws SQLException, E;
 
+    <T, E extends Throwable> int update(String updateSql, T entity, ThrowableConsumer2<PreparedStatement, T, E> mapper) throws SQLException, E;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataStore.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataStore.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataStore.java
index 2e12e23..1af8e78 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataStore.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/JDBCMetadataStore.java
@@ -23,10 +23,12 @@ import org.apache.eagle.alert.metadata.MetadataUtils;
 import org.apache.eagle.alert.metadata.impl.JdbcMetadataDaoImpl;
 import org.apache.eagle.metadata.persistence.MetadataStore;
 import org.apache.eagle.metadata.service.ApplicationEntityService;
+import org.apache.eagle.metadata.service.DashboardEntityService;
 import org.apache.eagle.metadata.service.SiteEntityService;
 import org.apache.eagle.metadata.store.jdbc.provider.JDBCDataSourceProvider;
 import org.apache.eagle.metadata.store.jdbc.provider.JDBCMetadataStoreConfigProvider;
 import org.apache.eagle.metadata.store.jdbc.service.ApplicationEntityServiceJDBCImpl;
+import org.apache.eagle.metadata.store.jdbc.service.DashboardEntityServiceJDBCImpl;
 import org.apache.eagle.metadata.store.jdbc.service.SiteEntityServiceJDBCImpl;
 
 import javax.sql.DataSource;
@@ -40,5 +42,6 @@ public class JDBCMetadataStore extends MetadataStore {
         bind(JDBCMetadataQueryService.class).to(JDBCMetadataMetadataStoreServiceImpl.class).in(Singleton.class);
         bind(ApplicationEntityService.class).to(ApplicationEntityServiceJDBCImpl.class).in(Singleton.class);
         bind(SiteEntityService.class).to(SiteEntityServiceJDBCImpl.class).in(Singleton.class);
+        bind(DashboardEntityService.class).to(DashboardEntityServiceJDBCImpl.class).in(Singleton.class);
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/provider/JDBCDataSourceProvider.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/provider/JDBCDataSourceProvider.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/provider/JDBCDataSourceProvider.java
index 40f08c1..7495ef5 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/provider/JDBCDataSourceProvider.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/provider/JDBCDataSourceProvider.java
@@ -45,7 +45,7 @@ public class JDBCDataSourceProvider implements Provider<DataSource> {
             @Override
             public void run() {
                 try {
-                    LOGGER.info("Shutting down data fromStream");
+                    LOGGER.info("Shutting down data source");
                     datasource.close();
                 } catch (SQLException e) {
                     LOGGER.error("SQLException: {}", e.getMessage(), e);

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/service/DashboardEntityServiceJDBCImpl.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/service/DashboardEntityServiceJDBCImpl.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/service/DashboardEntityServiceJDBCImpl.java
new file mode 100644
index 0000000..0566687
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/main/java/org/apache/eagle/metadata/store/jdbc/service/DashboardEntityServiceJDBCImpl.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.store.jdbc.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.google.common.base.Preconditions;
+import com.mongodb.util.JSON;
+import org.apache.eagle.common.function.ThrowableConsumer2;
+import org.apache.eagle.common.function.ThrowableFunction;
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.service.DashboardEntityService;
+import org.apache.eagle.metadata.store.jdbc.JDBCMetadataQueryService;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+public class DashboardEntityServiceJDBCImpl implements DashboardEntityService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DashboardEntityServiceJDBCImpl.class);
+
+    private static final String TABLE_NAME = "dashboards";
+    private static final String SELECT_ALL_SQL = "SELECT * FROM " + TABLE_NAME;
+    private static final String FILTER_BY_UUID_SQL = "SELECT * FROM " + TABLE_NAME + " WHERE uuid = ?";
+    private static final String DELETE_BY_UUID_SQL_FORMAT = "DELETE FROM " + TABLE_NAME + " WHERE uuid = '%s'";
+    private static final String FILTER_BY_UUID_OR_NAME_SQL = "SELECT * FROM " + TABLE_NAME + " WHERE uuid = ? or name = ?";
+    private static final String CREATE_SQL = "INSERT INTO " + TABLE_NAME
+        + "(name, description, author, charts, settings, modifiedtime, createdtime, uuid) VALUES (?,?,?,?,?,?,?,?)";
+    private static final String UPDATE_SQL = "UPDATE " + TABLE_NAME
+        + " SET name = ?, description = ?, author = ?, charts = ?, settings = ?, modifiedtime = ?, createdtime = ? WHERE uuid = ?";
+
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private static final MapType SETTINGS_TYPE = TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class);
+    private static final CollectionType CHARTS_TYPE = TypeFactory.defaultInstance().constructCollectionType(LinkedList.class, String.class);
+
+    private static ThrowableFunction<ResultSet, DashboardEntity, SQLException> DASHBOARD_DESERIALIZER = resultSet -> {
+        DashboardEntity entity = new DashboardEntity();
+        entity.setUuid(resultSet.getString("uuid"));
+        entity.setName(resultSet.getString("name"));
+        entity.setDescription(resultSet.getString("description"));
+        String settings = resultSet.getString("settings");
+        String charts = resultSet.getString("charts");
+        if (charts != null) {
+            try {
+                entity.setCharts(OBJECT_MAPPER.readValue(charts, CHARTS_TYPE));
+            } catch (IOException e) {
+                throw new SQLException("Error to deserialize JSON as List<String>: " + charts, e);
+            }
+        }
+        if (settings != null) {
+            try {
+                entity.setSettings(OBJECT_MAPPER.readValue(settings, SETTINGS_TYPE));
+            } catch (IOException e) {
+                throw new IllegalArgumentException("Error to deserialize JSON as Map<String, Object>: " + settings, e);
+            }
+        }
+        entity.setAuthor(resultSet.getString("author"));
+        entity.setCreatedTime(resultSet.getLong("createdtime"));
+        entity.setModifiedTime(resultSet.getLong("modifiedtime"));
+        return entity;
+    };
+
+    private static ThrowableConsumer2<PreparedStatement, DashboardEntity, SQLException> DASHBOARD_SERIALIZER = (statement, dashboard) -> {
+        statement.setString(1, dashboard.getName());
+        statement.setString(2, dashboard.getDescription());
+        statement.setString(3, dashboard.getAuthor());
+        if (dashboard.getCharts() != null) {
+            try {
+                statement.setString(4, OBJECT_MAPPER.writeValueAsString(dashboard.getCharts()));
+            } catch (JsonProcessingException e) {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+        }
+        if (dashboard.getSettings() != null) {
+            try {
+                statement.setString(5, OBJECT_MAPPER.writeValueAsString(dashboard.getSettings()));
+            } catch (JsonProcessingException e) {
+                throw new IllegalArgumentException(e.getMessage(), e);
+            }
+        }
+        statement.setLong(6, dashboard.getModifiedTime());
+        statement.setLong(7, dashboard.getCreatedTime());
+        statement.setString(8, dashboard.getUuid());
+    };
+
+    @Inject
+    JDBCMetadataQueryService queryService;
+
+    @Override
+    public Collection<DashboardEntity> findAll() {
+        try {
+            return queryService.query(SELECT_ALL_SQL, DASHBOARD_DESERIALIZER);
+        } catch (SQLException e) {
+            LOGGER.error("Error to execute {}: {}", SELECT_ALL_SQL, e.getMessage(), e);
+            throw new IllegalArgumentException("SQL execution error" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public DashboardEntity getByUUID(String uuid) throws EntityNotFoundException {
+        try {
+            return queryService.queryWithCond(FILTER_BY_UUID_SQL,
+                o -> o.setString(1, uuid), DASHBOARD_DESERIALIZER).stream().findAny().orElseThrow(() -> new EntityNotFoundException("Dashboard (uuid: " + uuid + ") not found"));
+        } catch (SQLException e) {
+            LOGGER.error("Error to execute {} (uuid: {}): {}", FILTER_BY_UUID_SQL, uuid, e.getMessage(), e);
+            throw new IllegalArgumentException("SQL execution error:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public DashboardEntity create(DashboardEntity entity) {
+        Preconditions.checkNotNull(entity, "Entity should not be null");
+        entity.ensureDefault();
+        try {
+            int retCode = queryService.insert(CREATE_SQL, Collections.singletonList(entity), DASHBOARD_SERIALIZER);
+            if (retCode > 0) {
+                return entity;
+            } else {
+                throw new SQLException("Insertion returned: " + retCode);
+            }
+        } catch (SQLException e) {
+            LOGGER.error("Error to insert entity {} (entity: {}): {}", CREATE_SQL, entity.toString(), e.getMessage(), e);
+            throw new IllegalArgumentException("SQL execution error:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public DashboardEntity update(DashboardEntity entity, User user) throws EntityNotFoundException {
+        Preconditions.checkNotNull(entity, "Entity should not be null");
+        Preconditions.checkNotNull(entity.getUuid(), "uuid should not be null");
+        DashboardEntity current = getByUUID(entity.getUuid());
+
+        Preconditions.checkArgument(user.isInRole(User.Role.ADMINISTRATOR)
+            || current.getAuthor().equals(user.getName()), "UPDATE operation is not allowed");
+
+        if (entity.getName() != null) {
+            current.setName(entity.getName());
+        }
+        if (entity.getDescription() != null) {
+            current.setDescription(entity.getDescription());
+        }
+        if (entity.getAuthor() != null) {
+            current.setAuthor(entity.getAuthor());
+        }
+        if (entity.getCharts() != null) {
+            current.setCharts(entity.getCharts());
+        }
+        if (entity.getSettings() != null) {
+            current.setSettings(entity.getSettings());
+        }
+        if (entity.getCreatedTime() > 0) {
+            LOGGER.warn("createdTime  is not updatable but provided: {}, ignore", current.getCreatedTime());
+        }
+        if (entity.getModifiedTime() > 0) {
+            LOGGER.warn("modifiedTime is not updatable but provided: {}, ignore", current.getModifiedTime());
+        }
+        current.ensureDefault();
+        try {
+            if (!queryService.execute(UPDATE_SQL, current, DASHBOARD_SERIALIZER)) {
+                throw new IllegalArgumentException("Failed to update dashboard");
+            }
+        } catch (SQLException e) {
+            LOGGER.error("Error to execute {}: {}", UPDATE_SQL, current, e);
+            throw new IllegalArgumentException("SQL execution error: " + e.getMessage(), e);
+        }
+        return current;
+    }
+
+    @Override
+    public DashboardEntity getByUUIDOrName(String uuid, String name) throws EntityNotFoundException {
+        Preconditions.checkArgument(uuid != null && name != null, "Both uuid and name are null");
+        try {
+            return queryService.queryWithCond(FILTER_BY_UUID_OR_NAME_SQL, o -> {
+                o.setString(0, uuid);
+                o.setString(1, name);
+            }, DASHBOARD_DESERIALIZER).stream().findAny().orElseThrow(() -> new EntityNotFoundException("Dashboard (uuid: " + uuid + " or name: " + name + ") not found"));
+        } catch (SQLException e) {
+            LOGGER.error("Error to execute {} (uuid: {}, name: {}): {}", FILTER_BY_UUID_OR_NAME_SQL, uuid, e.getMessage(), e);
+            throw new IllegalArgumentException("SQL execution error: " + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public DashboardEntity deleteByUUID(String uuid, User user) throws EntityNotFoundException {
+        Preconditions.checkNotNull(uuid, "uuid should not be null");
+        DashboardEntity entity = this.getByUUID(uuid);
+
+        Preconditions.checkArgument(user.isInRole(User.Role.ADMINISTRATOR)
+            || entity.getAuthor().equals(user.getName()), "DELETE operation is not allowed");
+
+        try {
+            queryService.execute(String.format(DELETE_BY_UUID_SQL_FORMAT, uuid));
+        } catch (SQLException e) {
+            LOGGER.error("Error to execute {}: {}", String.format(DELETE_BY_UUID_SQL_FORMAT, uuid), uuid, e.getMessage(), e);
+            throw new IllegalArgumentException("SQL execution error: " + e.getMessage(), e);
+        }
+        return entity;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/DashboardEntityServiceJDBCImplTest.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/DashboardEntityServiceJDBCImplTest.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/DashboardEntityServiceJDBCImplTest.java
new file mode 100644
index 0000000..55fc54f
--- /dev/null
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/DashboardEntityServiceJDBCImplTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.eagle.metadata.store.jdbc;
+
+
+import com.google.inject.Inject;
+import org.apache.eagle.common.security.User;
+import org.apache.eagle.metadata.exceptions.EntityNotFoundException;
+import org.apache.eagle.metadata.model.DashboardEntity;
+import org.apache.eagle.metadata.service.DashboardEntityService;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+
+public class DashboardEntityServiceJDBCImplTest extends JDBCMetadataTestBase {
+    @Inject
+    private DashboardEntityService dashboardEntityService;
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void testDashboardCRUD() throws EntityNotFoundException {
+        User user1 = new User();
+        user1.setName("user1");
+        user1.setRoles(Collections.singleton(User.Role.USER));
+
+        User user2 = new User();
+        user2.setName("user2");
+        user2.setRoles(Collections.singleton(User.Role.USER));
+
+        User admin  = new User();
+        admin.setName("admin");
+        admin.setRoles(Collections.singleton(User.Role.ADMINISTRATOR));
+
+        DashboardEntity entity = new DashboardEntity();
+        {
+            entity.setName("Sample Dashboard");
+            entity.setDescription("A sample dashboard for unit test");
+            entity.setAuthor(user1.getName());
+            entity.setSettings(new HashMap<String, Object>() {
+                {
+                    put("Stringkey", "SettingValue");
+                }
+            });
+            entity.setCharts(Arrays.asList(
+                "{chartType: 'LINE'}",
+                "{chartType}: 'PIE'"
+            ));
+        }
+
+        {
+            Assert.assertEquals(0, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity createdEntity;
+        {
+            createdEntity = dashboardEntityService.create(entity);
+            Assert.assertNotNull(createdEntity.getUuid());
+            Assert.assertTrue(createdEntity.getCreatedTime() > 0);
+            Assert.assertTrue(createdEntity.getModifiedTime() > 0);
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity updatedEntity;
+        {
+            DashboardEntity entityToUpdate = new DashboardEntity();
+            entityToUpdate.setUuid(createdEntity.getUuid());
+            entityToUpdate.setName("Sample Dashboard (Updated)");
+            updatedEntity = dashboardEntityService.update(entityToUpdate, user1);
+            Assert.assertEquals(createdEntity.getUuid(), updatedEntity.getUuid());
+            Assert.assertEquals("Sample Dashboard (Updated)", updatedEntity.getName());
+            Assert.assertEquals(createdEntity.getCreatedTime(), updatedEntity.getCreatedTime());
+            Assert.assertEquals(createdEntity.getAuthor(),updatedEntity.getAuthor());
+            Assert.assertEquals(createdEntity.getCharts(), updatedEntity.getCharts());
+            Assert.assertEquals(createdEntity.getSettings(), updatedEntity.getSettings());
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        DashboardEntity deletedEntity;
+
+        {
+            expectedException.expect(IllegalArgumentException.class);
+            dashboardEntityService.deleteByUUID(updatedEntity.getUuid(), user2);
+            Assert.assertEquals(1, dashboardEntityService.findAll().size());
+        }
+
+        {
+            deletedEntity = dashboardEntityService.deleteByUUID(updatedEntity.getUuid(), admin);
+            Assert.assertEquals(updatedEntity, deletedEntity);
+            Assert.assertEquals(0, dashboardEntityService.findAll().size());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/SiteEntityServiceJDBCImplTest.java
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/SiteEntityServiceJDBCImplTest.java b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/SiteEntityServiceJDBCImplTest.java
index 88ed729..c22cc2b 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/SiteEntityServiceJDBCImplTest.java
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/java/org/apache/eagle/metadata/store/jdbc/SiteEntityServiceJDBCImplTest.java
@@ -58,6 +58,7 @@ public class SiteEntityServiceJDBCImplTest extends JDBCMetadataTestBase {
     public void testInsertSiteEntityWithNullValues() throws SQLException {
         SiteEntity siteEntity = new SiteEntity();
         siteEntity.setSiteId("test-null-site");
+        siteEntity.setSiteName("Test Site Name");
 
         siteEntityService.create(siteEntity);
         String uuid = siteEntity.getUuid();
@@ -69,7 +70,7 @@ public class SiteEntityServiceJDBCImplTest extends JDBCMetadataTestBase {
         SiteEntity siteEntityFromDB = results.iterator().next();
         Assert.assertEquals(uuid, siteEntityFromDB.getUuid());
         Assert.assertEquals("test-null-site", siteEntityFromDB.getSiteId());
-        Assert.assertNull(siteEntityFromDB.getSiteName());
+        Assert.assertEquals("Test Site Name",siteEntityFromDB.getSiteName());
         Assert.assertNull(siteEntityFromDB.getDescription());
         Assert.assertEquals(createdTime, siteEntityFromDB.getCreatedTime());
         Assert.assertEquals(modifiedTime, siteEntityFromDB.getModifiedTime());

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/resources/init.sql
----------------------------------------------------------------------
diff --git a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/resources/init.sql b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/resources/init.sql
index 749032b..1168473 100644
--- a/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/resources/init.sql
+++ b/eagle-core/eagle-metadata/eagle-metadata-jdbc/src/test/resources/init.sql
@@ -18,7 +18,7 @@
 
 CREATE TABLE IF NOT EXISTS applications (
   uuid varchar(50) PRIMARY KEY,
-  appid varchar(100) DEFAULT NULL,
+  appid varchar(100) NOT NULL,
   siteid varchar(100) DEFAULT NULL,
   apptype varchar(30) DEFAULT NULL,
   appmode varchar(10) DEFAULT NULL,
@@ -26,16 +26,31 @@ CREATE TABLE IF NOT EXISTS applications (
   appstatus  varchar(20) DEFAULT NULL,
   configuration mediumtext DEFAULT NULL,
   context mediumtext DEFAULT NULL,
-  createdtime bigint(20) DEFAULT NULL,
-  modifiedtime  bigint(20) DEFAULT NULL
-);
+  createdtime bigint(20) NOT NULL,
+  modifiedtime  bigint(20) NOT NULL,
+  UNIQUE INDEX `appid_UNIQUE` (`appid` ASC))
+COMMENT = 'eagle application metadata';
 
 CREATE TABLE IF NOT EXISTS sites (
   uuid varchar(50) PRIMARY KEY,
-  siteid varchar(100) DEFAULT NULL,
-  sitename varchar(100) DEFAULT NULL,
+  siteid varchar(100) NOT NULL,
+  sitename varchar(100) NOT NULL,
   description varchar(255) DEFAULT NULL,
-  createdtime bigint(20) DEFAULT NULL,
-  modifiedtime  bigint(20) DEFAULT NULL,
-  UNIQUE (siteid)
-);
\ No newline at end of file
+  createdtime bigint(20) NOT NULL,
+  modifiedtime  bigint(20) NOT NULL,
+  UNIQUE INDEX `siteid_UNIQUE` (`siteid` ASC))
+COMMENT = 'eagle site metadata';
+
+CREATE TABLE IF NOT EXISTS `dashboards` (
+  `uuid` VARCHAR(50) NOT NULL,
+  `name` VARCHAR(200) NOT NULL,
+  `description` VARCHAR(500) NOT NULL,
+  `settings` mediumtext NULL,
+  `charts` longtext NULL,
+  `modifiedtime` BIGINT(20) NOT NULL,
+  `createdtime` BIGINT(20) NOT NULL,
+  `author` VARCHAR(50) NOT NULL,
+  PRIMARY KEY (`uuid`),
+  UNIQUE INDEX `uuid_UNIQUE` (`uuid` ASC),
+  UNIQUE INDEX `name_UNIQUE` (`name` ASC))
+COMMENT = 'eagle dashboard metadata';
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/ab5bf107/eagle-server-assembly/src/main/bin/metadata-ddl.sql
----------------------------------------------------------------------
diff --git a/eagle-server-assembly/src/main/bin/metadata-ddl.sql b/eagle-server-assembly/src/main/bin/metadata-ddl.sql
index c24e592..44101a3 100644
--- a/eagle-server-assembly/src/main/bin/metadata-ddl.sql
+++ b/eagle-server-assembly/src/main/bin/metadata-ddl.sql
@@ -16,12 +16,11 @@
 --  *
 --  */
 
-
 -- application framework metadata
 
 CREATE TABLE IF NOT EXISTS applications (
   uuid varchar(50) PRIMARY KEY,
-  appid varchar(100) DEFAULT NULL,
+  appid varchar(100) NOT NULL,
   siteid varchar(100) DEFAULT NULL,
   apptype varchar(30) DEFAULT NULL,
   appmode varchar(10) DEFAULT NULL,
@@ -29,19 +28,34 @@ CREATE TABLE IF NOT EXISTS applications (
   appstatus  varchar(20) DEFAULT NULL,
   configuration mediumtext DEFAULT NULL,
   context mediumtext DEFAULT NULL,
-  createdtime bigint(20) DEFAULT NULL,
-  modifiedtime  bigint(20) DEFAULT NULL
-);
+  createdtime bigint(20) NOT NULL,
+  modifiedtime  bigint(20) NOT NULL,
+  UNIQUE INDEX `appid_UNIQUE` (`appid` ASC))
+COMMENT = 'eagle application metadata';
 
 CREATE TABLE IF NOT EXISTS sites (
   uuid varchar(50) PRIMARY KEY,
-  siteid varchar(100) DEFAULT NULL,
-  sitename varchar(100) DEFAULT NULL,
+  siteid varchar(100) NOT NULL,
+  sitename varchar(100) NOT NULL,
   description varchar(255) DEFAULT NULL,
-  createdtime bigint(20) DEFAULT NULL,
-  modifiedtime  bigint(20) DEFAULT NULL,
-  UNIQUE (siteid)
-);
+  createdtime bigint(20) NOT NULL,
+  modifiedtime  bigint(20) NOT NULL,
+  UNIQUE INDEX `siteid_UNIQUE` (`siteid` ASC))
+COMMENT = 'eagle site metadata';
+
+CREATE TABLE IF NOT EXISTS `dashboards` (
+  `uuid` VARCHAR(50) NOT NULL,
+  `name` VARCHAR(200) NOT NULL,
+  `description` VARCHAR(500) NOT NULL,
+  `settings` mediumtext NULL,
+  `charts` longtext NULL,
+  `modifiedtime` BIGINT(20) NOT NULL,
+  `createdtime` BIGINT(20) NOT NULL,
+  `author` VARCHAR(50) NOT NULL,
+  PRIMARY KEY (`uuid`),
+  UNIQUE INDEX `uuid_UNIQUE` (`uuid` ASC),
+  UNIQUE INDEX `name_UNIQUE` (`name` ASC))
+COMMENT = 'eagle dashboard metadata';
 
 -- eagle security module metadata
 


Mime
View raw message