From 3b5e366b771202527412bad0866d2bc94497a05b Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Fri, 3 Jan 2020 16:06:57 +0530 Subject: [PATCH 1/8] Create cetificates view in devicemgt react app --- .../react-app/package.json | 2 +- .../Certificates/CertificateTable.js | 233 ++++++++++++++++++ .../src/components/Policies/configuration.js | 4 +- .../react-app/src/index.js | 6 + .../Certificates/Certificates.js | 67 +++++ .../src/pages/Dashboard/Dashboard.js | 14 ++ 6 files changed, 323 insertions(+), 3 deletions(-) create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Configurations/Certificates/Certificates.js diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json index 2471a872d9..05bc685731 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json @@ -22,7 +22,7 @@ "react-image-viewer-zoom": "^1.0.36", "react-infinite-scroller": "^1.2.4", "react-leaflet": "^2.4.0", - "react-moment": "^0.9.2", + "react-moment": "^0.9.7", "react-router": "^5.0.1", "react-router-config": "^5.0.1", "react-router-dom": "^5.0.1", diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js new file mode 100644 index 0000000000..97cbb331fa --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import axios from "axios"; +import {Icon, message, Modal, notification, Popconfirm, Select, Table, Tag, Tooltip, Typography} from "antd"; +import TimeAgo from 'javascript-time-ago' +// Load locale-specific relative date/time formatting rules. +import en from 'javascript-time-ago/locale/en' +import {withConfigContext} from "../../../context/ConfigContext"; +import Moment from "react-moment"; + +const {Paragraph, Text} = Typography; + +let config = null; + +class CertificateTable extends React.Component { + constructor(props) { + super(props); + config = this.props.context; + TimeAgo.addLocale(en); + this.state = { + data: [], + pagination: {}, + loading: false, + }; + } + + componentDidMount() { + this.fetch(); + } + + //fetch data from api + fetch = (params = {}) => { + this.setState({loading: true}); + // get current page + const currentPage = (params.hasOwnProperty("page")) ? params.page : 1; + + const extraParams = { + offset: 10 * (currentPage - 1), //calculate the offset + limit: 10, + requireDeviceInfo: true, + }; + + const encodedExtraParams = Object.keys(extraParams) + .map(key => key + '=' + extraParams[key]).join('&'); + + //send request to the invoker + axios.get( + window.location.origin + config.serverConfig.invoker.uri + + '/certificate-mgt/v1.0/admin/certificates', + ).then(res => { + if (res.status === 200) { + const pagination = {...this.state.pagination}; + this.setState({ + loading: false, + data: res.data.data.certificates, + pagination + }); + } + + }).catch((error) => { + if (error.hasOwnProperty("response") && error.response.status === 401) { + //todo display a popop with error + message.error('You are not logged in'); + window.location.href = window.location.origin + '/entgra/login'; + } else { + notification["error"]({ + message: "There was a problem", + duration: 0, + description: + "Error occurred while trying to load devices.", + }); + } + + this.setState({loading: false}); + }); + }; + + handleTableChange = (pagination, filters, sorter) => { + const pager = {...this.state.pagination}; + pager.current = pagination.current; + this.setState({ + pagination: pager, + }); + this.fetch({ + results: pagination.pageSize, + page: pagination.current, + sortField: sorter.field, + sortOrder: sorter.order, + ...filters, + }); + }; + + deleteCertificate = (serialNumber) =>{ + axios.delete( + window.location.origin + config.serverConfig.invoker.uri + + '/certificate-mgt/v1.0/admin/certificates/'+ serialNumber, + {headers: {'Content-Type': 'application/json'}} + ).then(res => { + if (res.status === 200) { + notification["success"]({ + message: "Done", + duration: 4, + description: + "Successfully deleted the certificate.", + }); + } + }).catch((error) => { + if (error.hasOwnProperty("response") && error.response.status === 401) { + //todo display a popop with error + message.error('You are not logged in'); + window.location.href = window.location.origin + '/entgra/login'; + } else { + notification["error"]({ + message: "There was a problem", + duration: 0, + description: + "Error occurred while trying to delete the certificate.", + }); + } + }); + + }; + + columns = [ + { + title: 'Serial Number', + dataIndex: 'serialNumber' + }, + { + title: 'Username', + dataIndex: 'username' + }, + { + title: 'Certificate Version', + dataIndex: 'certificateVersion' + }, + { + title: 'Certificate Serial', + dataIndex: 'certificateserial' + }, + { + title: 'Not Before', + dataIndex: 'notBefore', + render: notBefore => ( + + ) + }, + { + title: 'Not After', + dataIndex: 'notAfter', + render: notAfter => ( + + ) + }, + { + title: 'Subject', + dataIndex: 'subject', + render: subject => ( + {subject} + ) + }, + { + title: 'Issuer', + dataIndex: 'issuer', + render: issuer => ( + {issuer} + ) + }, + { + title: 'Actions', + key: 'actions', + dataIndex: 'serialNumber', + render: (serialNumber) => ( + + {this.deleteCertificate(serialNumber)}} + okText="Ok" + cancelText="Cancel"> + + + + ) + } + ]; + + + + render() { + const {data, pagination, loading} = this.state; + + return ( +
+
+ record.serialNumber} + dataSource={data} + pagination={{ + ...pagination, + size: "small", + // position: "top", + showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices` + // showQuickJumper: true + }} + loading={loading} + onChange={this.handleTableChange} + /> + + + ); + } +} + +export default withConfigContext(CertificateTable); diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Policies/configuration.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Policies/configuration.js index 0ea8062b4a..84a9dc87c8 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Policies/configuration.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Policies/configuration.js @@ -1077,7 +1077,7 @@ const jsonResponse = { "Name": "COSU Profile Configurations", "Panel": [{ "title": "COSU Profile Configurations", - "description": "This policy can be used to configure the profile of COSU Devices.", + "description": "This policy can be used to configure the profile of COSU Certificates.", "PanelItem": [ { "Label": "Restrict Device Operation Time", @@ -2081,4 +2081,4 @@ const jsonResponse = { } }; -export default jsonResponse; \ No newline at end of file +export default jsonResponse; diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js index cd69abffff..32eb56672d 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js @@ -33,6 +33,7 @@ import Roles from "./pages/Dashboard/Roles/Roles"; import DeviceTypes from "./pages/Dashboard/DeviceTypes/DeviceTypes"; import DeviceEnroll from "./pages/Dashboard/Devices/DeviceEnroll"; import AddNewPolicy from "./pages/Dashboard/Policies/AddNewPolicy"; +import Certificates from "./pages/Dashboard/Configurations/Certificates/Certificates"; const routes = [ { @@ -94,6 +95,11 @@ const routes = [ path: '/entgra/devicetypes', component: DeviceTypes, exact: true + }, + { + path: '/entgra/certificates', + component: Certificates, + exact: true } ] } diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Configurations/Certificates/Certificates.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Configurations/Certificates/Certificates.js new file mode 100644 index 0000000000..6b06ef7eb5 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Configurations/Certificates/Certificates.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import { + PageHeader, + Typography, + Breadcrumb, + Icon, +} from "antd"; +import {Link} from "react-router-dom"; +import DeviceTable from "../../../../components/Devices/DevicesTable"; +import CertificateTable from "../../../../components/Configurations/Certificates/CertificateTable"; + +const {Paragraph} = Typography; + +class Certificates extends React.Component { + routes; + + constructor(props) { + super(props); + this.routes = props.routes; + } + + render() { + return ( +
+ + + + Home + + Configurations + Certificates + +
+

Certificates

+ Certificate configurations +
+
+ +
+
+
+ +
+
+ ); + } +} + +export default Certificates; diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js index a9a0313e25..96d7f01bc7 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js @@ -154,6 +154,20 @@ class Dashboard extends React.Component { Device Types + + + Configurations + } + > + + + Certificates + + + Date: Fri, 3 Jan 2020 18:19:57 +0530 Subject: [PATCH 2/8] Fix policy edit not working when policy cache is disabled --- .../org/wso2/carbon/policy/mgt/core/dao/impl/PolicyDAOImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/PolicyDAOImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/PolicyDAOImpl.java index be140bae71..a244d3b6a6 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/PolicyDAOImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/PolicyDAOImpl.java @@ -1038,6 +1038,7 @@ public class PolicyDAOImpl implements PolicyDAO { policy.setProfileId(resultSet.getInt("PROFILE_ID")); policy.setCompliance(resultSet.getString("COMPLIANCE")); policy.setDescription(resultSet.getString("DESCRIPTION")); + policy.setPolicyType(resultSet.getString("POLICY_TYPE")); policy.setUpdated(PolicyManagerUtil.convertIntToBoolean(resultSet.getInt("UPDATED"))); policy.setActive(PolicyManagerUtil.convertIntToBoolean(resultSet.getInt("ACTIVE"))); } From 2ea3af7f12d790790725fe7be6da460b2f276202 Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Mon, 6 Jan 2020 14:41:56 +0530 Subject: [PATCH 3/8] Fix navbar on Devicemgt react app --- .../react-app/src/pages/Dashboard/Dashboard.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js index 96d7f01bc7..acd3a9df1c 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Dashboard.js @@ -65,7 +65,10 @@ class Dashboard extends React.Component { Date: Mon, 6 Jan 2020 15:22:53 +0530 Subject: [PATCH 4/8] Fix login redirection in devicemgt react app --- .../io.entgra.device.mgt.ui/react-app/src/pages/Login.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Login.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Login.js index 3ac1408ba0..8ecd514fd4 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Login.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Login.js @@ -101,7 +101,12 @@ class NormalLoginForm extends React.Component { axios.post(window.location.origin+ config.serverConfig.loginUri, request ).then(res => { if (res.status === 200) { - window.location = window.location.origin+ "/entgra"; + let redirectUrl = window.location.origin + "/entgra"; + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has("redirect")) { + redirectUrl = searchParams.get("redirect"); + } + window.location = redirectUrl; } }).catch(function (error) { if (error.response.status === 400) { From 54e3e6e1514be61162e178c6047bbbb14ac69131 Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Tue, 7 Jan 2020 10:37:50 +0530 Subject: [PATCH 5/8] Change pagesize of groups table in devicemgt react app --- .../react-app/src/components/Groups/GroupsTable.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Groups/GroupsTable.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Groups/GroupsTable.js index 832552001b..22b1f3bc08 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Groups/GroupsTable.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Groups/GroupsTable.js @@ -175,11 +175,9 @@ class GroupsTable extends React.Component { pagination={{ ...pagination, size: "small", - // position: "top", total: data.count, - pageSize: 2, + pageSize: 10, showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups` - // showQuickJumper: true }} loading={loading} onChange={this.handleTableChange} @@ -191,4 +189,4 @@ class GroupsTable extends React.Component { } } -export default withConfigContext(GroupsTable); \ No newline at end of file +export default withConfigContext(GroupsTable); From 9f2b6598a9168444e3282b8e7b4c95e9457d72ec Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Tue, 7 Jan 2020 11:50:21 +0530 Subject: [PATCH 6/8] Fix certification deletion issue in devicemgt react app --- .../mgt/cert/jaxrs/api/CertificateManagementAdminService.java | 3 ++- .../jaxrs/api/impl/CertificateManagementAdminServiceImpl.java | 4 +++- .../Configurations/Certificates/CertificateTable.js | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java index 6fed010f65..993060707a 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java @@ -306,8 +306,9 @@ public interface CertificateManagementAdminService { @DELETE @Path("/{serialNumber}") + @Consumes(MediaType.WILDCARD) @ApiOperation( - consumes = MediaType.APPLICATION_JSON, + consumes = MediaType.WILDCARD, produces = MediaType.APPLICATION_JSON, httpMethod = "DELETE", value = "Deleting an SSL Certificate", diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java index 697aadc1a8..506fcac51e 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java @@ -18,6 +18,7 @@ package org.wso2.carbon.certificate.mgt.cert.jaxrs.api.impl; +import javax.ws.rs.core.MediaType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.certificate.mgt.cert.jaxrs.api.CertificateManagementAdminService; @@ -145,6 +146,7 @@ public class CertificateManagementAdminServiceImpl implements CertificateManagem @DELETE @Path("/{serialNumber}") + @Consumes(MediaType.WILDCARD) public Response removeCertificate(@PathParam("serialNumber") String serialNumber) { RequestValidationUtil.validateSerialNumber(serialNumber); @@ -328,4 +330,4 @@ public class CertificateManagementAdminServiceImpl implements CertificateManagem } return Response.status(Response.Status.OK).entity("invalid").build(); } -} \ No newline at end of file +} diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js index 97cbb331fa..1dcb03e397 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Configurations/Certificates/CertificateTable.js @@ -114,6 +114,7 @@ class CertificateTable extends React.Component { {headers: {'Content-Type': 'application/json'}} ).then(res => { if (res.status === 200) { + this.fetch(); notification["success"]({ message: "Done", duration: 4, @@ -202,8 +203,6 @@ class CertificateTable extends React.Component { } ]; - - render() { const {data, pagination, loading} = this.state; From 8d3ed2ed885ea3ef5a305053bfa16d20f14fa50c Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Tue, 7 Jan 2020 12:27:42 +0530 Subject: [PATCH 7/8] Fix the user deletion issue in devicemgt react app --- .../device/mgt/jaxrs/service/api/UserManagementService.java | 2 ++ .../mgt/jaxrs/service/impl/UserManagementServiceImpl.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java index 6000845829..d50f4cb50b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java @@ -372,8 +372,10 @@ public interface UserManagementService { @DELETE @Path("/{username}") + @Consumes(MediaType.WILDCARD) @ApiOperation( httpMethod = "DELETE", + consumes = MediaType.WILDCARD, value = "Deleting a User", notes = "When an employee leaves the organization, you can remove the user details from WSO2 IoTS using " + "this REST API.", diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java index 52aeb46e40..251aa0eb64 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java @@ -183,7 +183,7 @@ public class UserManagementServiceImpl implements UserManagementService { DEFAULT_SUBSCRIBER + "' is missing in the system"); } } - + String[] roles = new String[tmpRoles.size()]; tmpRoles.toArray(roles); @@ -363,6 +363,7 @@ public class UserManagementServiceImpl implements UserManagementService { @DELETE @Path("/{username}") + @Consumes(MediaType.WILDCARD) @Override public Response removeUser(@PathParam("username") String username, @QueryParam("domain") String domain) { if (domain != null && !domain.isEmpty()) { From 40bf2ca1e9ae5c6e57c78a05ba71153cad7e6a22 Mon Sep 17 00:00:00 2001 From: Shamalka Navod Date: Wed, 8 Jan 2020 10:30:53 +0000 Subject: [PATCH 8/8] UI for device enrollment reports --- .../react-app/package.json | 2 + .../react-app/src/App.js | 24 +- .../Reports/Templates/DeviceStatusReport.js | 125 +++++++ .../Reports/Templates/EnrollmentTypeReport.js | 133 ++++++++ .../EnrollmentsVsUnenrollmentsReport.js | 172 ++++++++++ .../components/Reports/Widgets/CountWidget.js | 63 ++++ .../components/Reports/Widgets/PieChart.js | 322 ++++++++++++++++++ .../react-app/src/index.js | 23 ++ .../Reports/ReportDurationItemList.js | 152 +++++++++ .../src/pages/Dashboard/Reports/Reports.js | 51 +-- .../service/api/ReportManagementService.java | 75 +++- .../impl/ReportManagementServiceImpl.java | 30 +- .../report/mgt/ReportManagementService.java | 7 +- .../carbon/device/mgt/core/dao/DeviceDAO.java | 5 + .../dao/impl/device/GenericDeviceDAOImpl.java | 87 ++++- .../dao/impl/device/OracleDeviceDAOImpl.java | 7 +- .../impl/device/PostgreSQLDeviceDAOImpl.java | 7 +- .../impl/device/SQLServerDeviceDAOImpl.java | 7 +- .../mgt/ReportManagementServiceImpl.java | 22 +- 19 files changed, 1251 insertions(+), 63 deletions(-) create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/DeviceStatusReport.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentTypeReport.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentsVsUnenrollmentsReport.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/CountWidget.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/PieChart.js create mode 100644 components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/ReportDurationItemList.js diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json index 05bc685731..04c40c1edc 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/package.json @@ -8,6 +8,7 @@ "acorn": "^6.2.0", "antd": "^3.23.5", "axios": "^0.18.1", + "bizcharts": "^3.5.6", "bootstrap": "^4.3.1", "javascript-time-ago": "^2.0.1", "keymirror": "^0.1.1", @@ -35,6 +36,7 @@ "storm-react-diagrams": "^5.2.1" }, "devDependencies": { + "@antv/data-set": "^0.10.2", "@babel/core": "^7.5.4", "@babel/plugin-proposal-class-properties": "^7.5.0", "@babel/preset-env": "^7.5.4", diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/App.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/App.js index 5f8f429e8e..078ceaaaeb 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/App.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/App.js @@ -24,7 +24,7 @@ import { Redirect, Switch, } from 'react-router-dom'; import axios from "axios"; -import {Layout, Spin, Result} from "antd"; +import {Layout, Spin, Result, message, notification} from "antd"; import ConfigContext from "./context/ConfigContext"; const {Content} = Layout; @@ -93,6 +93,7 @@ class App extends React.Component { config: config }); } + this.getDeviceTypes(config); }).catch((error) => { if (error.hasOwnProperty("response") && error.response.status === 401) { const redirectUrl = encodeURI(window.location.href); @@ -115,9 +116,30 @@ class App extends React.Component { }); }; + getDeviceTypes = (config) => { + axios.get( + window.location.origin + "/entgra-ui-request-handler/invoke/device-mgt/v1.0/device-types", + ).then(res => { + config.deviceTypes = JSON.parse(res.data.data); + this.setState({ + config: config, + loading: false + }); + }).catch((error) => { + + notification["error"]({ + message: "There was a problem", + duration: 0, + description:"Error occurred while trying to load device types.", + }); + + }); + }; + render() { const {loading, error} = this.state; + const abc = this.state.deviceTypes; const applicationView = ( diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/DeviceStatusReport.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/DeviceStatusReport.js new file mode 100644 index 0000000000..44249aba0a --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/DeviceStatusReport.js @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import { + PageHeader, + Typography, + Breadcrumb, + Icon, + Tag, + Radio, Select, Button, Card, + Row, Col, message, notification +} from "antd"; + +import {Link} from "react-router-dom"; +import PoliciesTable from "../../../components/Policies/PoliciesTable"; +import DevicesTable from "../../../components/Devices/DevicesTable"; +import DateRangePicker from "../../../components/Reports/DateRangePicker"; +import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable"; +import PieChart from "../../../components/Reports/Widgets/PieChart"; +import axios from "axios"; +import CountWidget from "../../../components/Reports/Widgets/CountWidget"; +import {withConfigContext} from "../../../context/ConfigContext"; +const {Paragraph} = Typography; +const { CheckableTag } = Tag; + +const { Option } = Select; +let config = null; + + +class DeviceStatusReport extends React.Component { + routes; + + constructor(props) { + super(props); + this.routes = props.routes; + config = this.props.context; + const { reportData } = this.props.location; + this.state = { + selectedTags: ['Enrolled'], + paramsObject:{ + from:reportData.duration[0], + to:reportData.duration[1] + }, + statsObject:{}, + statArray:[{item:"ACTIVE",count:0},{item:"INACTIVE",count:0},{item:"REMOVED",count:0}] + }; + } + + onClickPieChart = (value) => { + console.log(value.data.point.item); + const chartValue = value.data.point.item; + let tempParamObj = this.state.paramsObject; + + tempParamObj.status = chartValue; + + + this.setState({paramsObject:tempParamObj}); + console.log(this.state.paramsObject) + }; + + render() { + const { statArray } = this.state; + const { reportData } = this.props.location; + + const params = {...this.state.paramsObject}; + return ( +
+ + + + Home + + Report + +
+

Summary of enrollments

+
+ + +
+
+ +
+ + + + +
+ +
+ +
+
+
+ +
+
+ ); + } +} + +export default withConfigContext(DeviceStatusReport); diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentTypeReport.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentTypeReport.js new file mode 100644 index 0000000000..595aa8856e --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentTypeReport.js @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import { + PageHeader, + Typography, + Breadcrumb, + Icon, + Tag, + Radio, Select, Button, Card, + Row, Col, message, notification +} from "antd"; + +import {Link} from "react-router-dom"; +import PoliciesTable from "../../../components/Policies/PoliciesTable"; +import DevicesTable from "../../../components/Devices/DevicesTable"; +import DateRangePicker from "../../../components/Reports/DateRangePicker"; +import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable"; +import PieChart from "../../../components/Reports/Widgets/PieChart"; +import axios from "axios"; +import CountWidget from "../../../components/Reports/Widgets/CountWidget"; +import {withConfigContext} from "../../../context/ConfigContext"; +const {Paragraph} = Typography; +const { CheckableTag } = Tag; + +const { Option } = Select; +let config = null; + + +class EnrollmentTypeReport extends React.Component { + routes; + + constructor(props) { + super(props); + this.routes = props.routes; + config = this.props.context; + const { reportData } = this.props.location; + this.state = { + paramsObject:{ + from:reportData.duration[0], + to:reportData.duration[1] + } + }; + + + console.log(reportData.duration); + } + + setParam = (tempParamObj, type, value) => { + if(type==="status"){ + tempParamObj.status = value; + if(tempParamObj.status) { + delete tempParamObj.status; + } + } else if(type=="ownership"){ + tempParamObj.ownership = value; + if(value=="ALL" && tempParamObj.ownership) { + delete tempParamObj.ownership; + } + } + }; + + onClickPieChart = (value) => { + const chartValue = value.data.point.item; + let tempParamObj = this.state.paramsObject; + + console.log(chartValue) + + tempParamObj.ownership = chartValue; + + this.setState({paramsObject:tempParamObj}); + }; + + render() { + const { reportData } = this.props.location; + + const params = {...this.state.paramsObject}; + return ( +
+ + + + Home + + Report + +
+

Summary of enrollments

+ +
+ +
+ + + + + + + +
+ +
+ +
+
+
+ +
+
+ ); + } +} + +export default withConfigContext(EnrollmentTypeReport); diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentsVsUnenrollmentsReport.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentsVsUnenrollmentsReport.js new file mode 100644 index 0000000000..b6ab7c2935 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Templates/EnrollmentsVsUnenrollmentsReport.js @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import { + PageHeader, + Typography, + Breadcrumb, + Icon, + Tag, + Radio, Select, Button, Card, + Row, Col, message, notification +} from "antd"; + +import {Link, Redirect} from "react-router-dom"; +import PoliciesTable from "../../../components/Policies/PoliciesTable"; +import DevicesTable from "../../../components/Devices/DevicesTable"; +import DateRangePicker from "../../../components/Reports/DateRangePicker"; +import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable"; +import PieChart from "../../../components/Reports/Widgets/PieChart"; +import axios from "axios"; +import CountWidget from "../../../components/Reports/Widgets/CountWidget"; +import {withConfigContext} from "../../../context/ConfigContext"; +const {Paragraph} = Typography; +const { CheckableTag } = Tag; + +const { Option } = Select; +let config = null; + + +class EnrollmentsVsUnenrollmentsReport extends React.Component { + routes; + + constructor(props) { + super(props); + this.routes = props.routes; + config = this.props.context; + const { reportData } = this.props.location; + + this.state = { + paramsObject:{ + from:reportData? reportData.duration[0]: "2019-01-01", + to:reportData? reportData.duration[1]: "2019-01-01" + }, + redirect: false + }; + + this.redirectToHome(); + console.log(this.state.paramsObject); + } + + setParam = (tempParamObj, type, value) => { + if(type==="status"){ + tempParamObj.status = value; + if(tempParamObj.status) { + delete tempParamObj.status; + } + } else if(type=="ownership"){ + tempParamObj.ownership = value; + if(value=="ALL" && tempParamObj.ownership) { + delete tempParamObj.ownership; + } + } + }; + + redirectToHome = () => { + return + }; + + setRedirect = (reportData) => { + if(!reportData){ + this.setState({ + redirect: true + }) + } + }; + + renderRedirect = () => { + if (this.state.redirect) { + return + } + } + + onClickPieChart = (value) => { + const chartValue = value.data.point.item; + let tempParamObj = this.state.paramsObject; + + console.log(chartValue) + + // tempParamObj.status = chartValue; + + if(chartValue==="Enrollments"){ + tempParamObj.status = "ACTIVE&status=INACTIVE" + }else{ + tempParamObj.status = "REMOVED" + } + + + this.setState({paramsObject:tempParamObj}); + }; + + render() { + const { reportData } = this.props.location; + + console.log("======") + console.log(reportData) + console.log("======") + + let reportDataClone = { + params: ["ACTIVE"], + duration: ["2020-01-01","2020-01-01"] + }; + + const params = {...this.state.paramsObject}; + return ( +
+ +
{!reportData ? ( + + ) : ( + + + + Home + + Report + +
+

Summary of enrollments

+ +
+ +
+ + + + + + + +
+ +
+ +
+
+ + )}
+
+ ); + } +} + +export default withConfigContext(EnrollmentsVsUnenrollmentsReport); diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/CountWidget.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/CountWidget.js new file mode 100644 index 0000000000..f1854f8737 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/CountWidget.js @@ -0,0 +1,63 @@ +import React from "react"; + +import {Card, Col, Icon} from "antd"; +import {Link} from "react-router-dom"; + +class CountWidget extends React.Component { + + constructor(props) { + super(props); + this.routes = props.routes; + this.state = { + statArray:[] + } + } + + componentDidMount() { + this.setState({statArray:this.props.statArray}) + console.log("$$$$") + console.log(this.props.statArray) + } + + + render() { + const countObj = [ + {item:"All",count:100}, + {item:"Enrolled",count:80}, + {item:"Unenrolled",count:20}]; + + const { statArray } = this.state; + + let card = statArray.map((data) => + // + // + //

{data.item} Devices: {data.count}

+ // + //
+
+ + +
+

{data.item}

+

{data.count}

+ {/*

{data.duration}

*/} + {/**/} +
+
+ + ) + + return( +
+ {card} +
+ + ) + } +} + +export default CountWidget; \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/PieChart.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/PieChart.js new file mode 100644 index 0000000000..f66eac10b7 --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/components/Reports/Widgets/PieChart.js @@ -0,0 +1,322 @@ +import React from "react"; +import { + G2, + Chart, + Geom, + Axis, + Tooltip, + Coord, + Label, + Legend, + View, + Guide, + Shape, + Facet, + Util +} from "bizcharts"; +import DataSet from "@antv/data-set"; +import axios from "axios"; +import {message, notification} from "antd"; +import {withConfigContext} from "../../../context/ConfigContext"; + +let config = null; + +class PieChart extends React.Component { + + constructor(props) { + super(props); + config = this.props.context; + this.state = { + loading:true, + statArray:[] + }; + + } + + componentDidMount() { + let { statArray } = this.state; + const { reportData } = this.props; + let params = { + status: reportData.params[0], + from: reportData.duration[0], + to: reportData.duration[1] + }; + + const urlSet = { + paramsList:reportData.params, + duration:reportData.duration + }; + + console.log(urlSet) + + if(reportData.params[0]==="Enrollments"){ + this.getEnrollmentsVsUnenrollmentsCount(params, urlSet) + }else if(reportData.params[0]==="BYOD"){ + this.getEnrollmentTypeCount(params, urlSet); + }else{ + this.getCount(params, urlSet); + } + } + + clicked = () => { + console.log("Clicked...!!") + }; + + onChartChange = (data) => { + this.props.onClickPieChart(data); + }; + + statArray = []; + + //Call count APIs and get count for given parameters, then create data object to build pie chart + getCount = (params, urlSet) => { + + this.setState({loading: true}); + + let { statArray } = this.state; + + console.log(urlSet); + + const urlArray = []; + + urlSet.paramsList.map((data) => { + const paramsObj = { + status:data, + from:urlSet.duration[0], + to:urlSet.duration[1] + } + // console.log(paramsObj) + const encodedExtraParams = Object.keys(paramsObj) + .map(key => key + '=' + paramsObj[key]).join('&'); + const apiUrl = window.location.origin + config.serverConfig.invoker.uri + + config.serverConfig.invoker.deviceMgt + + "/reports/devices/count?" + encodedExtraParams; + + urlArray.push(axios.get(apiUrl, data)); + }); + + console.log(urlArray) + + + axios.all(urlArray).then(res => { + + res.map((response) => { + if(response.status === 200){ + let countData = {item:response.config[0], count:parseInt(response.data.data)} + statArray.push(countData); + } + }) + this.setState({statArray}) + }).catch((error) => { + if (error.hasOwnProperty("response") && error.response.status === 401) { + //todo display a popup with error + message.error('You are not logged in'); + window.location.href = window.location.origin + '/entgra/login'; + } else { + notification["error"]({ + message: "There was a problem", + duration: 0, + description:"Error occurred while trying to get device count.", + }); + } + }); + }; + + //Call count APIs and get count for given parameters, then create data object to build pie chart + getEnrollmentsVsUnenrollmentsCount = (params, urlSet) => { + + this.setState({loading: true}); + + let { statArray } = this.state; + + console.log(urlSet); + + const urlArray = []; + + urlSet.paramsList.map((data) => { + const paramsObj = { + from:urlSet.duration[0], + to:urlSet.duration[1] + } + const encodedExtraParams = Object.keys(paramsObj) + .map(key => key + '=' + paramsObj[key]).join('&'); + + let apiUrl; + if(data==="Enrollments"){ + apiUrl = window.location.origin + config.serverConfig.invoker.uri + + config.serverConfig.invoker.deviceMgt + + "/reports/devices/count?status=ACTIVE&status=INACTIVE&" + encodedExtraParams; + }else{ + apiUrl = window.location.origin + config.serverConfig.invoker.uri + + config.serverConfig.invoker.deviceMgt + + "/reports/devices/count?status=REMOVED&" + encodedExtraParams; + } + + urlArray.push(axios.get(apiUrl, data)); + }); + + console.log(urlArray) + + + axios.all(urlArray).then(res => { + res.map((response) => { + if(response.status === 200){ + let countData = {item:response.config[0], count:parseInt(response.data.data)} + statArray.push(countData); + } + }) + this.setState({statArray}) + }).catch((error) => { + if (error.hasOwnProperty("response") && error.response.status === 401) { + //todo display a popup with error + message.error('You are not logged in'); + window.location.href = window.location.origin + '/entgra/login'; + } else { + notification["error"]({ + message: "There was a problem", + duration: 0, + description:"Error occurred while trying to get device count.", + }); + } + }); + }; + + //Call count APIs and get count for given parameters, then create data object to build pie chart + getEnrollmentTypeCount = (params, urlSet) => { + + this.setState({loading: true}); + + let { statArray } = this.state; + + console.log(urlSet); + + const urlArray = []; + + urlSet.paramsList.map((data) => { + const paramsObj = { + ownership:data, + from:urlSet.duration[0], + to:urlSet.duration[1] + } + const encodedExtraParams = Object.keys(paramsObj) + .map(key => key + '=' + paramsObj[key]).join('&'); + const apiUrl = window.location.origin + config.serverConfig.invoker.uri + + config.serverConfig.invoker.deviceMgt + + "/reports/devices/count?" + encodedExtraParams; + + urlArray.push(axios.get(apiUrl, data)); + }); + + console.log(urlArray) + + + axios.all(urlArray).then(res => { + res.map((response) => { + if(response.status === 200){ + let countData = {item:response.config[0], count:parseInt(response.data.data)} + statArray.push(countData); + } + }) + this.setState({statArray}) + }).catch((error) => { + if (error.hasOwnProperty("response") && error.response.status === 401) { + //todo display a popup with error + message.error('You are not logged in'); + window.location.href = window.location.origin + '/entgra/login'; + } else { + notification["error"]({ + message: "There was a problem", + duration: 0, + description:"Error occurred while trying to get device count.", + }); + } + }); + }; + + render() { + const { DataView } = DataSet; + const { Html } = Guide; + const { statArray , loading} = this.state; + + const dv = new DataView(); + dv.source(statArray).transform({ + type: "percent", + field: "count", + dimension: "item", + as: "percent" + }); + const cols = { + percent: { + formatter: val => { + val = val * 100 + "%"; + return val; + } + } + }; + + return ( +
+ + + + + + + + +
+ { + percent = percent * 100 + "%"; + return { + name: item, + value: percent + }; + } + ]} + style={{ + lineWidth: 1, + stroke: "#fff" + }} + > + +
+ +
+
+ ); + } +} + +export default withConfigContext(PieChart); \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js index 32eb56672d..2f9c0179d3 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js @@ -34,6 +34,9 @@ import DeviceTypes from "./pages/Dashboard/DeviceTypes/DeviceTypes"; import DeviceEnroll from "./pages/Dashboard/Devices/DeviceEnroll"; import AddNewPolicy from "./pages/Dashboard/Policies/AddNewPolicy"; import Certificates from "./pages/Dashboard/Configurations/Certificates/Certificates"; +import ReportDurationItemList from "./pages/Dashboard/Reports/ReportDurationItemList"; +import EnrollmentsVsUnenrollmentsReport from "./components/Reports/Templates/EnrollmentsVsUnenrollmentsReport"; +import EnrollmentTypeReport from "./components/Reports/Templates/EnrollmentTypeReport"; const routes = [ { @@ -100,6 +103,26 @@ const routes = [ path: '/entgra/certificates', component: Certificates, exact: true + }, + { + path: '/entgra/reportList', + component: ReportDurationItemList, + exact: true + }, + { + path: '/entgra/enrollmentsvsunenrollments', + component: EnrollmentsVsUnenrollmentsReport, + exact: true + }, + { + path: '/entgra/enrollmenttype', + component: EnrollmentTypeReport, + exact: true + }, + { + path: '/entgra/devicestatus', + component: DeviceStatusReport, + exact: true } ] } diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/ReportDurationItemList.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/ReportDurationItemList.js new file mode 100644 index 0000000000..13804d845e --- /dev/null +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/ReportDurationItemList.js @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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 + * + * 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. + */ + +import React from "react"; +import { + Icon, + Col, + Row, Select, + Radio, Card, + Button +} from "antd"; + +import {Link} from "react-router-dom"; +import moment from "moment"; + + +const { Option } = Select; + +class ReportDurationItemList extends React.Component { + + constructor(props) { + super(props); + this.state = { + reportParams:["ACTIVE","INACTIVE","REMOVED"], + enrollmentsVsUnenrollmentsParams:["Enrollments", "Unenrollments"], + enrollmentTypeParams:["BYOD", "COPE"] + } + } + + durationItemArray = [ + {name:"Daily Report", description:"Enrollments of today", duration:[moment().format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}, + {name:"Weekly Report", description:"Enrollments of last 7 days", duration:[moment().subtract(6, 'days').format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}, + {name:"Monthly Report", description:"Enrollments of last month", duration:[moment().subtract(29, 'days').format('YYYY-MM-DD'), moment().add(1, 'days').format('YYYY-MM-DD')]}] + + + render(){ + + let itemStatus = this.durationItemArray.map((data) => + + + + +
+ +

{data.name}

+

{data.description}

+ {/*

{data.duration}

*/} +
+
+ + + ); + + let itemEnrollmentsVsUnenrollments = this.durationItemArray.map((data) => + + + + +
+ +

{data.name}

+

{data.description}

+
+
+ + + ); + + let itemEnrollmentType = this.durationItemArray.map((data) => + + + + +
+ +

{data.name}

+

{data.description}

+
+
+ + + ); + return( +
+
+ + {itemStatus} + +
+ +
+ + {itemEnrollmentsVsUnenrollments} + +
+ +
+ + {itemEnrollmentType} + +
+
+ ) + } +} + +export default ReportDurationItemList; \ No newline at end of file diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js index 0f271fa05a..17abf992f7 100644 --- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js +++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/pages/Dashboard/Reports/Reports.js @@ -24,9 +24,7 @@ import { Icon } from "antd"; import {Link} from "react-router-dom"; -import ReportDeviceTable from "../../../components/Devices/ReportDevicesTable"; -import Filter from "../../../components/Reports/Filter"; -import DateRangePicker from "../../../components/Reports/DateRangePicker"; +import ReportDurationItemList from "./ReportDurationItemList"; const {Paragraph} = Typography; @@ -37,17 +35,16 @@ class Reports extends React.Component { super(props); this.routes = props.routes; this.state = { - paramsObject:{} + paramsObject:{}, } } - - //Get modified value from datepicker and set it to paramsObject + //Get modified value from datepicker and set it to paramsObject updateDurationValue = (modifiedFromDate,modifiedToDate) => { let tempParamObj = this.state.paramsObject; tempParamObj.from = modifiedFromDate; tempParamObj.to = modifiedToDate; this.setState({paramsObject:tempParamObj}); - } + }; //Get modified value from filters and set it to paramsObject updateFiltersValue = (modifiedValue,filterType) => { @@ -64,13 +61,13 @@ class Reports extends React.Component { } } this.setState({paramsObject:tempParamObj}); - } + }; render() { //Arrays for filters const statusObj = ['ALL','ACTIVE','INACTIVE','REMOVED']; const ownershipObj = ['ALL','BYOD','COPE']; - + const params = {...this.state.paramsObject}; return ( @@ -84,41 +81,7 @@ class Reports extends React.Component {

Reports

- - To generate a report, select a duration and apply filters - -
-
- - - - - - - - - - - - -
Select DurationDevice StatusDevice Ownership
- - - - - -
-
-
- -
+
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java index 6f3427181b..37325c58b4 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java @@ -42,6 +42,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.List; @SwaggerDefinition( info = @Info( @@ -127,7 +128,7 @@ public interface ReportManagementService { @ApiParam( name = "status", value = "Provide the device status details, such as active or inactive.") - @QueryParam("status") String status, + @QueryParam("status") List status, @ApiParam( name = "ownership", allowableValues = "BYOD, COPE", @@ -157,4 +158,76 @@ public interface ReportManagementService { defaultValue = "5") @QueryParam("limit") int limit) throws ReportManagementException; + + + @GET + @Path("/devices/count") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Getting Details of Registered Devices", + notes = "Provides details of all the devices enrolled with WSO2 IoT Server.", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the list of devices.", + response = DeviceList.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid device status type received. \n" + + "Valid status types are NEW | CHECKED", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n There are no devices.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. " + + "\n Server error occurred while fetching the device list.", + response = ErrorResponse.class) + }) + Response getDevicesByDurationCount( + @ApiParam( + name = "status", + value = "Provide the device status details, such as active or inactive.") + @QueryParam("status") List status, + @ApiParam( + name = "ownership", + allowableValues = "BYOD, COPE", + value = "Provide the ownership status of the device. The following values can be assigned:\n" + + "- BYOD: Bring Your Own Device\n" + + "- COPE: Corporate-Owned, Personally-Enabled") + @QueryParam("ownership") String ownership, + @ApiParam( + name = "fromDate", + value = "Start date of the duration", + required = true) + @QueryParam("from") String fromDate, + @ApiParam( + name = "toDate", + value = "end date of the duration", + required = true) + @QueryParam("to") String toDate) throws ReportManagementException; } \ No newline at end of file diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ReportManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ReportManagementServiceImpl.java index c06542ea15..cebd3c1cbe 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ReportManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ReportManagementServiceImpl.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationResult; +import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; @@ -54,13 +55,13 @@ public class ReportManagementServiceImpl implements ReportManagementService { @Path("/devices") @Override public Response getDevicesByDuration( - @QueryParam("status") String status, + @QueryParam("status") List status, @QueryParam("ownership") String ownership, @QueryParam("from") String fromDate, @QueryParam("to") String toDate, @DefaultValue("0") @QueryParam("offset") int offset, - @DefaultValue("5") + @DefaultValue("10") @QueryParam("limit") int limit) { try { RequestValidationUtil.validatePaginationParameters(offset, limit); @@ -68,15 +69,12 @@ public class ReportManagementServiceImpl implements ReportManagementService { PaginationResult result; DeviceList devices = new DeviceList(); - if (!StringUtils.isBlank(status)) { - request.setStatus(status); - } if (!StringUtils.isBlank(ownership)) { request.setOwnership(ownership); } result = DeviceMgtAPIUtils.getReportManagementService() - .getDevicesByDuration(request, fromDate, toDate); + .getDevicesByDuration(request, status, fromDate, toDate); if (result.getData().isEmpty()) { String msg = "No devices have enrolled between " + fromDate + " to " + toDate + " or doesn't match with" + @@ -94,4 +92,24 @@ public class ReportManagementServiceImpl implements ReportManagementService { new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); } } + + @GET + @Path("/devices/count") + @Override + public Response getDevicesByDurationCount( + @QueryParam("status") List status, + @QueryParam("ownership") String ownership, + @QueryParam("from") String fromDate, + @QueryParam("to") String toDate) { + int deviceCount; + try { + deviceCount = DeviceMgtAPIUtils.getReportManagementService().getDevicesByDurationCount(status, ownership, fromDate, toDate); + return Response.status(Response.Status.OK).entity(deviceCount).build(); + } catch (ReportManagementException e) { + String errorMessage = "Error while retrieving device count."; + log.error(errorMessage, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build(); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/report/mgt/ReportManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/report/mgt/ReportManagementService.java index b4fe1a98e4..f788508f9e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/report/mgt/ReportManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/report/mgt/ReportManagementService.java @@ -21,6 +21,8 @@ import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; +import java.util.List; + /** * This is the service class for reports which connects with DAO layer */ @@ -36,6 +38,9 @@ public interface ReportManagementService { * @throws {@Link DeviceManagementException} When error occurred while validating device list page size * @throws {@Link ReportManagementException} When failed to retrieve devices. */ - PaginationResult getDevicesByDuration(PaginationRequest request, String fromDate, String toDate) + PaginationResult getDevicesByDuration(PaginationRequest request, List statusList, String fromDate, String toDate) + throws ReportManagementException; + + int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate) throws ReportManagementException; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java index 695a9766c8..6ff205517f 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java @@ -552,10 +552,15 @@ public interface DeviceDAO { * */ List getDevicesByDuration(PaginationRequest request, + List statusList, int tenantId, String fromDate, String toDate) throws DeviceManagementDAOException; + int getDevicesByDurationCount( + List statusList, String ownership, String fromDate, String toDate, int tenantId) + throws DeviceManagementDAOException; + /** * Retrieve device location information * @param deviceIdentifier Device Identifier object diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java index e316baa78c..e8ce9b0cca 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java @@ -455,12 +455,12 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { } @Override - public List getDevicesByDuration(PaginationRequest request, int tenantId, + public List getDevicesByDuration(PaginationRequest request, List statusList, int tenantId, String fromDate, String toDate) throws DeviceManagementDAOException { List devices; - String deviceStatus = request.getStatus(); String ownership = request.getOwnership(); + boolean isStatusProvided = false; String sql = "SELECT " + "d.ID AS DEVICE_ID, " + @@ -479,9 +479,15 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { "e.TENANT_ID = ? AND " + "e.DATE_OF_ENROLMENT BETWEEN ? AND ?"; - if (deviceStatus != null) { - sql = sql + " AND e.STATUS = ?"; + //Add the query for status + StringBuilder sqlBuilder = new StringBuilder(sql); + isStatusProvided = buildStatusQuery(statusList, sqlBuilder); + sql = sqlBuilder.toString(); + + if(statusList != null && !statusList.isEmpty()){ + isStatusProvided = true; } + if (ownership != null) { sql = sql + " AND e.OWNERSHIP = ?"; } @@ -494,8 +500,10 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { stmt.setInt(paramIdx++, tenantId); stmt.setString(paramIdx++, fromDate); stmt.setString(paramIdx++, toDate); - if (deviceStatus != null) { - stmt.setString(paramIdx++, deviceStatus); + if (isStatusProvided) { + for (String status : statusList) { + stmt.setString(paramIdx++, status); + } } if (ownership != null) { stmt.setString(paramIdx++, ownership); @@ -518,6 +526,73 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { return devices; } + @Override + public int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate, int tenantId) throws DeviceManagementDAOException { + int deviceCount = 0; + boolean isStatusProvided = false; + + String sql = "SELECT " + + "COUNT(d.ID) AS DEVICE_COUNT " + + "FROM DM_DEVICE AS d , DM_ENROLMENT AS e , DM_DEVICE_TYPE AS t " + + "WHERE d.ID = e.DEVICE_ID AND " + + "d.DEVICE_TYPE_ID = t.ID AND " + + "e.TENANT_ID = ? AND " + + "e.DATE_OF_ENROLMENT BETWEEN ? AND ?"; + + //Add the query for status + StringBuilder sqlBuilder = new StringBuilder(sql); + isStatusProvided = buildStatusQuery(statusList, sqlBuilder); + sql = sqlBuilder.toString(); + + if (ownership != null) { + sql = sql + " AND e.OWNERSHIP = ?"; + } + + try (Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + int paramIdx = 1; + stmt.setInt(paramIdx++, tenantId); + stmt.setString(paramIdx++, fromDate); + stmt.setString(paramIdx++, toDate); + if (isStatusProvided) { + for (String status : statusList) { + stmt.setString(paramIdx++, status); + } + } + if (ownership != null) { + stmt.setString(paramIdx++, ownership); + } + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + deviceCount = rs.getInt("DEVICE_COUNT"); + } + } + } catch (SQLException e) { + String msg = "Error occurred while retrieving information of all " + + "registered devices under tenant id " + tenantId; + log.error(msg, e); + throw new DeviceManagementDAOException(msg, e); + } + return deviceCount; + } + + protected boolean buildStatusQuery(List statusList, StringBuilder sqlBuilder) { + if (statusList != null && !statusList.isEmpty() && !statusList.get(0).isEmpty()) { + sqlBuilder.append(" AND e.STATUS IN("); + for (int i = 0; i < statusList.size(); i++) { + sqlBuilder.append("?"); + if (i != statusList.size() - 1) { + sqlBuilder.append(","); + } + } + sqlBuilder.append(")"); + return true; + }else { + return false; + } + } + + /** * Get the list of devices that matches with the given device name and (or) device type. * diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java index 9cd59077a9..25b37095f0 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java @@ -459,7 +459,7 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl { } @Override - public List getDevicesByDuration(PaginationRequest request, int tenantId, + public List getDevicesByDuration(PaginationRequest request, List statusList, int tenantId, String fromDate, String toDate) throws DeviceManagementDAOException { List devices; @@ -522,6 +522,11 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl { return devices; } + @Override + public int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate, int tenantId) throws DeviceManagementDAOException { + return 0; + } + /** * Get the list of devices that matches with the given device name and (or) device type. * diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java index 044bbeb85a..f0c53ea9c0 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java @@ -438,7 +438,7 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl { } @Override - public List getDevicesByDuration(PaginationRequest request, int tenantId, + public List getDevicesByDuration(PaginationRequest request, List statusList, int tenantId, String fromDate, String toDate) throws DeviceManagementDAOException { List devices; @@ -501,6 +501,11 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl { return devices; } + @Override + public int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate, int tenantId) throws DeviceManagementDAOException { + return 0; + } + /** * Get the list of devices that matches with the given device name and (or) device type. * diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java index 706dee6148..b7c1e30a6a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java @@ -601,7 +601,7 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl { } @Override - public List getDevicesByDuration(PaginationRequest request, int tenantId, + public List getDevicesByDuration(PaginationRequest request, List statusList, int tenantId, String fromDate, String toDate) throws DeviceManagementDAOException { List devices; @@ -664,6 +664,11 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl { return devices; } + @Override + public int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate, int tenantId) throws DeviceManagementDAOException { + return 0; + } + @Override public int getSubscribedDeviceCount(List deviceIds, int tenantId, String status) throws DeviceManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/ReportManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/ReportManagementServiceImpl.java index 282e11c5f9..a1d39e29cb 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/ReportManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/ReportManagementServiceImpl.java @@ -49,7 +49,7 @@ public class ReportManagementServiceImpl implements ReportManagementService { } @Override - public PaginationResult getDevicesByDuration(PaginationRequest request, String fromDate, + public PaginationResult getDevicesByDuration(PaginationRequest request, List statusList, String fromDate, String toDate) throws ReportManagementException { PaginationResult paginationResult = new PaginationResult(); @@ -64,6 +64,7 @@ public class ReportManagementServiceImpl implements ReportManagementService { DeviceManagementDAOFactory.openConnection(); List devices = deviceDAO.getDevicesByDuration( request, + statusList, DeviceManagementDAOUtil.getTenantId(), fromDate, toDate @@ -85,4 +86,23 @@ public class ReportManagementServiceImpl implements ReportManagementService { DeviceManagementDAOFactory.closeConnection(); } } + + @Override + public int getDevicesByDurationCount(List statusList, String ownership, String fromDate, String toDate) + throws ReportManagementException { + try { + DeviceManagementDAOFactory.openConnection(); + return deviceDAO.getDevicesByDurationCount(statusList, ownership, fromDate, toDate, DeviceManagementDAOUtil.getTenantId()); + } catch (DeviceManagementDAOException e) { + String msg = "Error occurred in while retrieving device count by status for " + statusList + "devices."; + log.error(msg, e); + throw new ReportManagementException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred while opening a connection to the data source"; + log.error(msg, e); + throw new ReportManagementException(msg, e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + } }