Merge branch 'application-mgt-new' of gitlab.com:entgra/carbon-device-mgt into application-mgt-new

feature/appm-store/pbac
lasanthaDLPDS 5 years ago
commit 8feb157bb9

@ -30,7 +30,7 @@ import {
Spin, Spin,
message, message,
Icon, Icon,
Card Card, Badge
} from 'antd'; } from 'antd';
import DetailedRating from "../../detailed-rating/DetailedRating"; import DetailedRating from "../../detailed-rating/DetailedRating";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
@ -487,18 +487,14 @@ class AppDetailsDrawer extends React.Component {
)} )}
<Text strong={true}>Releases </Text> <Text strong={true}>Releases </Text>
{/*display add new release only if app type is enterprise*/}
<div className="releases-details"> <div className="releases-details">
{(app.type === "ENTERPRISE") && (
<Link to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}><Button
htmlType="button"
size="small">Add
new release</Button></Link>)}
<List <List
style={{paddingTop: 16}} style={{paddingTop: 16}}
grid={{gutter: 16, column: 2}} grid={{gutter: 16, column: 2}}
pagination={{
pageSize: 4, // number of releases per page
size: "small",
}}
dataSource={app.applicationReleases} dataSource={app.applicationReleases}
renderItem={release => ( renderItem={release => (
<div className="app-release-cards"> <div className="app-release-cards">
@ -507,7 +503,27 @@ class AppDetailsDrawer extends React.Component {
<Card className="release-card"> <Card className="release-card">
<Meta <Meta
avatar={ avatar={
<Avatar size="large" shape="square" src={release.iconPath}/> <div>
{(release.currentStatus === "PUBLISHED") ? (
<Badge
title="Published"
style={{
backgroundColor: '#52c41a',
borderRadius:"50%",
color:"white"
}}
count={
<Icon
type="check-circle"/>
}>
<Avatar size="large" shape="square"
src={release.iconPath}/>
</Badge>
) : (
<Avatar size="large" shape="square"
src={release.iconPath}/>
)}
</div>
} }
title={release.version} title={release.version}
description={ description={
@ -529,10 +545,27 @@ class AppDetailsDrawer extends React.Component {
</div> </div>
)} )}
/> />
</div> </div>
<Divider dashed={true}/> <Divider dashed={true}/>
{/*display add new release only if app type is enterprise*/}
{(app.type === "ENTERPRISE") && (
<div>
<div style={{paddingBottom: 16}}>
<Text>
Add new release for the application
</Text>
</div>
<Link to={`/publisher/apps/${app.deviceType}/${app.id}/add-release`}>
<Button
htmlType="button"
type="primary"
size="small">
Add
</Button>
</Link>
</div>)}
<Divider dashed={true}/>
<Text strong={true}>Description </Text> <Text strong={true}>Description </Text>
{!isDescriptionEditEnabled && ( {!isDescriptionEditEnabled && (

@ -17,7 +17,7 @@
*/ */
import React from "react"; import React from "react";
import {Avatar, Table, Tag, Icon, message, notification, Col} from "antd"; import {Avatar, Table, Tag, Icon, message, notification, Col, Badge} from "antd";
import axios from "axios"; import axios from "axios";
import pSBC from 'shade-blend-color'; import pSBC from 'shade-blend-color';
import "./AppsTable.css"; import "./AppsTable.css";
@ -47,7 +47,31 @@ const columns = [
</Avatar> </Avatar>
); );
} else { } else {
avatar = ( const {applicationReleases} = row;
let hasPublishedRelease = false;
for (let i = 0; i < applicationReleases.length; i++) {
if (applicationReleases[i].currentStatus === "PUBLISHED") {
hasPublishedRelease = true;
break;
}
}
avatar = (hasPublishedRelease) ? (
<Badge
title="Published"
style={{ backgroundColor: '#52c41a', borderRadius:"50%", color:"white"}}
count={
<Icon
type="check-circle"/>
}>
<Avatar shape="square" size="large"
style={{
borderRadius: "28%",
border: "1px solid #ddd"
}}
src={row.applicationReleases[0].iconPath}
/>
</Badge>
) : (
<Avatar shape="square" size="large" <Avatar shape="square" size="large"
style={{ style={{
marginRight: 20, marginRight: 20,
@ -56,13 +80,13 @@ const columns = [
}} }}
src={row.applicationReleases[0].iconPath} src={row.applicationReleases[0].iconPath}
/> />
) );
} }
return ( return (
<div> <div>
{avatar} {avatar}
{name} <span style={{marginLeft: 20}}>{name}</span>
</div>); </div>);
} }
}, },

@ -24,6 +24,7 @@ import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils";
const {Text} = Typography; const {Text} = Typography;
@ -180,19 +181,7 @@ class InstalledDevicesTable extends React.Component {
} }
}).catch((error) => { }).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) { handleApiError(error, "Something went wrong when trying to load subscription data.");
//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}); this.setState({loading: false});
}); });
}; };
@ -203,10 +192,7 @@ class InstalledDevicesTable extends React.Component {
<div> <div>
<div style={{paddingBottom: 24}}> <div style={{paddingBottom: 24}}>
<Text> <Text>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque The following are the subscription details of the application in each respective device.
laudantium,
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo.
</Text> </Text>
</div> </div>
<Table <Table

@ -21,12 +21,9 @@ import {Divider, Row, Col, Typography, Button, Dropdown, notification, Menu, Ico
import "../../../App.css"; import "../../../App.css";
import ImgViewer from "../../apps/release/images/ImgViewer"; import ImgViewer from "../../apps/release/images/ImgViewer";
import StarRatings from "react-star-ratings"; import StarRatings from "react-star-ratings";
import DetailedRating from "./DetailedRating";
import Reviews from "./review/Reviews";
import axios from "axios"; import axios from "axios";
import AppInstallModal from "./install/AppInstallModal"; import AppInstallModal from "./install/AppInstallModal";
import AppUninstallModal from "./install/AppUninstallModal"; import AppUninstallModal from "./install/AppUninstallModal";
import CurrentUsersReview from "./review/CurrentUsersReview";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import {handleApiError} from "../../../js/Utils"; import {handleApiError} from "../../../js/Utils";
import ReviewContainer from "./review/ReviewContainer"; import ReviewContainer from "./review/ReviewContainer";
@ -207,7 +204,7 @@ class ReleaseView extends React.Component {
<Divider/> <Divider/>
<ReviewContainer uuid={release.uuid}/> <ReviewContainer uuid={release.uuid}/>
</TabPane> </TabPane>
<TabPane tab="Installed devices" key="2"> <TabPane tab="Subscription Details" key="2">
<InstalledDevicesTable uuid={release.uuid}/> <InstalledDevicesTable uuid={release.uuid}/>
</TabPane> </TabPane>
</Tabs> </Tabs>

@ -17,12 +17,11 @@
"moment": "latest", "moment": "latest",
"prop-types": "latest", "prop-types": "latest",
"rc-viewer": "0.0.9", "rc-viewer": "0.0.9",
"react-advanced-datetimerange-picker": "^1.0.8", "react-bootstrap": "^1.0.0-beta.12",
"react-highlight-words": "^0.16.0", "react-highlight-words": "^0.16.0",
"react-image-viewer-zoom": "^1.0.36", "react-image-viewer-zoom": "^1.0.36",
"react-infinite-scroller": "^1.2.4", "react-infinite-scroller": "^1.2.4",
"react-leaflet": "^2.4.0", "react-leaflet": "^2.4.0",
"react-bootstrap": "^1.0.0-beta.12",
"react-moment": "^0.9.2", "react-moment": "^0.9.2",
"react-router": "^5.0.1", "react-router": "^5.0.1",
"react-router-config": "^5.0.1", "react-router-config": "^5.0.1",
@ -31,6 +30,7 @@
"react-star-ratings": "^2.3.0", "react-star-ratings": "^2.3.0",
"react-twemoji": "^0.2.3", "react-twemoji": "^0.2.3",
"react-virtualized": "^9.21.1", "react-virtualized": "^9.21.1",
"react-websocket": "^2.1.0",
"reqwest": "^2.0.5", "reqwest": "^2.0.5",
"storm-react-diagrams": "^5.2.1" "storm-react-diagrams": "^5.2.1"
}, },

@ -18,9 +18,8 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Card, Col, Row, Select} from "antd"; import {Card, Col, Icon, message, notification, Row, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
@ -113,16 +112,27 @@ class DeviceTypesTable extends React.Component {
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows} = this.state;
const { Meta } = Card;
const itemCard = data.map((data) => const itemCard = data.map((data) =>
<Col span={8} key={data.id}> <Col span={5} key={data.id}>
<Card hoverable title="Device Type" bordered={true}> <Card
{data.name} size="default"
style={{ width: 200 }}
bordered={true}
actions={[
<Icon type="setting" key="setting" />,
<Icon type="edit" key="edit" />,]}
>
<Meta
avatar={<Icon type="desktop" key="device-types"/>}
title={data.name}
/>
</Card> </Card>
</Col> </Col>
); );
return ( return (
<div style={{ background: '#ECECEC', padding: '30px' }}> <div style={{ background: '#ECECEC', padding: '20px' }}>
<Row gutter={16}> <Row gutter={16}>
{itemCard} {itemCard}
</Row> </Row>

@ -18,9 +18,8 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Button, Modal, Select} from "antd"; import {Icon, message, Modal, notification, Select, Table, Tag, Tooltip, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
@ -133,18 +132,11 @@ class DeviceTable extends React.Component {
selectedRows: [], selectedRows: [],
deviceGroups: [], deviceGroups: [],
groupModalVisible: false, groupModalVisible: false,
selectedGroupId: [] selectedGroupId: [],
selectedRowKeys:[]
}; };
} }
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
});
}
};
componentDidMount() { componentDidMount() {
this.fetch(); this.fetch();
} }
@ -249,6 +241,9 @@ class DeviceTable extends React.Component {
).then(res => { ).then(res => {
if (res.status === 200) { if (res.status === 200) {
this.fetch(); this.fetch();
this.setState({
selectedRowKeys:[]
})
notification["success"]({ notification["success"]({
message: "Done", message: "Done",
duration: 4, duration: 4,
@ -409,11 +404,30 @@ class DeviceTable extends React.Component {
}); });
}; };
render() { onSelectChange = (selectedRowKeys, selectedRows) => {
const {data, pagination, loading, selectedRows} = this.state; this.setState({
selectedRowKeys,
selectedRows: selectedRows
});
};
render() {
const {data, pagination, loading, selectedRows, selectedRowKeys} = this.state;
const isSelectedSingle = this.state.selectedRows.length == 1; const isSelectedSingle = this.state.selectedRows.length == 1;
let selectedText;
if(isSelectedSingle){
selectedText = "You have selected 1 device"
}else{
selectedText = "You have selected " + this.state.selectedRows.length + " devices"
}
const rowSelection = {
selectedRowKeys,
selectedRows,
onChange: this.onSelectChange,
};
let item = this.state.deviceGroups.map((data) => let item = this.state.deviceGroups.map((data) =>
<Select.Option <Select.Option
value={data.id} value={data.id}
@ -441,8 +455,7 @@ class DeviceTable extends React.Component {
}} }}
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={rowSelection}
scroll={{x: 1000}}
/> />
</div> </div>
@ -453,10 +466,11 @@ class DeviceTable extends React.Component {
onOk={this.handleOk} onOk={this.handleOk}
onCancel={this.handleCancel} onCancel={this.handleCancel}
> >
<p>{selectedText}</p>
<Select <Select
mode={isSelectedSingle ? "multiple" : "default"} mode={isSelectedSingle ? "multiple" : "default"}
showSearch showSearch
style={{width: 200}} style={{display:"block"}}
placeholder="Select Group" placeholder="Select Group"
optionFilterProp="children" optionFilterProp="children"
onChange={this.onGroupSelectChange} onChange={this.onGroupSelectChange}

@ -18,9 +18,8 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd"; import {Icon, message, notification, Table, Tag, Tooltip, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
@ -239,7 +238,6 @@ class ReportDeviceTable extends React.Component {
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}}
/> />
</div> </div>
); );

@ -18,8 +18,7 @@
import React from "react"; import React from "react";
import moment from "moment"; import moment from "moment";
import DateTimeRangeContainer from "react-advanced-datetimerange-picker"; import {Button, Select, message, notification, Tag, Tooltip, Empty, DatePicker} from "antd";
import {Button, Select, message, notification, Tag, Tooltip, Empty} from "antd";
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext"; import {withConfigContext} from "../../../context/ConfigContext";
import GeoCustomMap from "../geo-custom-map/GeoCustomMap"; import GeoCustomMap from "../geo-custom-map/GeoCustomMap";
@ -29,15 +28,16 @@ class GeoDashboard extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
let start = moment(new Date()); let start = moment(
new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate(), 0, 0, 0, 0)
);
let end = moment(start) let end = moment(start)
.add(5, "days") .add(1, "days")
.subtract(1, "minute"); .subtract(1, "seconds");
this.state = { this.state = {
deviceData: [], deviceData: [],
selectedDevice: '', selectedDevice: '',
locationData: [], locationData: [],
// currentLocation: [],
loading: false, loading: false,
start: start, start: start,
end: end, end: end,
@ -55,11 +55,11 @@ class GeoDashboard extends React.Component {
* @param startDate - start date * @param startDate - start date
* @param endDate - end date * @param endDate - end date
*/ */
applyCallback = (startDate, endDate) => { applyCallback = (dates, dateStrings) => {
console.log("Apply Callback"); console.log("Apply Callback");
this.setState({ this.setState({
start: startDate, start: dateStrings[0],
end: endDate end: dateStrings[1]
}); });
}; };
@ -180,6 +180,7 @@ class GeoDashboard extends React.Component {
*/ */
controllerBar = () => { controllerBar = () => {
const {RangePicker} = DatePicker;
let now = new Date(); let now = new Date();
let start = moment( let start = moment(
new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0) new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0)
@ -199,32 +200,20 @@ class GeoDashboard extends React.Component {
"2 Weeks": [moment(start).subtract(14, "days"), moment(end)], "2 Weeks": [moment(start).subtract(14, "days"), moment(end)],
"1 Month": [moment(start).subtract(1, "months"), moment(end)], "1 Month": [moment(start).subtract(1, "months"), moment(end)],
}; };
let local = {
format: "DD-MM-YYYY HH:mm",
sundayFirst: false
};
let maxDate = moment(start).add(24, "hour");
let value =
`
${this.state.start.format("DD-MM-YYYY HH:mm")} - ${this.state.end.format("DD-MM-YYYY HH:mm")}
`;
let {deviceData} = this.state; let {deviceData} = this.state;
return ( return (
<div className="controllerDiv"> <div className="controllerDiv">
<RangePicker
ranges={ranges}
style={{marginRight: 20}}
showTime
format="YYYY-MM-DD HH:mm:ss"
defaultValue={[this.state.start, this.state.end]}
onChange={this.applyCallback}
<Button style={{marginRight: 20}}> />
<DateTimeRangeContainer
ranges={ranges}
start={this.state.start}
end={this.state.end}
local={local}
maxDate={maxDate}
applyCallback={this.applyCallback}
>
{value}
</DateTimeRangeContainer>
</Button>
<Select <Select
showSearch showSearch

@ -0,0 +1,175 @@
/*
* 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 {Button, Form, Input, message, Modal, notification, Typography} from "antd";
import axios from "axios";
import {withConfigContext} from "../../context/ConfigContext";
const {Text} = Typography;
let config = null;
class AddGroup extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
this.state = {
addModalVisible: false,
name:'',
description:'',
}
}
onConfirmAdGroup = () => {
const config = this.props.context;
const groupData = {
name: this.state.name,
description: this.state.description
}
//send request to the invoker
axios.post(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups",
groupData,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 201) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully added the group.",
});
}
}).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 add group.",
});
}
});
};
openAddModal = () => {
this.setState({
addModalVisible:true
})
};
handleAddOk = e => {
this.props.form.validateFields(err => {
if (!err) {
this.onConfirmAdGroup();
this.setState({
addModalVisible: false,
});
}
});
};
handleAddCancel = e => {
this.setState({
addModalVisible: false,
});
};
onChangeName = (e) => {
this.setState({
name:e.currentTarget.value
})
};
onChangeDescription = (e) => {
this.setState({
description:e.currentTarget.value
})
};
render() {
const { getFieldDecorator } = this.props.form;
return(
<div>
<div>
<Button type="primary" icon="plus" size={"default"} onClick={this.openAddModal}>
Add Group
</Button>
</div>
<div>
<Modal
title="ADD NEW GROUP"
width="40%"
visible={this.state.addModalVisible}
onOk={this.handleAddOk}
onCancel={this.handleAddCancel}
footer={[
<Button key="cancel" onClick={this.handleAddCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleAddOk}>
Submit
</Button>,
]}
>
<div style={{alignItems:"center"}}>
<p>Create new device group on IoT Server.</p>
<Form
labelCol={{ span: 5 }}
wrapperCol={{ span: 18 }}
>
<Form.Item label="Name" style={{display:"block"}}>
{getFieldDecorator('name', {
rules: [
{
required: true,
message: 'Please input group name',
},
],
})(<Input onChange={this.onChangeName}/>)}
</Form.Item>
<Form.Item label="Description" style={{display:"block"}}>
{getFieldDecorator('description', {
rules: [
{
required: true,
message: 'Please input group description',
},
],
})(<Input onChange={this.onChangeDescription}/>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
</div>
)
}
}
export default withConfigContext(Form.create({name: 'add-group'})(AddGroup));

@ -0,0 +1,379 @@
/*
* 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 {
Button,
Divider,
Form,
Icon,
Input,
message,
Modal,
notification,
Popconfirm,
Select,
Tooltip,
Typography
} from "antd";
import axios from "axios";
import {withConfigContext} from "../../context/ConfigContext";
const {Text} = Typography;
let config = null;
class GroupActions extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
this.state = {
editModalVisible: false,
shareModalVisible: false,
name:this.props.data.name,
description:this.props.data.description,
groupDataObject:{},
rolesData:[],
shareRolesData:[]
}
}
onConfirmDeleteGroup = () => {
const config = this.props.context;
//send request to the invoker
axios.delete(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + this.props.data.id,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully deleted the group.",
});
}
}).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 group.",
});
}
});
};
onConfirmUpdateGroup = (data) => {
const config = this.props.context;
//send request to the invoker
axios.put(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + this.props.data.id,
data
).then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully updated the group.",
});
}
}).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 update group.",
});
}
});
};
fetchUserRoles = (params = {}) => {
const config = this.props.context;
const apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/roles";
//send request to the invokerss
axios.get(apiUrl).then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
}).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 roles.",
});
}
});
};
onConfirmShareGroup = (data) => {
const config = this.props.context;
//send request to the invoker
axios.post(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/groups/id/" + this.props.data.id + "/share",
data
).then(res => {
if (res.status === 200) {
this.props.fetchGroups();
notification["success"]({
message: "Done",
duration: 4,
description:
"Successfully shared the group.",
});
}
}).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 share group.",
});
}
});
}
openEditModal = () => {
this.setState({
editModalVisible:true
})
};
openShareModal = () => {
this.fetchUserRoles();
this.setState({
shareModalVisible:true
})
}
handleEditOk = e => {
this.state.groupDataObject = {
name:this.state.name,
description:this.state.description,
id:this.props.data.id,
owner:this.props.data.owner,
groupProperties:this.props.data.groupProperties
};
this.props.form.validateFields(err => {
if (!err) {
this.onConfirmUpdateGroup(this.state.groupDataObject);
this.setState({
editModalVisible: false,
});
}
});
};
handleEditCancel = e => {
this.setState({
editModalVisible: false,
});
};
handleShareOk = e => {
this.setState({
shareModalVisible: false,
});
this.onConfirmShareGroup(this.state.shareRolesData);
};
handleShareCancel = e => {
this.setState({
shareModalVisible: false,
});
};
onChangeName = (e) => {
this.setState({
name:e.currentTarget.value
})
};
onChangeDescription = (e) => {
this.setState({
description:e.currentTarget.value
})
};
handleRolesDropdownChange = (value) => {
this.setState({
shareRolesData:value
})
};
render() {
const isAdminGroups = this.props.data.id==1 || this.props.data.id==2;
const { Option } = Select;
const { getFieldDecorator } = this.props.form;
let item = this.state.rolesData.map((data) =>
<Select.Option
value={data}
key={data}>
{data}
</Select.Option>);
return(
<div>
<div style={{display:isAdminGroups ? "none" : "inline"}}>
<Tooltip placement="top" title={"Share Group"}>
<a><Icon type="share-alt" onClick={this.openShareModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="top" title={"Edit Group"}>
<a><Icon type="edit" onClick={this.openEditModal}/></a>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={"Delete Group"}>
<Popconfirm
placement="top"
title={"Are you sure?"}
onConfirm={this.onConfirmDeleteGroup}
okText="Ok"
cancelText="Cancel">
<a><Text type="danger"><Icon type="delete"/></Text></a>
</Popconfirm>
</Tooltip>
</div>
<div>
<Modal
title="Update Group"
width="40%"
visible={this.state.editModalVisible}
onOk={this.handleEditOk}
onCancel={this.handleEditCancel}
footer={[
<Button key="cancel" onClick={this.handleEditCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleEditOk}>
Submit
</Button>,
]}
>
<div style={{alignItems:"center"}}>
<p>Enter new name and description for the group</p>
<Form
labelCol={{ span: 5 }}
wrapperCol={{ span: 18 }}
>
<Form.Item label="Name" style={{display:"block"}}>
{getFieldDecorator(
'name',
{
initialValue: this.props.data.name,
rules: [
{
required: true,
message: 'Please input group name',
},
],
})(<Input
onChange={this.onChangeName}/>)}
</Form.Item>
<Form.Item label="Description" style={{display:"block"}}>
{getFieldDecorator(
'description',
{
initialValue: this.props.data.description,
rules: [
{
required: true,
message: 'Please input group description',
},
],
})(<Input
onChange={this.onChangeDescription}/>)}
</Form.Item>
</Form>
</div>
</Modal>
</div>
<div>
<Modal
title="Share Group"
width="500px"
visible={this.state.shareModalVisible}
onOk={this.handleShareOk}
onCancel={this.handleShareCancel}
footer={[
<Button key="new-role" onClick={this.handleShareCancel}>
New Role
</Button>,
<Button key="new-role-selection" onClick={this.handleShareCancel}>
New Role from Selection
</Button>,
<Button key="submit" type="primary" onClick={this.handleShareOk}>
Share
</Button>,
]}
>
<p>Select user role(s)</p>
<Select
mode="multiple"
defaultValue={"admin"}
style={{ width: '100%' }}
onChange={this.handleRolesDropdownChange}>
{item}
</Select>,
</Modal>
</div>
</div>
)
}
}
export default withConfigContext(Form.create({name: 'group-actions'})(GroupActions));

@ -18,40 +18,19 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd"; import {message, notification, Table, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import GroupActions from "./GroupActions";
import AddGroup from "./AddGroup";
const {Text} = Typography; const {Text} = Typography;
let config = null; let config = null;
let apiUrl; let apiUrl;
const columns = [
{
title: 'Group Name',
dataIndex: 'name',
width: 100,
},
{
title: 'Owner',
dataIndex: 'owner',
key: 'owner',
// render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Description',
dataIndex: 'description',
key: 'description',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
}
];
const getTimeAgo = (time) => { const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US'); const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time); return timeAgo.format(time);
@ -70,6 +49,38 @@ class GroupsTable extends React.Component {
}; };
} }
columns = [
{
title: 'Group Name',
dataIndex: 'name',
width: 100,
},
{
title: 'Owner',
dataIndex: 'owner',
key: 'owner',
// render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Description',
dataIndex: 'description',
key: 'description',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<GroupActions data={row} fetchGroups={this.fetchGroups}/>
</span>
),
},
];
rowSelection = { rowSelection = {
onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
this.setState({ this.setState({
@ -130,6 +141,8 @@ class GroupsTable extends React.Component {
}); });
}; };
handleTableChange = (pagination, filters, sorter) => { handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination}; const pager = {...this.state.pagination};
pager.current = pagination.current; pager.current = pagination.current;
@ -148,24 +161,29 @@ class GroupsTable extends React.Component {
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows} = this.state;
return ( return (
<div> <div>
<Table <div style={{background: '#f0f2f5'}}>
columns={columns} <AddGroup fetchGroups={this.fetchGroups} style={{marginBottom:"10px"}}/>
rowKey={record => (record.id)} </div>
dataSource={data} <div>
pagination={{ <Table
...pagination, columns={this.columns}
size: "small", rowKey={record => (record.id)}
// position: "top", dataSource={data}
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups` pagination={{
// showQuickJumper: true ...pagination,
}} size: "small",
loading={loading} // position: "top",
onChange={this.handleTableChange} showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
rowSelection={this.rowSelection} // showQuickJumper: true
scroll={{x: 1000}} }}
/> loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
/>
</div>
</div> </div>
); );
} }

@ -18,7 +18,7 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd"; import {message, notification, Table, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
@ -171,7 +171,6 @@ class PoliciesTable extends React.Component {
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}}
/> />
</div> </div>
); );

@ -18,9 +18,8 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Card, Col, Row, Select} from "antd"; import {Card, Col, Icon, message, notification, Row, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
@ -124,18 +123,27 @@ class RolesTable extends React.Component {
}; };
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows} = this.state;
const { Meta } = Card;
const itemCard = data.map((data) => const itemCard = data.map((data) =>
<Col span={8} key={data}> <Col span={5} key={data}>
<Card hoverable title="Role" bordered={true}> <Card
{data} size="default"
</Card> style={{ width: 200 }}
bordered={true}
actions={[
<Icon type="setting" key="setting" />,
<Icon type="edit" key="edit" />,]}
>
<Meta
avatar={<Icon type="book" key="roles"/>}
title={data}
/>
</Card>
</Col> </Col>
); );
return ( return (
<div style={{ background: '#ECECEC', padding: '30px' }}> <div style={{ background: '#ECECEC', padding: '20px' }}>
<Row gutter={16}> <Row gutter={16}>
{itemCard} {itemCard}
</Row> </Row>

@ -18,9 +18,8 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd"; import {Icon, message, notification, Table, Tag, Tooltip, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
@ -240,7 +239,6 @@ class UsersDevices extends React.Component {
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}}
/> />
</div> </div>
); );

@ -18,13 +18,11 @@
import React from "react"; import React from "react";
import axios from "axios"; import axios from "axios";
import {message, notification, Table, Typography, Panel, Collapse, Button, List, Modal, Icon} from "antd"; import {Button, Collapse, Icon, List, message, Modal, notification, Table, Tabs, Typography} from "antd";
import TimeAgo from 'javascript-time-ago' import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en' import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import DeviceTable from "../Devices/DevicesTable";
import UsersDevices from "./UsersDevices"; import UsersDevices from "./UsersDevices";
const {Text} = Typography; const {Text} = Typography;
@ -174,9 +172,9 @@ class UsersTable extends React.Component {
}; };
render() { render() {
const {data, pagination, loading, selectedRows} = this.state; const {data, pagination, loading, selectedRows} = this.state;
const { Panel } = Collapse; const { Panel } = Collapse;
const { TabPane } = Tabs;
const columns = [ const columns = [
{ {
title: 'User Name', title: 'User Name',
@ -231,30 +229,44 @@ class UsersTable extends React.Component {
loading={loading} loading={loading}
onChange={this.handleTableChange} onChange={this.handleTableChange}
rowSelection={this.rowSelection} rowSelection={this.rowSelection}
scroll={{x: 1000}}
/> />
</div> </div>
<div> <div>
<Modal <Modal
width="900px" width="900px"
title="Info"
visible={this.state.rolesModalVisible} visible={this.state.rolesModalVisible}
onOk={this.handleOk} onOk={this.handleOk}
onCancel={this.handleCancel} onCancel={this.handleCancel}
> >
<Collapse> <Tabs size="small" defaultActiveKey="1">
<Panel header="User Roles" key="1"> <TabPane
tab={
<span>
<Icon type="book"/>
Roles
</span>
}
key="1"
>
<List <List
size="small" size="small"
bordered bordered
dataSource={this.state.rolesData} dataSource={this.state.rolesData}
renderItem={item => <List.Item>{item}</List.Item>} renderItem={item => <List.Item>{item}</List.Item>}
/> />
</Panel> </TabPane>
<Panel header="Enrolled Devices" key="2"> <TabPane
tab={
<span>
<Icon type="appstore"/>
Enrolled Devices
</span>
}
key="2"
>
<UsersDevices user={this.state.user}/> <UsersDevices user={this.state.user}/>
</Panel> </TabPane>
</Collapse> </Tabs>
</Modal> </Modal>
</div> </div>
</div> </div>

@ -74,12 +74,25 @@ class Dashboard extends React.Component {
<span>Devices</span> <span>Devices</span>
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="geo"> <SubMenu
<Link to="/entgra/geo"> key="geo"
<Icon type="environment"/> title={
<span>Geo</span> <span>
</Link> <Icon type="environment"/>
</Menu.Item> <span>Geo</span>
</span>}
>
<Menu.Item key="singleDevice">
<Link to="/entgra/geo">
<span>Single Device View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceGroup">
<Link to="#">
<span>Device Group View</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="reports"> <Menu.Item key="reports">
<Link to="/entgra/reports"> <Link to="/entgra/reports">
<Icon type="bar-chart"/> <Icon type="bar-chart"/>

@ -25,6 +25,7 @@ import {
} from "antd"; } from "antd";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import GroupsTable from "../../../components/Groups/GroupsTable"; import GroupsTable from "../../../components/Groups/GroupsTable";
import AddGroup from "../../../components/Groups/AddGroup";
const {Paragraph} = Typography; const {Paragraph} = Typography;

@ -55,7 +55,7 @@ class Policies extends React.Component {
</div> </div>
</PageHeader> </PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}> <div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div> </div>
</div> </div>
); );

@ -157,7 +157,7 @@ class NormalLoginForm extends React.Component {
)} )}
<br/> <br/>
<a className="login-form-forgot" href="">Forgot password</a> <a className="login-form-forgot" href="">Forgot password</a>
<Button block type="primary" htmlType="submit" className="login-form-button"> <Button loading={this.state.loading} block type="primary" htmlType="submit" className="login-form-button">
Log in Log in
</Button> </Button>
</Form.Item> </Form.Item>

@ -1713,16 +1713,16 @@ public interface DeviceManagementService {
@Valid OperationRequest operationRequest); @Valid OperationRequest operationRequest);
@GET @GET
@Path("/status/count/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}/count")
@ApiOperation( @ApiOperation(
produces = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON,
httpMethod = "GET", httpMethod = "GET",
value = "Get Device Count with status", value = "Get Device Count with status",
notes = "Get specified device count with status.", notes = "Get specified device count with type and status.",
tags = "Device Management", tags = "Device Management",
extensions = { extensions = {
@Extension(properties = { @Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:details") @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view")
}) })
} }
) )
@ -1730,7 +1730,7 @@ public interface DeviceManagementService {
value = { value = {
@ApiResponse( @ApiResponse(
code = 200, code = 200,
message = "OK. \n Successfully fetched the details of the device.", message = "OK. \n Successfully fetched the count of matching devices.",
response = int.class, response = int.class,
responseHeaders = { responseHeaders = {
@ResponseHeader( @ResponseHeader(
@ -1764,13 +1764,6 @@ public interface DeviceManagementService {
response = ErrorResponse.class) response = ErrorResponse.class)
}) })
Response getDeviceCountByStatus( Response getDeviceCountByStatus(
@ApiParam(
name = "tenantDomain",
value = "The tenant doamin.",
required = true)
@PathParam("tenantDomain")
@Size(max = 45)
String tenantDomain,
@ApiParam( @ApiParam(
name = "type", name = "type",
value = "The device type name, such as ios, android, windows or fire-alarm.", value = "The device type name, such as ios, android, windows or fire-alarm.",
@ -1788,17 +1781,16 @@ public interface DeviceManagementService {
@GET @GET
@Path("/status/ids/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}/ids")
@ApiOperation( @ApiOperation(
produces = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON,
httpMethod = "GET", httpMethod = "GET",
value = "Getting Details of a Device", value = "Getting ids of devices with specified type and status",
notes = "Get the details of a device by specifying the device type and device identifier and optionally " + notes = "Get the ids of a device by specifying the device type and status.",
"the owner.",
tags = "Device Management", tags = "Device Management",
extensions = { extensions = {
@Extension(properties = { @Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:details") @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view")
}) })
} }
) )
@ -1840,13 +1832,6 @@ public interface DeviceManagementService {
response = ErrorResponse.class) response = ErrorResponse.class)
}) })
Response getDeviceIdentifiersByStatus( Response getDeviceIdentifiersByStatus(
@ApiParam(
name = "tenantDomain",
value = "The tenant domain.",
required = true)
@PathParam("tenantDomain")
@Size(max = 45)
String tenantDomain,
@ApiParam( @ApiParam(
name = "type", name = "type",
value = "The device type name, such as ios, android, windows or fire-alarm.", value = "The device type name, such as ios, android, windows or fire-alarm.",
@ -1863,7 +1848,7 @@ public interface DeviceManagementService {
String status); String status);
@PUT @PUT
@Path("/status/update/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}")
@ApiOperation( @ApiOperation(
produces = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON,
@ -1915,8 +1900,6 @@ public interface DeviceManagementService {
response = ErrorResponse.class) response = ErrorResponse.class)
}) })
Response bulkUpdateDeviceStatus( Response bulkUpdateDeviceStatus(
@ApiParam(name = "tenantDomain", value = "The tenant domain.", required = true)
@PathParam("tenantDomain") String tenantDomain,
@ApiParam(name = "type", value = "The device type, such as ios, android or windows.", required = true) @ApiParam(name = "type", value = "The device type, such as ios, android or windows.", required = true)
@PathParam("type") String type, @PathParam("type") String type,
@ApiParam(name = "status", value = "The device type, such as ios, android or windows.", required = true) @ApiParam(name = "status", value = "The device type, such as ios, android or windows.", required = true)

@ -458,8 +458,10 @@ public interface GroupManagementService {
@Path("/id/{groupId}") @Path("/id/{groupId}")
@DELETE @DELETE
@Consumes(MediaType.WILDCARD)
@ApiOperation( @ApiOperation(
produces = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON,
consumes = MediaType.WILDCARD,
httpMethod = HTTPConstants.HEADER_DELETE, httpMethod = HTTPConstants.HEADER_DELETE,
value = "Deleting a Group", value = "Deleting a Group",
notes = "If you wish to remove an existing group, that can be done by updating the group using this API.", notes = "If you wish to remove an existing group, that can be done by updating the group using this API.",

@ -45,9 +45,11 @@ import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses; import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.ResponseHeader; import io.swagger.annotations.ResponseHeader;
import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes; import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList;
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.util.Constants; import org.wso2.carbon.device.mgt.jaxrs.util.Constants;
@ -198,6 +200,59 @@ public interface DeviceManagementAdminService {
defaultValue = "5") defaultValue = "5")
@QueryParam("limit") int limit); @QueryParam("limit") int limit);
@Path("/count")
@GET
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = HTTPConstants.HEADER_GET,
value = "Get the count of devices.",
notes = "Returns count of all devices enrolled with the system.",
tags = "Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:admin:devices:view")
})
}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. \n Successfully fetched the device count.",
response = Integer.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 has been modified the last time.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. \n Empty body because the client has already the latest version of " +
"the requested resource."),
@ApiResponse(
code = 404,
message = "No groups found.",
response = ErrorResponse.class),
@ApiResponse(
code = 406,
message = "Not Acceptable.\n The requested media type is not supported."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Server error occurred while fetching the device count.",
response = ErrorResponse.class)
})
Response getDeviceCount(@ApiParam(
name = "status",
value = "status of group of which count should be retrieved")
@QueryParam("status")
String status);
@PUT @PUT
@Path("/device-owner") @Path("/device-owner")
@ApiOperation( @ApiOperation(

@ -33,10 +33,12 @@ import io.swagger.annotations.ResponseHeader;
import org.apache.axis2.transport.http.HTTPConstants; import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes; import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList;
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.Valid;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -75,6 +77,12 @@ import javax.ws.rs.core.Response;
description = "", description = "",
key = "perm:admin-groups:count", key = "perm:admin-groups:count",
permissions = {"/device-mgt/admin/groups/view"} permissions = {"/device-mgt/admin/groups/view"}
),
@Scope(
name = "Add groups",
description = "",
key = "perm:admin-groups:add",
permissions = {"/device-mgt/admin/groups/add"}
) )
} }
) )
@ -166,7 +174,7 @@ public interface GroupManagementAdminService {
) )
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. \n Successfully fetched the device group count.", @ApiResponse(code = 200, message = "OK. \n Successfully fetched the device group count.",
response = DeviceGroupList.class, response = Integer.class,
responseHeaders = { responseHeaders = {
@ResponseHeader( @ResponseHeader(
name = "Content-Type", name = "Content-Type",
@ -202,4 +210,71 @@ public interface GroupManagementAdminService {
@QueryParam("status") @QueryParam("status")
String status); String status);
@POST
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
httpMethod = HTTPConstants.HEADER_POST,
value = "Adding a New Device Group",
notes = "Add device group with the current user as the owner.",
tags = "Device Group Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:admin-groups:add")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 201,
message = "Created. \n Device group has successfully been created",
responseHeaders = {
@ResponseHeader(
name = "Content-Location",
description = "The URL of the added group."),
@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 has been modified the last time" +
".\n" + "Used by caches, or in conditional requests.")
}
),
@ApiResponse(
code = 303,
message = "See Other. \n Source can be retrieved from the URL specified at the Location " +
"header.",
responseHeaders = {
@ResponseHeader(
name = "Content-Location",
description = "The Source URL of the document.")}),
@ApiResponse(
code = 400,
message = "Bad Request. \n Invalid request or validation error.",
response = ErrorResponse.class),
@ApiResponse(
code = 401,
message = "Unauthorized. \n Current logged in user is not authorized to add device groups.",
response = ErrorResponse.class),
@ApiResponse(
code = 415,
message = "Unsupported media type. \n The entity of the request was in a not supported " +
"format."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while adding a new device group.",
response = ErrorResponse.class)
})
Response createGroup(@ApiParam(
name = "group",
value = "Define the group object with data.",
required = true)
@Valid DeviceGroup group);
} }

@ -973,66 +973,48 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
@GET @GET
@Override @Override
@Path("/status/count/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}/count")
public Response getDeviceCountByStatus(@PathParam("tenantDomain") String tenantDomain, @PathParam("type") String type, @PathParam("status") String status) { public Response getDeviceCountByStatus(@PathParam("type") String type, @PathParam("status") String status) {
int deviceCount; int deviceCount;
try { try {
int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain); deviceCount = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceCountOfTypeByStatus(type, status);
deviceCount = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceCountOfTypeByStatus(tenantId, type, status);
return Response.status(Response.Status.OK).entity(deviceCount).build(); return Response.status(Response.Status.OK).entity(deviceCount).build();
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
String errorMessage = "Error while retrieving device count."; String errorMessage = "Error while retrieving device count.";
log.error(errorMessage, e); log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} catch (UserStoreException e) {
String errorMessage = "Error resolving tenant Domain";
log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} }
} }
@GET @GET
@Override @Override
@Path("/status/ids/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}/ids")
public Response getDeviceIdentifiersByStatus(@PathParam("tenantDomain") String tenantDomain, @PathParam("type") String type, @PathParam("status") String status) { public Response getDeviceIdentifiersByStatus(@PathParam("type") String type, @PathParam("status") String status) {
List<String> deviceIds; List<String> deviceIds;
try { try {
int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain); deviceIds = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceIdentifiersByStatus(type, status);
deviceIds = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceIdentifiersByStatus(tenantId, type, status);
return Response.status(Response.Status.OK).entity(deviceIds.toArray(new String[0])).build(); return Response.status(Response.Status.OK).entity(deviceIds.toArray(new String[0])).build();
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
String errorMessage = "Error while obtaining list of devices"; String errorMessage = "Error while obtaining list of devices";
log.error(errorMessage, e); log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} catch (UserStoreException e) {
String errorMessage = "Error resolving tenant Domain";
log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} }
} }
@PUT @PUT
@Override @Override
@Path("/status/update/{tenantDomain}/{type}/{status}") @Path("/type/{type}/status/{status}")
public Response bulkUpdateDeviceStatus(@PathParam("tenantDomain") String tenantDomain, @PathParam("type") String type, public Response bulkUpdateDeviceStatus(@PathParam("type") String type, @PathParam("status") String status,
@PathParam("status") String status, @Valid List<String> deviceList) { @Valid List<String> deviceList) {
try { try {
int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain); DeviceMgtAPIUtils.getDeviceManagementService().bulkUpdateDeviceStatus(type, deviceList, status);
DeviceMgtAPIUtils.getDeviceManagementService().bulkUpdateDeviceStatus(tenantId, type, deviceList, status);
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
String errorMessage = "Error while updating device status in bulk."; String errorMessage = "Error while updating device status in bulk.";
log.error(errorMessage, e); log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} catch (UserStoreException e) {
String errorMessage = "Error resolving tenant Domain";
log.error(errorMessage, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMessage).build()).build();
} }
return Response.status(Response.Status.OK).build(); return Response.status(Response.Status.OK).build();
} }

@ -40,10 +40,10 @@ import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
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.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
@ -53,7 +53,13 @@ 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.validation.constraints.Size;
import javax.ws.rs.*; import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
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;
@ -107,6 +113,26 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
} }
} }
@Override
@Path("/count")
@GET
public Response getDeviceCount(@QueryParam("status") String status) {
int deviceCount;
try {
if (status == null) {
deviceCount = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceCount();
} else {
deviceCount = DeviceMgtAPIUtils.getDeviceManagementService().getDeviceCount(EnrolmentInfo.Status.valueOf(status));
}
} catch (DeviceManagementException e) {
String msg = "Error occurred while fetching device count.";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
return Response.status(Response.Status.OK).entity(deviceCount).build();
}
@PUT @PUT
@Override @Override
@Path("/device-owner") @Path("/device-owner")

@ -20,9 +20,11 @@ package org.wso2.carbon.device.mgt.jaxrs.service.impl.admin;
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.apache.solr.common.StringUtils;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList;
import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.GroupManagementAdminService; import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.GroupManagementAdminService;
@ -36,6 +38,10 @@ public class GroupManagementAdminServiceImpl implements GroupManagementAdminServ
private static final Log log = LogFactory.getLog(GroupManagementAdminServiceImpl.class); private static final Log log = LogFactory.getLog(GroupManagementAdminServiceImpl.class);
private static final String DEFAULT_ADMIN_ROLE = "admin";
private static final String[] DEFAULT_ADMIN_PERMISSIONS = {"/permission/device-mgt/admin/groups",
"/permission/device-mgt/user/groups"};
@Override @Override
public Response getGroups(String name, String owner, int offset, int limit, String status) { public Response getGroups(String name, String owner, int offset, int limit, String status) {
try { try {
@ -84,4 +90,24 @@ public class GroupManagementAdminServiceImpl implements GroupManagementAdminServ
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} }
} }
@Override
public Response createGroup(DeviceGroup group) {
if (group == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
group.setStatus(DeviceGroupConstants.GroupStatus.ACTIVE);
try {
DeviceMgtAPIUtils.getGroupManagementProviderService().createGroup(group, DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN_PERMISSIONS);
return Response.status(Response.Status.CREATED).build();
} catch (GroupManagementException e) {
String msg = "Error occurred while adding new group.";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (GroupAlreadyExistException e) {
String msg = "Group already exists with name " + group.getName() + ".";
log.warn(msg);
return Response.status(Response.Status.CONFLICT).entity(msg).build();
}
}
} }

@ -56,45 +56,29 @@ public class DeviceLocation implements Serializable {
@ApiModelProperty(name = "updatedTime", value = "Update time of the device.", required = true) @ApiModelProperty(name = "updatedTime", value = "Update time of the device.", required = true)
private Date updatedTime; private Date updatedTime;
@ApiModelProperty(name = "altitude", value = "Device altitude.", required = true) @ApiModelProperty(name = "altitude", value = "Device altitude.", required = true)
private Double altitude; private double altitude;
@ApiModelProperty(name = "speed", value = "Device speed.", required = true) @ApiModelProperty(name = "speed", value = "Device speed.", required = true)
private Float speed; private float speed;
@ApiModelProperty(name = "bearing", value = "Device bearing.", required = true) @ApiModelProperty(name = "bearing", value = "Device bearing.", required = true)
private Float bearing; private float bearing;
@ApiModelProperty(name = "distance", value = "Device distance.", required = true) @ApiModelProperty(name = "distance", value = "Device distance.", required = true)
private Double distance; private double distance;
public Double getDistance() { public double getAltitude() { return altitude; }
return distance;
}
public void setDistance(Double distance) { public void setAltitude(double altitude) { this.altitude = altitude; }
this.distance = distance;
}
public Double getAltitude() { public float getSpeed() { return speed; }
return altitude;
}
public Float getSpeed() { public void setSpeed(float speed) { this.speed = speed; }
return speed;
}
public void setSpeed(Float speed) { public float getBearing() { return bearing; }
this.speed = speed;
}
public Float getBearing() { public void setBearing(float bearing) { this.bearing = bearing; }
return bearing;
}
public void setBearing(Float bearing) { public double getDistance() { return distance; }
this.bearing = bearing;
}
public void setAltitude(Double altitude) { public void setDistance(double distance) { this.distance = distance; }
this.altitude = altitude;
}
public int getDeviceId() { public int getDeviceId() {
return deviceId; return deviceId;

@ -752,11 +752,11 @@ public interface DeviceManagementProviderService {
List<GeoCluster> findGeoClusters(String deviceType, GeoCoordinate southWest, GeoCoordinate northEast, List<GeoCluster> findGeoClusters(String deviceType, GeoCoordinate southWest, GeoCoordinate northEast,
int geohashLength) throws DeviceManagementException; int geohashLength) throws DeviceManagementException;
int getDeviceCountOfTypeByStatus(int tenantId, String deviceType, String deviceStatus) throws DeviceManagementException; int getDeviceCountOfTypeByStatus(String deviceType, String deviceStatus) throws DeviceManagementException;
List<String> getDeviceIdentifiersByStatus(int tenantId, String deviceType, String deviceStatus) throws DeviceManagementException; List<String> getDeviceIdentifiersByStatus(String deviceType, String deviceStatus) throws DeviceManagementException;
boolean bulkUpdateDeviceStatus(int tenantId, String deviceType, List<String> deviceList, String status) throws DeviceManagementException; boolean bulkUpdateDeviceStatus(String deviceType, List<String> deviceList, String status) throws DeviceManagementException;
boolean updateEnrollment(String owner, List<String> deviceIdentifiers) boolean updateEnrollment(String owner, List<String> deviceIdentifiers)
throws DeviceManagementException, UserNotFoundException, InvalidDeviceException; throws DeviceManagementException, UserNotFoundException, InvalidDeviceException;

@ -3125,10 +3125,10 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
} }
@Override @Override
public int getDeviceCountOfTypeByStatus(int tenantId, String deviceType, String deviceStatus) throws DeviceManagementException { public int getDeviceCountOfTypeByStatus(String deviceType, String deviceStatus) throws DeviceManagementException {
try { try {
DeviceManagementDAOFactory.openConnection(); DeviceManagementDAOFactory.openConnection();
return deviceDAO.getDeviceCount(deviceType, deviceStatus, tenantId); return deviceDAO.getDeviceCount(deviceType, deviceStatus, getTenantId());
} catch (DeviceManagementDAOException e) { } catch (DeviceManagementDAOException e) {
String msg = "Error occurred in while retrieving device count by status for deviceType :" +deviceType + " status : " + deviceStatus; String msg = "Error occurred in while retrieving device count by status for deviceType :" +deviceType + " status : " + deviceStatus;
log.error(msg, e); log.error(msg, e);
@ -3143,11 +3143,11 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
} }
@Override @Override
public List<String> getDeviceIdentifiersByStatus(int tenantId, String deviceType, String deviceStatus) throws DeviceManagementException { public List<String> getDeviceIdentifiersByStatus(String deviceType, String deviceStatus) throws DeviceManagementException {
List<String> deviceIds; List<String> deviceIds;
try { try {
DeviceManagementDAOFactory.openConnection(); DeviceManagementDAOFactory.openConnection();
deviceIds = deviceDAO.getDeviceIdentifiers(deviceType, deviceStatus, tenantId); deviceIds = deviceDAO.getDeviceIdentifiers(deviceType, deviceStatus, getTenantId());
} catch (DeviceManagementDAOException e) { } catch (DeviceManagementDAOException e) {
String msg = "Error occurred in while retrieving devices by status for deviceType :" +deviceType + " status : " + deviceStatus; String msg = "Error occurred in while retrieving devices by status for deviceType :" +deviceType + " status : " + deviceStatus;
log.error(msg, e); log.error(msg, e);
@ -3163,20 +3163,19 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
} }
@Override @Override
public boolean bulkUpdateDeviceStatus(int tenantId, String deviceType, public boolean bulkUpdateDeviceStatus(String deviceType, List<String> deviceList, String status)
List<String> deviceList, String status)
throws DeviceManagementException { throws DeviceManagementException {
boolean success; boolean success;
try { try {
DeviceManagementDAOFactory.openConnection(); DeviceManagementDAOFactory.beginTransaction();
success = deviceDAO.setEnrolmentStatusInBulk(deviceType, status, tenantId, deviceList); success = deviceDAO.setEnrolmentStatusInBulk(deviceType, status, getTenantId(), deviceList);
DeviceManagementDAOFactory.commitTransaction(); DeviceManagementDAOFactory.commitTransaction();
} catch (DeviceManagementDAOException e) { } catch (DeviceManagementDAOException e) {
String msg = "Error occurred in while updating status of devices :" + deviceType + " status : " + deviceList DeviceManagementDAOFactory.rollbackTransaction();
.toString(); String msg = "Error occurred in while updating status of devices :" + deviceType + " status : " + status;
log.error(msg, e); log.error(msg, e);
throw new DeviceManagementException(msg, e); throw new DeviceManagementException(msg, e);
} catch (SQLException e) { } catch (TransactionManagementException e) {
String msg = "Error occurred while opening a connection to the data source"; String msg = "Error occurred while opening a connection to the data source";
log.error(msg, e); log.error(msg, e);
throw new DeviceManagementException(msg, e); throw new DeviceManagementException(msg, e);

@ -118,7 +118,7 @@
{{/if}} {{/if}}
{{#if iosPluginFlag}} {{#if iosPluginFlag}}
<li> <li>
<a href="{{@app.context}}/dep/devices"><i class="fw fw-ios"></i> <a href="{{@app.context}}/dep/devices"><i class="fw fw-apple"></i>
DEP Configurations DEP Configurations
</a> </a>
</li> </li>

@ -155,8 +155,8 @@ public class PolicyManagerUtil {
PolicyAdministratorPoint pap = new PolicyAdministratorPointImpl(); PolicyAdministratorPoint pap = new PolicyAdministratorPointImpl();
try { try {
Policy correctivePolicy = pap.getPolicy(correctiveAction.getPolicyId()); Policy correctivePolicy = pap.getPolicy(correctiveAction.getPolicyId());
if (correctivePolicy == null || PolicyManagementConstants.CORRECTIVE_POLICY_TYPE if (correctivePolicy == null || !PolicyManagementConstants.CORRECTIVE_POLICY_TYPE
.equalsIgnoreCase(correctivePolicy.getPolicyType())) { .equalsIgnoreCase(correctivePolicy.getPolicyType() )) {
String msg = "No corrective policy was found for the policy " + policy.getPolicyName() + String msg = "No corrective policy was found for the policy " + policy.getPolicyName() +
" and policy ID " + policy.getId(); " and policy ID " + policy.getId();
log.error(msg); log.error(msg);

@ -4,7 +4,7 @@
CREATE TABLE IF NOT EXISTS AP_APP( CREATE TABLE IF NOT EXISTS AP_APP(
ID INTEGER NOT NULL AUTO_INCREMENT, ID INTEGER NOT NULL AUTO_INCREMENT,
NAME VARCHAR(45) NOT NULL, NAME VARCHAR(45) NOT NULL,
DESCRIPTION CLOB NULL, DESCRIPTION VARCHAR(200) NOT NULL,
TYPE VARCHAR(200) NOT NULL, TYPE VARCHAR(200) NOT NULL,
TENANT_ID INTEGER NOT NULL, TENANT_ID INTEGER NOT NULL,
STATUS VARCHAR(45) NOT NULL DEFAULT 'ACTIVE', STATUS VARCHAR(45) NOT NULL DEFAULT 'ACTIVE',
@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS AP_APP(
-- ----------------------------------------------------- -- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS AP_APP_RELEASE( CREATE TABLE IF NOT EXISTS AP_APP_RELEASE(
ID INTEGER NOT NULL AUTO_INCREMENT, ID INTEGER NOT NULL AUTO_INCREMENT,
DESCRIPTION CLOB NOT NULL, DESCRIPTION VARCHAR(200) NOT NULL,
VERSION VARCHAR(70) NOT NULL, VERSION VARCHAR(70) NOT NULL,
TENANT_ID INTEGER NOT NULL, TENANT_ID INTEGER NOT NULL,
UUID VARCHAR(200) NOT NULL, UUID VARCHAR(200) NOT NULL,
@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS AP_APP_RELEASE(
SC_3_LOCATION VARCHAR(100) NULL DEFAULT NULL, SC_3_LOCATION VARCHAR(100) NULL DEFAULT NULL,
APP_HASH_VALUE VARCHAR(1000) NOT NULL, APP_HASH_VALUE VARCHAR(1000) NOT NULL,
SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE, SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE,
APP_META_INFO CLOB NULL DEFAULT NULL, APP_META_INFO VARCHAR(150) NULL DEFAULT NULL,
SUPPORTED_OS_VERSIONS VARCHAR(45) NOT NULL, SUPPORTED_OS_VERSIONS VARCHAR(45) NOT NULL,
RATING DOUBLE NULL DEFAULT NULL, RATING DOUBLE NULL DEFAULT NULL,
CURRENT_STATE VARCHAR(45) NOT NULL, CURRENT_STATE VARCHAR(45) NOT NULL,
@ -57,8 +57,8 @@ CREATE TABLE IF NOT EXISTS AP_APP_REVIEW(
COMMENT TEXT NOT NULL, COMMENT TEXT NOT NULL,
ROOT_PARENT_ID INTEGER NOT NULL, ROOT_PARENT_ID INTEGER NOT NULL,
IMMEDIATE_PARENT_ID INTEGER NOT NULL, IMMEDIATE_PARENT_ID INTEGER NOT NULL,
CREATED_AT TIMESTAMP NOT NULL, CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
MODIFIED_AT TIMESTAMP NOT NULL, MODIFIED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
RATING INTEGER NULL, RATING INTEGER NULL,
USERNAME VARCHAR(45) NOT NULL, USERNAME VARCHAR(45) NOT NULL,
ACTIVE_REVIEW BOOLEAN NOT NULL DEFAULT TRUE, ACTIVE_REVIEW BOOLEAN NOT NULL DEFAULT TRUE,
@ -267,14 +267,11 @@ CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION(
ID INTEGER NOT NULL AUTO_INCREMENT, ID INTEGER NOT NULL AUTO_INCREMENT,
TASK_NAME VARCHAR(100) NOT NULL, TASK_NAME VARCHAR(100) NOT NULL,
APPLICATION_UUID VARCHAR(36) NOT NULL, APPLICATION_UUID VARCHAR(36) NOT NULL,
SUBSCRIBER_LIST LONGVARCHAR NOT NULL, SUBSCRIBER_LIST TEXT NOT NULL,
STATUS VARCHAR(15) NOT NULL, STATUS VARCHAR(15) NOT NULL,
SCHEDULED_AT TIMESTAMP NOT NULL, SCHEDULED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
SCHEDULED_BY VARCHAR(100) NOT NULL, SCHEDULED_BY VARCHAR(100) NOT NULL,
SCHEDULED_TIMESTAMP TIMESTAMP NOT NULL, SCHEDULED_TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
DELETED BOOLEAN, DELETED BOOLEAN,
PRIMARY KEY (ID), PRIMARY KEY (ID)
CONSTRAINT fk_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE );
FOREIGN KEY (APPLICATION_UUID)
REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION
);
Loading…
Cancel
Save