Skip to content

Commit

Permalink
Update device summary store structure (#26)
Browse files Browse the repository at this point in the history
* remove interface id from device summary

* update reducer per feedback

* address more comments

* more comments addressed
  • Loading branch information
YingXue committed Jul 29, 2019
1 parent 83ade53 commit 52617f6
Show file tree
Hide file tree
Showing 25 changed files with 337 additions and 454 deletions.
9 changes: 0 additions & 9 deletions src/app/api/dataTransforms/deviceSummaryTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@ import { DeviceSummary } from '../models/deviceSummary';
import { parseDateTimeString } from './transformHelper';
import { Device } from '../models/device';
import { DeviceIdentity } from '../models/deviceIdentity';
import { SynchronizationStatus } from '../models/synchronizationStatus';

export const transformDevice = (device: Device): DeviceSummary => {
return {
authenticationType: device.AuthenticationType,
cloudToDeviceMessageCount: device.CloudToDeviceMessageCount,
deviceId: device.DeviceId,
deviceSummarySynchronizationStatus: SynchronizationStatus.initialized,
interfaceIds: [],
isEdgeDevice: device.IotEdge,
isPnpDevice: undefined,
lastActivityTime: parseDateTimeString(device.LastActivityTime),
status: device.Status,
statusUpdatedTime: parseDateTimeString(device.StatusUpdatedTime)
Expand All @@ -28,10 +23,6 @@ export const transformDeviceIdentity = (device: DeviceIdentity): DeviceSummary =
authenticationType: device.authentication.type,
cloudToDeviceMessageCount: device.cloudToDeviceMessageCount,
deviceId: device.deviceId,
deviceSummarySynchronizationStatus: SynchronizationStatus.initialized,
interfaceIds: [],
isEdgeDevice: device.capabilities.iotEdge,
isPnpDevice: false, // device created using add device api won't be pnp device
lastActivityTime: parseDateTimeString(device.lastActivityTime),
status: device.status,
statusUpdatedTime: parseDateTimeString(device.statusUpdatedTime)
Expand Down
9 changes: 0 additions & 9 deletions src/app/api/models/deviceSummary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,11 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import { SynchronizationStatus } from './synchronizationStatus';

export interface DeviceSummary {
deviceId: string;
isEdgeDevice?: boolean;
isPnpDevice: boolean;
status: string;
lastActivityTime: string;
statusUpdatedTime: string;
cloudToDeviceMessageCount: string;
authenticationType: string;

// Interfaces
interfaceIds: string[];

deviceSummarySynchronizationStatus: SynchronizationStatus;
}
5 changes: 3 additions & 2 deletions src/app/api/models/deviceSummaryListWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import { List, Map } from 'immutable';
import { Map } from 'immutable';
import { ErrorResponse } from './errorResponse';
import { SynchronizationStatus } from './synchronizationStatus';
import { DeviceSummary } from './deviceSummary';

export interface DeviceSummaryListWrapper {
deviceList?: List<Map<string, any>>; // tslint:disable-line:no-any
deviceList?: Map<string, DeviceSummary>;
deviceListSynchronizationStatus: SynchronizationStatus;
error?: ErrorResponse;
}
22 changes: 13 additions & 9 deletions src/app/devices/deviceContent/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,23 @@ export const getDigitalTwinInterfacePropertiesSelector = (state: StateInterface)

export const getDigitalTwinInterfaceNameAndIdsSelector = createSelector(
getDigitalTwinInterfacePropertiesSelector,
// tslint:disable-next-line:cyclomatic-complexity
properties => {
return properties &&
properties.interfaces &&
properties.interfaces[modelDiscoveryInterfaceName] &&
properties.interfaces[modelDiscoveryInterfaceName].properties &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported.value &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported.value.interfaces;
return getReportedInterfacesFromDigitalTwin(properties);
}
);

// tslint:disable-next-line:cyclomatic-complexity
export const getReportedInterfacesFromDigitalTwin = (properties: DigitalTwinInterfaces) => {
return properties &&
properties.interfaces &&
properties.interfaces[modelDiscoveryInterfaceName] &&
properties.interfaces[modelDiscoveryInterfaceName].properties &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported.value &&
properties.interfaces[modelDiscoveryInterfaceName].properties.modelInformation.reported.value.interfaces;
};

const getDigitalTwinInterfaceIdToNameMapSelector = createSelector(
getDigitalTwinInterfaceNameAndIdsSelector,
nameAndIds => {
Expand Down
6 changes: 0 additions & 6 deletions src/app/devices/deviceList/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,16 @@ import { DeviceSummary } from '../../api/models/deviceSummary';
import { BulkRegistryOperationResult } from '../../api/models/bulkRegistryOperationResult';
import { DeviceIdentity } from '../../api/models/deviceIdentity';
import DeviceQuery from '../../api/models/deviceQuery';
import { DigitalTwinInterfaces } from '../../api/models/digitalTwinModels';

const deviceListCreator = actionCreatorFactory(actionPrefixes.DEVICELISTS);
const clearDevicesAction = deviceListCreator<void>(actionTypes.CLEAR_DEVICES);
const listDevicesAction = deviceListCreator.async<DeviceQuery, DeviceSummary[]>(actionTypes.LIST_DEVICES);
const deleteDevicesAction = deviceListCreator.async<string[], BulkRegistryOperationResult>(actionTypes.DELETE_DEVICES);
const addDeviceAction = deviceListCreator.async<DeviceIdentity, DeviceSummary>(actionTypes.ADD_DEVICE);

const setIsPnpAction = deviceListCreator.async<string, boolean>(actionTypes.SET_ISPNP);
const fetchInterfacesAction = deviceListCreator.async<string, DigitalTwinInterfaces>(actionTypes.FETCH_INTERFACES);

export {
addDeviceAction,
clearDevicesAction,
deleteDevicesAction,
listDevicesAction,
setIsPnpAction,
fetchInterfacesAction
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@ exports[`components/devices/DeviceList matches snapshot 1`] = `
<DeviceListComponent
connectionString="connectionString"
deleteDevices={[MockFunction]}
deviceListSyncStatus={1}
devices={
Array [
Object {
"authenticationType": "sas",
"cloudToDeviceMessageCount": "0",
"deviceId": "testDeviceId",
"deviceSummarySynchronizationStatus": 1,
"interfaceIds": Array [],
"isPnpDevice": true,
"lastActivityTime": "0001-01-01T00:00:00Z",
"status": "Enabled",
"statusUpdatedTime": "0001-01-01T00:00:00Z",
},
]
}
isFetching={false}
listDevices={[MockFunction]}
t={[MockFunction]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ exports[`components/devices/addDevice matches snapshot 1`] = `
],
}
}
value=""
/>
<div
className="authentication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
**********************************************************/
import * as React from 'react';
import { RouteComponentProps, Route } from 'react-router-dom';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import { PrimaryButton, DefaultButton, Overlay } from 'office-ui-fabric-react';
import { Toggle } from 'office-ui-fabric-react/lib/Toggle';
import { ChoiceGroup, IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
Expand All @@ -17,12 +17,16 @@ import { validateKey, validateThumbprint, validateDeviceId } from '../../../../.
import LabelWithTooltip from '../../../../../shared/components/labelWithTooltip';
import BreadcrumbContainer from '../../../../../shared/components/breadcrumbContainer';
import { CopyableMaskField } from '../../../../../shared/components/copyableMaskField';
import { SynchronizationStatus } from '../../../../../api/models/synchronizationStatus';
import '../../../../../css/_addDevice.scss';
import '../../../../../css/_layouts.scss';

export interface AddDeviceActionProps {
handleSave: (deviceIdentity: DeviceIdentity) => void;
listDevices: () => void;
}

export interface AddDeviceDataProps {
deviceListSyncStatus: SynchronizationStatus;
}

export interface AddDeviceState {
Expand All @@ -42,18 +46,18 @@ export interface AddDeviceState {
secondaryThumbprintError?: string;
}

export default class AddDevice extends React.Component<AddDeviceActionProps & RouteComponentProps, AddDeviceState> {
constructor(props: AddDeviceActionProps & RouteComponentProps) {
export default class AddDevice extends React.Component<AddDeviceActionProps & AddDeviceDataProps & RouteComponentProps, AddDeviceState> {
constructor(props: AddDeviceActionProps & AddDeviceDataProps & RouteComponentProps) {
super(props);

this.state = {
authenticationType: DeviceAuthenticationType.SymmetricKey,
autoGenerateKeys: true,
deviceId: undefined,
primaryKey: undefined,
primaryThumbprint: undefined,
secondaryKey: undefined,
secondaryThumbprint: undefined,
deviceId: '',
primaryKey: '',
primaryThumbprint: '',
secondaryKey: '',
secondaryThumbprint: '',
status: DeviceStatus.Enabled
};
}
Expand All @@ -75,14 +79,17 @@ export default class AddDevice extends React.Component<AddDeviceActionProps & Ro
{this.showCommands(context)}
</div>
</div>
{this.props.deviceListSyncStatus === SynchronizationStatus.updating && <Overlay/>}
</div>
)}
</LocalizationContextConsumer>
);
}

public componentDidMount() {
this.props.listDevices();
public componentWillReceiveProps(newProps: AddDeviceActionProps & AddDeviceDataProps & RouteComponentProps) {
if (newProps.deviceListSyncStatus === SynchronizationStatus.upserted) { // only when device has been added successfully would navigate to list view
this.props.history.push(`/devices`);
}
}

private readonly showDeviceId = (context: LocalizationContextInterface) => {
Expand Down Expand Up @@ -385,7 +392,6 @@ export default class AddDevice extends React.Component<AddDeviceActionProps & Ro

private readonly handleSave = () => {
this.props.handleSave(this.getDeviceIdentity());
this.props.history.push(`/devices`);
}

private readonly handleCancel = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
**********************************************************/
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import AddDevice, { AddDeviceActionProps } from './addDevice';
import AddDevice, { AddDeviceActionProps, AddDeviceDataProps } from './addDevice';
import { DeviceIdentity } from '../../../../../api/models/deviceIdentity';
import { addDeviceAction, listDevicesAction } from '../../../actions';
import { StateType } from '../../../../../shared/redux/state';
import { getDeviceSummaryListStatus } from '../../../selectors';

const mapDispatchToProps = (dispatch: Dispatch): AddDeviceActionProps => {
return {
handleSave: (deviceIdentity: DeviceIdentity) => dispatch(addDeviceAction.started(deviceIdentity)),
listDevices: () => dispatch(listDevicesAction.started(undefined))
};
};

export default connect(undefined, mapDispatchToProps, undefined, {
const mapStateToProps = (state: StateType): AddDeviceDataProps => {
return {
deviceListSyncStatus: getDeviceSummaryListStatus(state)
};
};

export default connect(mapStateToProps, mapDispatchToProps, undefined, {
pure: false,
})(AddDevice);
12 changes: 5 additions & 7 deletions src/app/devices/deviceList/components/deviceList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import DeviceListComponent from './deviceList';
import { testWithLocalizationContext } from '../../../shared/utils/testHelpers';
import { SynchronizationStatus } from '../../../api/models/synchronizationStatus';
import { DeviceSummary } from '../../../api/models/deviceSummary';

const devices = [
const devices: DeviceSummary[] = [
{
authenticationType: 'sas',
cloudToDeviceMessageCount: '0',
deviceId: 'testDeviceId',
deviceSummarySynchronizationStatus: SynchronizationStatus.fetched,
interfaceIds: [],
isPnpDevice: true,
lastActivityTime: '0001-01-01T00:00:00Z',
status: 'Enabled',
statusUpdatedTime: '0001-01-01T00:00:00Z'
}
];
jest.mock('./deviceListCell', () => <></>);

describe('components/devices/DeviceList', () => {
it('matches snapshot', () => {
Expand All @@ -31,7 +29,7 @@ describe('components/devices/DeviceList', () => {
devices={devices}
listDevices={jest.fn()}
deleteDevices={jest.fn()}
deviceListSyncStatus={SynchronizationStatus.fetched}
isFetching={false}
/>);

expect(wrapper).toMatchSnapshot();
Expand All @@ -44,7 +42,7 @@ describe('components/devices/DeviceList', () => {
devices={[]}
listDevices={jest.fn()}
deleteDevices={jest.fn()}
deviceListSyncStatus={SynchronizationStatus.fetched}
isFetching={false}
/>);
const child = shallow(wrapper.props().children());

Expand Down
Loading

0 comments on commit 52617f6

Please sign in to comment.