Create a report to get application not installed devices

4.x.x
Shamalka Navod 5 years ago committed by Dharmakeerthi Lasantha
parent c4e4713c7b
commit e4105783a6

@ -0,0 +1,136 @@
/*
* Copyright (c) 2020, 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, Breadcrumb, Icon, Button } from 'antd';
import { Link } from 'react-router-dom';
import { withConfigContext } from '../../../context/ConfigContext';
import AppListDropDown from '../Widgets/AppListDropDown';
import ReportDevicesTable from '../Widgets/ReportDevicesTable';
import AppVersionDropDown from '../Widgets/AppVersionDropDown';
// eslint-disable-next-line no-unused-vars
let config = null;
let url;
let packageName;
let version;
class AppNotInstalledDevicesReport extends React.Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
config = this.props.context;
this.state = {
apiUrl: null,
visible: false,
packageName: null,
version: 'all',
};
}
getAppList = appPackageName => {
packageName = appPackageName;
this.setState({ packageName });
};
getVersion = appVersion => {
version = appVersion;
this.setState({ version });
};
onClickGenerateButton = () => {
const { packageName, version } = this.state;
if (version === 'all') {
url =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/android/' +
packageName +
'/not-installed?';
} else {
url =
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/reports/devices/android/' +
packageName +
'/not-installed?app-version=' +
version +
'&';
}
this.setState({ apiUrl: url });
};
render() {
const { apiUrl, packageName } = this.state;
return (
<div>
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link to="/entgra/reports">Reports</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>App NOT Installed Devices Report</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Policy Report</h3>
<div style={{ display: 'flex', marginBottom: '10px' }}>
<div>
<AppListDropDown getAppList={this.getAppList} />
</div>
<div style={{ marginLeft: '10px' }}>
<AppVersionDropDown
getVersion={this.getVersion}
packageName={packageName}
/>
</div>
<Button
type="primary"
onClick={this.onClickGenerateButton}
style={{ marginLeft: '10px' }}
>
Generate Report
</Button>
</div>
<div style={{ backgroundColor: '#ffffff', borderRadius: 5 }}>
<ReportDevicesTable apiUrl={apiUrl} />
</div>
</div>
</PageHeader>
<div
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
</div>
);
}
}
export default withConfigContext(AppNotInstalledDevicesReport);

@ -188,7 +188,10 @@ class DeviceStatusReport extends React.Component {
<Icon type="home" /> Home <Icon type="home" /> Home
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/reports">Reports</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Device Status Report</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}> <div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Device Status Report</h3> <h3>Device Status Report</h3>

@ -177,7 +177,10 @@ class EnrollmentTypeReport extends React.Component {
<Icon type="home" /> Home <Icon type="home" /> Home
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/reports">Reports</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Device Enrollment Type Report</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}> <div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Device Enrollment Type Report</h3> <h3>Device Enrollment Type Report</h3>

@ -177,7 +177,12 @@ class EnrollmentsVsUnenrollmentsReport extends React.Component {
<Icon type="home" /> Home <Icon type="home" /> Home
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/reports">Reports</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
Enrollments vs Unenrollments Report
</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}> <div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Enrollments vs Unenrollments Report</h3> <h3>Enrollments vs Unenrollments Report</h3>

@ -132,7 +132,10 @@ class PolicyReport extends React.Component {
<Icon type="home" /> Home <Icon type="home" /> Home
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Report</Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra/reports">Reports</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Policy Compliance Report</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap" style={{ marginBottom: '10px' }}> <div className="wrap" style={{ marginBottom: '10px' }}>
<h3>Policy Report</h3> <h3>Policy Report</h3>

@ -0,0 +1,100 @@
/*
* Copyright (c) 2020, 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 { message, notification, Select } from 'antd';
import axios from 'axios';
import { withConfigContext } from '../../../context/ConfigContext';
const { Option } = Select;
class AppListDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
selectItem: [],
};
}
componentDidMount() {
this.fetchFullAppList();
}
fetchFullAppList = () => {
const config = this.props.context;
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices/android/applications?offset=0&limit=-1',
)
.then(res => {
if (res.status === 200) {
let selectItem;
selectItem = res.data.data.applicationList.map(data => (
<Option value={data.id} key={data.applicationIdentifier}>
{data.name.replace('%', ' ')}
</Option>
));
this.setState({ selectItem });
}
})
.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 application list.',
});
}
});
};
onChange = (value, data) => {
this.props.getAppList(data.key);
};
render() {
const { selectItem } = this.state;
return (
<div>
<Select
showSearch
style={{ width: 200 }}
placeholder="Select an app"
optionFilterProp="children"
onChange={this.onChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >=
0
}
>
{selectItem}
</Select>
</div>
);
}
}
export default withConfigContext(AppListDropDown);

@ -0,0 +1,118 @@
/*
* Copyright (c) 2020, 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 { message, notification, Select } from 'antd';
import axios from 'axios';
import { withConfigContext } from '../../../context/ConfigContext';
const { Option } = Select;
class AppVersionDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
selectItem: [],
loading: false,
};
}
componentDidMount() {
if (this.props.packageName) {
this.fetchVersionList();
}
}
// Rerender component when parameters change
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.packageName !== this.props.packageName) {
this.fetchVersionList();
}
}
fetchVersionList = () => {
const config = this.props.context;
this.setState({ loading: true });
axios
.get(
window.location.origin +
config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
'/devices/application/' +
this.props.packageName +
'/versions',
)
.then(res => {
if (res.status === 200) {
let selectItem;
selectItem = JSON.parse(res.data.data).map(data => (
<Option value={data} key={data}>
{data}
</Option>
));
this.setState({ selectItem, loading: false });
}
})
.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 application list.',
});
}
});
};
onChange = value => {
this.props.getVersion(value);
};
render() {
const { selectItem, loading } = this.state;
return (
<div>
<Select
defaultValue={'all'}
showSearch
style={{ width: 200 }}
placeholder="Select app version"
optionFilterProp="children"
onChange={this.onChange}
loading={loading}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >=
0
}
>
<Option value={'all'} key={'all'}>
All
</Option>
{selectItem}
</Select>
</div>
);
}
}
export default withConfigContext(AppVersionDropDown);

@ -0,0 +1,249 @@
/*
* Copyright (c) 2020, 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, notification, Table, Tag, Tooltip } 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';
let config = null;
const columns = [
{
title: 'Device',
dataIndex: 'name',
width: 100,
},
{
title: 'Type',
dataIndex: 'type',
key: 'type',
// eslint-disable-next-line react/display-name
render: type => {
const defaultPlatformIcons = config.defaultPlatformIcons;
let icon = defaultPlatformIcons.default.icon;
let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme;
}
return (
<span style={{ fontSize: 20, color: color, textAlign: 'center' }}>
<Icon type={icon} theme={theme} />
</span>
);
},
// todo add filtering options
},
{
title: 'Owner',
dataIndex: 'enrolmentInfo',
key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner,
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
render: enrolmentInfo => enrolmentInfo.ownership,
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
key: 'status',
// eslint-disable-next-line react/display-name
render: enrolmentInfo => {
const status = enrolmentInfo.status.toLowerCase();
let color = '#f9ca24';
switch (status) {
case 'active':
color = '#badc58';
break;
case 'created':
color = '#6ab04c';
break;
case 'removed':
color = '#ff7979';
break;
case 'inactive':
color = '#f9ca24';
break;
case 'blocked':
color = '#636e72';
break;
}
return <Tag color={color}>{status}</Tag>;
},
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
// eslint-disable-next-line react/display-name
render: data => {
const { dateOfLastUpdate } = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return (
<Tooltip title={new Date(dateOfLastUpdate).toString()}>
{timeAgoString}
</Tooltip>
);
},
// todo add filtering options
},
];
const getTimeAgo = time => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class ReportDeviceTable extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: [],
paramsObj: {},
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows,
});
},
};
componentDidMount() {
if (this.props.apiUrl) {
this.fetch();
}
}
// Rerender component when parameters change
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.apiUrl !== this.props.apiUrl) {
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,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key])
.join('&');
// send request to the invokerss
axios
.get(this.props.apiUrl + encodedExtraParams)
.then(res => {
if (res.status === 200) {
const pagination = { ...this.state.pagination };
this.setState({
loading: false,
data: res.data.data,
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,
});
};
render() {
const { data, pagination, loading } = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record =>
record.deviceIdentifier +
record.enrolmentInfo.owner +
record.enrolmentInfo.ownership
}
dataSource={data.devices}
pagination={{
...pagination,
size: 'small',
total: data.count,
showTotal: (total, range) =>
`showing ${range[0]}-${range[1]} of ${total} devices`,
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
);
}
}
export default withConfigContext(ReportDeviceTable);

@ -31,6 +31,7 @@ import PolicyReport from './components/Reports/Templates/PolicyReport';
import DeviceStatusReport from './components/Reports/Templates/DeviceStatusReport'; import DeviceStatusReport from './components/Reports/Templates/DeviceStatusReport';
import PolicyReportHome from './pages/Dashboard/Reports/PolicyReportHome'; import PolicyReportHome from './pages/Dashboard/Reports/PolicyReportHome';
import ReportDurationItemList from './pages/Dashboard/Reports/ReportDurationItemList'; import ReportDurationItemList from './pages/Dashboard/Reports/ReportDurationItemList';
import AppNotInstalledDevicesReport from './components/Reports/Templates/AppNotInstalledDevicesReport';
const routes = [ const routes = [
{ {
@ -128,6 +129,11 @@ const routes = [
component: DeviceStatusReport, component: DeviceStatusReport,
exact: true, exact: true,
}, },
{
path: '/entgra/reports/app-not-installed',
component: AppNotInstalledDevicesReport,
exact: true,
},
], ],
}, },
]; ];

@ -146,6 +146,35 @@ class PolicyReportHome extends React.Component {
</Card> </Card>
</Link> </Link>
</Col> </Col>
<Col span={8}>
<Link
to={{
// Path to respective report page
pathname: '/entgra/reports/app-not-installed',
data: {
name: 'app_not_installed_devices_report',
},
}}
>
<Card
bordered={true}
hoverable={true}
style={{ borderRadius: 10, marginBottom: 16 }}
>
<div align="center">
<Icon
type="desktop"
style={{ fontSize: '25px', color: '#08c' }}
/>
<h2>
<b>App NOT Installed Devices Report</b>
</h2>
<p>Report for all device types</p>
</div>
</Card>
</Link>
</Col>
</Row> </Row>
</div> </div>
</div> </div>

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, 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.
*/
package org.wso2.carbon.device.mgt.jaxrs.beans;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import java.util.ArrayList;
import java.util.List;
public class ApplicationList extends BasePaginatedResult{
private List<Application> applicationList = new ArrayList<>();
@ApiModelProperty(value = "List of applications returned")
@JsonProperty("applications")
public List<Application> getList() {
return applicationList;
}
public void setList(List<Application> applicationList) {
this.applicationList = applicationList;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{\n");
sb.append(" count: ").append(getCount()).append(",\n");
sb.append(" applications: [").append(applicationList).append("\n");
sb.append("]}\n");
return sb.toString();
}
}

@ -60,6 +60,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.NonComplianceData; import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.NonComplianceData;
import org.wso2.carbon.device.mgt.common.search.PropertyMap; import org.wso2.carbon.device.mgt.common.search.PropertyMap;
import org.wso2.carbon.device.mgt.common.search.SearchContext; import org.wso2.carbon.device.mgt.common.search.SearchContext;
import org.wso2.carbon.device.mgt.jaxrs.beans.ApplicationList;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest;
@ -80,7 +81,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Device related REST-API. This can be used to manipulated device related details. * Device related REST-API. This can be used to manipulated device related details.
@ -2028,4 +2028,111 @@ public interface DeviceManagementService {
required = true) required = true)
@PathParam("id") @PathParam("id")
int id); int id);
@GET
@Path("/{device-type}/applications")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Getting Details of Applications",
notes = "Provides details of installed applications in all the devices enrolled with Entgra IoT Server.",
tags = "Device Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:applications")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully fetched the list of applications.",
response = ApplicationList.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 = 404,
message = "Not Found. \n There are no applications.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Error occurred while getting the application data.",
response = ErrorResponse.class)
})
Response getApplications(
@ApiParam(
name = "device-type",
value = "Device type (platform) of the application",
required = true)
@PathParam("device-type")
String deviceType,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
defaultValue = "0")
@QueryParam("offset")
int offset,
@ApiParam(
name = "limit",
value = "Provide how many device details you require from the starting pagination index/offset.",
defaultValue = "10")
@QueryParam("limit")
int limit);
@GET
@Path("/application/{package-name}/versions")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Getting versions of a given application",
notes = "Provides versions of a given application installed in devices of Entgra IoT Server.",
tags = "Device Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:applications")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully fetched the list of app versions.",
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 = 500,
message = "Error occurred while getting the version data.",
response = ErrorResponse.class)
})
Response getAppVersions(
@ApiParam(
name = "package-name",
value = "The package name of the app.",
required = true)
@PathParam("package-name")
String packageName);
} }

@ -35,6 +35,7 @@ import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import org.wso2.carbon.device.mgt.jaxrs.util.Constants;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@ -369,4 +370,77 @@ public interface ReportManagementService {
defaultValue = "5") defaultValue = "5")
@QueryParam("limit") @QueryParam("limit")
int limit); int limit);
@GET
@Path("/devices/{device-type}/{package-name}/not-installed")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Getting Details of Application Not Installed Devices",
notes = "Provides details of all the devices enrolled with Entgra 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 = 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 getAppNotInstalledDevices(
@ApiParam(
name = "device-type",
value = "The device type name, such as ios, android, windows, or fire-alarm.",
required = true)
@PathParam("device-type")
String deviceType,
@ApiParam(
name = "package-name",
value = "The package name of the app.",
required = true)
@PathParam("package-name")
String packageName,
@ApiParam(
name = "app-version",
value = "Version of the app")
@QueryParam("app-version") String version,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
defaultValue = "0")
@QueryParam("offset")
int offset,
@ApiParam(
name = "limit",
value = "Provide how many device details you require from the starting pagination index/offset.",
defaultValue = "5")
@QueryParam("limit")
int limit);
} }

@ -87,6 +87,7 @@ import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.beans.OperationList; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationList;
import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest;
import org.wso2.carbon.device.mgt.jaxrs.beans.ComplianceDeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.ComplianceDeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ApplicationList;
import org.wso2.carbon.device.mgt.jaxrs.service.api.DeviceManagementService; import org.wso2.carbon.device.mgt.jaxrs.service.api.DeviceManagementService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
@ -108,7 +109,6 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.DefaultValue; import javax.ws.rs.DefaultValue;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -1106,4 +1106,58 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} }
} }
@GET
@Override
@Path("/{device-type}/applications")
public Response getApplications(
@PathParam("device-type") String deviceType,
@DefaultValue("0")
@QueryParam("offset") int offset,
@DefaultValue("10")
@QueryParam("limit") int limit) {
PaginationRequest request = new PaginationRequest(offset, limit);
ApplicationList applicationList = new ApplicationList();
request.setDeviceType(deviceType);
try {
PaginationResult paginationResult = DeviceMgtAPIUtils
.getDeviceManagementService()
.getApplications(request);
if (paginationResult.getData().isEmpty()) {
return Response.status(Response.Status.OK)
.entity("No applications are available under " + deviceType + " platform.").build();
} else {
applicationList.setList((List<Application>) paginationResult.getData());
applicationList.setCount(paginationResult.getRecordsTotal());
return Response.status(Response.Status.OK).entity(applicationList).build();
}
} catch (DeviceTypeNotFoundException e) {
String msg = "Error occurred while retrieving application list." +
" Device type (Application Platform): " + deviceType +
"is not valid";
log.error(msg);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while retrieving application list";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@GET
@Path("/application/{package-name}/versions")
@Override
public Response getAppVersions(
@PathParam("package-name") String packageName) {
try {
List<String> versions = DeviceMgtAPIUtils.getDeviceManagementService()
.getAppVersions(packageName);
return Response.status(Response.Status.OK).entity(versions).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while retrieving version list for app with package name " + packageName;
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
} }

@ -34,6 +34,7 @@ import org.wso2.carbon.device.mgt.jaxrs.service.api.ReportManagementService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue; import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -197,4 +198,44 @@ public class ReportManagementServiceImpl implements ReportManagementService {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} }
} }
@GET
@Path("/devices/{device-type}/{package-name}/not-installed")
@Override
public Response getAppNotInstalledDevices(
@PathParam("device-type") String deviceType,
@PathParam("package-name") String packageName,
@QueryParam("app-version") String version,
@DefaultValue("0")
@QueryParam("offset") int offset,
@DefaultValue("10")
@QueryParam("limit") int limit) {
try {
RequestValidationUtil.validatePaginationParameters(offset, limit);
PaginationRequest request = new PaginationRequest(offset, limit);
DeviceList devices = new DeviceList();
request.setDeviceType(deviceType);
PaginationResult result = DeviceMgtAPIUtils.getReportManagementService()
.getAppNotInstalledDevices(request, packageName, version);
if (result.getData().isEmpty()) {
return Response.status(Response.Status.OK)
.entity("App with package name " + packageName +
" is installed in all enrolled devices").build();
} else {
devices.setList((List<Device>) result.getData());
devices.setCount(result.getRecordsTotal());
return Response.status(Response.Status.OK).entity(devices).build();
}
} catch (DeviceTypeNotFoundException e) {
String msg = "Error occurred while retrieving devices list. Device type: " + deviceType +
"is not valid";
log.error(msg);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
} catch (ReportManagementException e) {
String msg = "Error occurred while retrieving device list";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
} }

@ -20,6 +20,7 @@ package org.wso2.carbon.device.mgt.common.report.mgt;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException;
@ -59,4 +60,17 @@ public interface ReportManagementService {
*/ */
PaginationResult getDevicesExpiredByOSVersion(PaginationRequest request) PaginationResult getDevicesExpiredByOSVersion(PaginationRequest request)
throws ReportManagementException, DeviceTypeNotFoundException; throws ReportManagementException, DeviceTypeNotFoundException;
/**
* This method is used to get devices which have not installed the app with the given package name
*
* @param request Request object with device type
* @param packageName Package name of the application
* @param version Version of the application
* @return {@link PaginationResult}
* @throws ReportManagementException
* @throws DeviceTypeNotFoundException
*/
PaginationResult getAppNotInstalledDevices(PaginationRequest request, String packageName, String version)
throws ReportManagementException, DeviceTypeNotFoundException;
} }

@ -18,6 +18,8 @@
*/ */
package org.wso2.carbon.device.mgt.core.dao; package org.wso2.carbon.device.mgt.core.dao;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import java.util.List; import java.util.List;
@ -38,4 +40,25 @@ public interface ApplicationDAO {
throws DeviceManagementDAOException; throws DeviceManagementDAOException;
List<Application> getInstalledApplications(int deviceId, int enrolmentId) throws DeviceManagementDAOException; List<Application> getInstalledApplications(int deviceId, int enrolmentId) throws DeviceManagementDAOException;
/**
* This method is used to get a list of applications installed in all enrolled devices
*
* @param request Request object with limit and offset
* @param tenantId ID of the current tenant
* @return List of {@link Application} objects
* @throws DeviceManagementDAOException If any database error occured
*/
List<Application> getApplications(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException;
/**
* This method is used to get a list of app versions when app package name is given.
*
* @param tenantId ID of the current tenant
* @param packageName Package name of the application
* @return String list of app versions
* @throws DeviceManagementDAOException If any database error occured
*/
List<String> getAppVersions(int tenantId, String packageName) throws DeviceManagementDAOException;
} }

@ -634,4 +634,34 @@ public interface DeviceDAO {
*/ */
int getCountOfDeviceExpiredByOSVersion(String deviceType, long osBuildDate, int tenantId) int getCountOfDeviceExpiredByOSVersion(String deviceType, long osBuildDate, int tenantId)
throws DeviceManagementDAOException; throws DeviceManagementDAOException;
/**
* This method is used to get devices which have not installed the app with the given package name
*
* @param request Request object with device type
* @param tenantId ID of the current tenant
* @param packageName Package name of the application
* @param version Version of the application
* @return A list of device objects
* @throws DeviceManagementDAOException Thrown if error occurs while database transactions
*/
List<Device> getAppNotInstalledDevices(PaginationRequest request,
int tenantId,
String packageName,
String version) throws DeviceManagementDAOException;
/**
* This method is used to get count if devices which have not installed the app with the given package name
*
* @param request Request object with device type
* @param tenantId ID of the current tenant
* @param packageName Package name of the application
* @param version Version of the application
* @return Device count
* @throws DeviceManagementDAOException Thrown if error occurs while database transactions
*/
int getCountOfAppNotInstalledDevices(PaginationRequest request,
int tenantId,
String packageName,
String version) throws DeviceManagementDAOException;
} }

@ -20,6 +20,7 @@ package org.wso2.carbon.device.mgt.core.dao.impl;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO; import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
@ -271,6 +272,81 @@ public abstract class AbstractApplicationDAOImpl implements ApplicationDAO {
return applications; return applications;
} }
@Override
public List<Application> getApplications(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {
List<Application> applications = new ArrayList<>();
Application application;
String sql = "Select " +
"ID," +
" NAME, " +
"APP_IDENTIFIER, " +
"PLATFORM, " +
"CATEGORY, " +
"VERSION, " +
"TYPE, " +
"LOCATION_URL, " +
"IMAGE_URL, " +
"APP_PROPERTIES, " +
"MEMORY_USAGE, " +
"IS_ACTIVE, " +
"TENANT_ID " +
"From DM_APPLICATION " +
"WHERE PLATFORM = ? " +
"AND TENANT_ID = ? LIMIT ? OFFSET ?";
try {
Connection conn = this.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, request.getDeviceType());
stmt.setInt(2, tenantId);
stmt.setInt(3, request.getRowCount());
stmt.setInt(4, request.getStartIndex());
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
application = loadApplication(rs);
applications.add(application);
}
}
}
} catch (SQLException e) {
String msg = "SQL Error occurred while retrieving the list of Applications " +
"installed in all enrolled devices for device type " + request.getDeviceType() +
" under tenant id " + tenantId;
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
return applications;
}
@Override
public List<String> getAppVersions(int tenantId, String packageName) throws DeviceManagementDAOException {
String sql = "SELECT " +
"VERSION " +
"FROM DM_APPLICATION " +
"WHERE TENANT_ID=? " +
"AND APP_IDENTIFIER=?";
try {
Connection conn = this.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, tenantId);
stmt.setString(2, packageName);
try (ResultSet rs = stmt.executeQuery()) {
List<String> versions = new ArrayList<>();
while (rs.next()) {
versions.add(rs.getString("VERSION"));
}
return versions;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all " +
"registered apps under tenant id " + tenantId;
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
private Application loadApplication(ResultSet rs) throws DeviceManagementDAOException { private Application loadApplication(ResultSet rs) throws DeviceManagementDAOException {
ByteArrayInputStream bais; ByteArrayInputStream bais;
ObjectInputStream ois; ObjectInputStream ois;

@ -1894,6 +1894,123 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
} }
} }
@Override
public List<Device> getAppNotInstalledDevices(
PaginationRequest request, int tenantId, String packageName, String version)
throws DeviceManagementDAOException {
List<Device> devices;
String deviceType = request.getDeviceType();
boolean isVersionProvided = false;
String sql = "SELECT " +
"d.ID AS DEVICE_ID, " +
"d.DESCRIPTION,d.NAME AS DEVICE_NAME, " +
"t.NAME AS DEVICE_TYPE, " +
"d.DEVICE_IDENTIFICATION, " +
"e.OWNER, " +
"e.OWNERSHIP, " +
"e.STATUS, " +
"e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, " +
"e.ID AS ENROLMENT_ID " +
"FROM DM_DEVICE AS d " +
"INNER JOIN DM_ENROLMENT AS e ON d.ID = e.DEVICE_ID " +
"INNER JOIN DM_DEVICE_TYPE AS t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE t.NAME = ? AND e.TENANT_ID = ? AND d.ID " +
"NOT IN (SELECT m.DEVICE_ID " +
"FROM DM_DEVICE_APPLICATION_MAPPING AS m " +
"INNER JOIN DM_APPLICATION AS a ON m.APPLICATION_ID=a.ID " +
"WHERE a.APP_IDENTIFIER = ?";
if (!StringUtils.isBlank(version)) {
sql = sql + " AND a.VERSION = ? ";
isVersionProvided = true;
}
sql = sql + ") LIMIT ? OFFSET ?";
try {
Connection conn = this.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setString(paramIdx++, deviceType);
stmt.setInt(paramIdx++, tenantId);
stmt.setString(paramIdx++, packageName);
if (isVersionProvided) {
stmt.setString(paramIdx++, version);
}
stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex());
try (ResultSet rs = stmt.executeQuery()) {
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
return devices;
}
}
} 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);
}
}
@Override
public int getCountOfAppNotInstalledDevices(
PaginationRequest request, int tenantId, String packageName, String version)
throws DeviceManagementDAOException {
String deviceType = request.getDeviceType();
boolean isVersionProvided = false;
String sql = "SELECT " +
"COUNT(d.ID) AS DEVICE_COUNT " +
"FROM DM_DEVICE AS d " +
"INNER JOIN DM_ENROLMENT AS e ON d.ID = e.DEVICE_ID " +
"INNER JOIN DM_DEVICE_TYPE AS t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE t.NAME = ? AND e.TENANT_ID = ? AND d.ID " +
"NOT IN " +
"(SELECT m.DEVICE_ID " +
"FROM DM_DEVICE_APPLICATION_MAPPING AS m " +
"INNER JOIN DM_APPLICATION AS a ON m.APPLICATION_ID=a.ID " +
"WHERE a.APP_IDENTIFIER = ?";
if (!StringUtils.isBlank(version)) {
sql = sql + " AND a.VERSION = ? ";
isVersionProvided = true;
}
sql = sql + ")";
try {
Connection conn = this.getConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setString(paramIdx++, deviceType);
stmt.setInt(paramIdx++, tenantId);
stmt.setString(paramIdx++, packageName);
if (isVersionProvided) {
stmt.setString(paramIdx, version);
}
try (ResultSet rs = stmt.executeQuery()) {
int deviceCount = 0;
if (rs.next()) {
deviceCount = rs.getInt("DEVICE_COUNT");
}
return deviceCount;
}
}
} 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);
}
}
/*** /***
* This method removes records of a given list of devices from the DM_DEVICE_DETAIL table * This method removes records of a given list of devices from the DM_DEVICE_DETAIL table
* @param conn Connection object * @param conn Connection object

@ -23,12 +23,15 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.Count; import org.wso2.carbon.device.mgt.common.Count;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException;
import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService; import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService;
import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
@ -39,7 +42,6 @@ import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Calendar; import java.util.Calendar;
@ -54,9 +56,11 @@ public class ReportManagementServiceImpl implements ReportManagementService {
private static final Log log = LogFactory.getLog(ReportManagementServiceImpl.class); private static final Log log = LogFactory.getLog(ReportManagementServiceImpl.class);
private DeviceDAO deviceDAO; private DeviceDAO deviceDAO;
private ApplicationDAO applicationDAO;
public ReportManagementServiceImpl() { public ReportManagementServiceImpl() {
this.deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); this.deviceDAO = DeviceManagementDAOFactory.getDeviceDAO();
this.applicationDAO = DeviceManagementDAOFactory.getApplicationDAO();
} }
@Override @Override
@ -270,4 +274,62 @@ public class ReportManagementServiceImpl implements ReportManagementService {
} }
return resultObject; return resultObject;
} }
@Override
public PaginationResult getAppNotInstalledDevices(PaginationRequest request, String packageName, String version)
throws ReportManagementException, DeviceTypeNotFoundException {
PaginationResult paginationResult = new PaginationResult();
if(StringUtils.isBlank(packageName)){
String msg = "Error, application package name is not given";
log.error(msg);
throw new ReportManagementException(msg);
}
try {
int tenantId = DeviceManagementDAOUtil.getTenantId();
request = DeviceManagerUtil.validateDeviceListPageSize(request);
String deviceType = request.getDeviceType();
DeviceType deviceTypeObj = DeviceManagerUtil.getDeviceType(
deviceType, tenantId);
if (deviceTypeObj == null) {
String msg = "Error, device of type: " + deviceType + " does not exist";
log.error(msg);
throw new DeviceTypeNotFoundException(msg);
}
try {
DeviceManagementDAOFactory.openConnection();
List<Device> devices = deviceDAO.getAppNotInstalledDevices(
request,
tenantId,
packageName,
version
);
paginationResult.setData(devices);
int deviceCount = deviceDAO.getCountOfAppNotInstalledDevices(
request,
tenantId,
packageName,
version);
paginationResult.setRecordsTotal(deviceCount);
return paginationResult;
} 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();
}
} catch (DeviceManagementException e) {
String msg = "Error occurred while validating device list page size";
log.error(msg, e);
throw new ReportManagementException(msg, e);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving Tenant ID";
log.error(msg, e);
throw new ReportManagementException(msg, e);
}
}
} }

@ -45,6 +45,7 @@ import org.wso2.carbon.device.mgt.common.DeviceTransferRequest;
import org.wso2.carbon.device.mgt.common.MonitoringOperation; import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.StartupOperationConfig; import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
@ -855,4 +856,23 @@ public interface DeviceManagementProviderService {
*/ */
PaginationResult getAppSubscribedDevices(int offsetValue, int limitValue, PaginationResult getAppSubscribedDevices(int offsetValue, int limitValue,
List<Integer> devicesIds, String status) throws DeviceManagementException; List<Integer> devicesIds, String status) throws DeviceManagementException;
/**
* This method is used to get a list of applications installed in all enrolled devices
*
* @param request Request object with limit and offset
* @return {@link PaginationResult}
* @throws ApplicationManagementException if any service level or DAO level error occurs.
*/
PaginationResult getApplications(PaginationRequest request)
throws ApplicationManagementException, DeviceTypeNotFoundException;
/**
* This method is used to get a list of app versions when app package name is given.
*
* @param packageName Package name of the application
* @return String list of app versions
* @throws ApplicationManagementException if any service level or DAO level error occurs.
*/
List<String> getAppVersions(String packageName) throws ApplicationManagementException;
} }

@ -63,6 +63,7 @@ import org.wso2.carbon.device.mgt.common.DeviceTransferRequest;
import org.wso2.carbon.device.mgt.common.DevicePropertyNotification; import org.wso2.carbon.device.mgt.common.DevicePropertyNotification;
import org.wso2.carbon.device.mgt.common.DeviceEnrollmentInfoNotification; import org.wso2.carbon.device.mgt.common.DeviceEnrollmentInfoNotification;
import org.wso2.carbon.device.mgt.common.DeviceNotification; import org.wso2.carbon.device.mgt.common.DeviceNotification;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
@ -114,6 +115,7 @@ import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO;
import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO;
import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager;
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO;
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException;
@ -3967,6 +3969,76 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
return paginationResult; return paginationResult;
} }
@Override
public PaginationResult getApplications(PaginationRequest request)
throws ApplicationManagementException, DeviceTypeNotFoundException {
PaginationResult paginationResult = new PaginationResult();
try {
int tenantId = DeviceManagementDAOUtil.getTenantId();
request = DeviceManagerUtil.validateDeviceListPageSize(request);
String deviceType = request.getDeviceType();
DeviceType deviceTypeObj = DeviceManagerUtil.getDeviceType(
deviceType, tenantId);
if (deviceTypeObj == null) {
String msg = "Error, device of type (application platform): " + deviceType + " does not exist";
log.error(msg);
throw new DeviceTypeNotFoundException(msg);
}
try {
DeviceManagementDAOFactory.openConnection();
List<Application> applicationList = applicationDAO.getApplications(
request,
tenantId
);
paginationResult.setData(applicationList);
paginationResult.setRecordsTotal(applicationList.size());
return paginationResult;
} catch (SQLException e) {
String msg = "Error occurred while opening a connection " +
"to the data source";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
} catch (DeviceManagementException e) {
String msg = "Error occurred while validating device list page size";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving Tenant ID";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
}
}
@Override
public List<String> getAppVersions(String packageName)
throws ApplicationManagementException {
try {
DeviceManagementDAOFactory.openConnection();
List<String> versions = applicationDAO.getAppVersions(
DeviceManagementDAOUtil.getTenantId(),
packageName
);
return versions;
} catch (SQLException e) {
String msg = "Error occurred while opening a connection " +
"to the data source";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving Tenant ID";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
}
/** /**
* Wrap the device configuration data into DeviceConfiguration bean * Wrap the device configuration data into DeviceConfiguration bean
* @param device Device queried using the properties * @param device Device queried using the properties

Loading…
Cancel
Save