Skip to content

Commit

Permalink
Eventhub (#237)
Browse files Browse the repository at this point in the history
* initial commit

* update tests and fix package

* address comments
  • Loading branch information
YingXue authored Mar 12, 2020
1 parent feeb6f5 commit e662d66
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 67 deletions.
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/app/api/parameters/deviceParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ export interface FetchDevicesParameters extends DataPlaneParameters {

export interface MonitorEventsParameters {
deviceId: string;
startTime?: Date;
hubConnectionString: string;
fetchSystemProperties?: boolean;
consumerGroup: string;

customEventHubConnectionString?: string;
hubConnectionString?: string;

fetchSystemProperties?: boolean;
startTime?: Date;
}

export interface DeleteDevicesParameters extends DataPlaneParameters {
Expand Down
10 changes: 4 additions & 6 deletions src/app/api/services/devicesService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CONNECTION_TIMEOUT_IN_SECONDS, RESPONSE_TIME_IN_SECONDS } from '../../c
import { Twin } from '../models/device';
import { DeviceIdentity } from './../models/deviceIdentity';
import { buildQueryString, getConnectionInfoFromConnectionString } from '../shared/utils';
import { DataPlaneParameters } from '../parameters/deviceParameters';
import { DataPlaneParameters, MonitorEventsParameters } from '../parameters/deviceParameters';

const deviceId = 'deviceId';
const connectionString = 'HostName=test-string.azure-devices.net;SharedAccessKeyName=owner;SharedAccessKey=fakeKey=';
Expand Down Expand Up @@ -883,8 +883,9 @@ describe('deviceTwinService', () => {
});

context('monitorEvents', () => {
let parameters = {
let parameters: MonitorEventsParameters = {
consumerGroup: '$Default',
customEventHubConnectionString: undefined,
deviceId,
fetchSystemProperties: undefined,
hubConnectionString: undefined,
Expand All @@ -911,10 +912,7 @@ describe('deviceTwinService', () => {
const result = await DevicesService.monitorEvents(parameters);

const eventHubRequestParameters = {
connectionString,
consumerGroup: parameters.consumerGroup,
deviceId: parameters.deviceId,
fetchSystemProperties: parameters.fetchSystemProperties,
...parameters,
startTime: parameters.startTime && parameters.startTime.toISOString()
};

Expand Down
9 changes: 3 additions & 6 deletions src/app/api/services/devicesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,21 +343,18 @@ export const deleteDevices = async (parameters: DeleteDevicesParameters) => {
// tslint:disable-next-line:cyclomatic-complexity
export const monitorEvents = async (parameters: MonitorEventsParameters): Promise<Message[]> => {
try {
if (!parameters.hubConnectionString) {
if (!parameters.hubConnectionString && !parameters.customEventHubConnectionString) {
return;
}

const requestParameters = {
connectionString: parameters.hubConnectionString,
consumerGroup: parameters.consumerGroup,
deviceId: parameters.deviceId,
fetchSystemProperties: parameters.fetchSystemProperties,
...parameters,
startTime: parameters.startTime && parameters.startTime.toISOString()
};

const response = await request(EVENTHUB_MONITOR_ENDPOINT, requestParameters);
const messages = await response.json() as Message[];
return messages && messages.length !== 0 && messages.map(message => parseEventHubMessage(message)) || [];
return messages && messages.length && messages.length !== 0 && messages.map(message => parseEventHubMessage(message)) || [];
} catch (error) {
throw error;
}
Expand Down
25 changes: 24 additions & 1 deletion src/app/css/_deviceEvents.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,33 @@
@include themify($themes) {
color: themed('textColor');
}
width: 500px;
width: 80%;
}

.custom-event-hub-label {
font-weight: 600;
font-size: 14px;
@include themify($themes) {
color: themed('textColor');
}
}

.custom-event-hub-text-field {
display: grid;
padding-left: 24px;
padding-bottom: 10px;
@include themify($themes) {
color: themed('textColor');
}
width: 80%;
}

.toggle-button {
padding-left: 24px;
}

.scrollable-telemetry {
height: calc(100vh - 400px);
overflow-y: auto;
}
}
5 changes: 0 additions & 5 deletions src/app/css/_devicePnpDetailList.scss
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,3 @@
height: calc(100vh - 400px);
overflow-y: auto;
}

.scrollable-telemetry {
height: calc(100vh - 360px);
overflow-y: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ exports[`components/devices/deviceEvents matches snapshot in electron 1`] = `
underlined={true}
value="$Default"
/>
<StyledTextFieldBase
ariaLabel="deviceEvents.customEventHub.label"
className="custom-event-hub-text-field"
disabled={false}
errorMessage={false}
label="deviceEvents.customEventHub.label"
onChange={[Function]}
onRenderLabel={[Function]}
placeholder="deviceEvents.customEventHub.placeHolder"
underlined={true}
/>
<InfiniteScroll
className="device-events-container"
element="div"
Expand Down Expand Up @@ -145,6 +156,17 @@ exports[`components/devices/deviceEvents matches snapshot in hosted environment
underlined={true}
value="$Default"
/>
<StyledTextFieldBase
ariaLabel="deviceEvents.customEventHub.label"
className="custom-event-hub-text-field"
disabled={false}
errorMessage={false}
label="deviceEvents.customEventHub.label"
onChange={[Function]}
onRenderLabel={[Function]}
placeholder="deviceEvents.customEventHub.placeHolder"
underlined={true}
/>
<InfiniteScroll
className="device-events-container"
element="div"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ describe('components/devices/deviceEvents', () => {
expect((wrapper.state() as DeviceEventsState).consumerGroup).toEqual('testGroup');
});

it('changes state accordingly when custom event hub connection string value is changed', () => {
const wrapper = mountWithLocalization(getComponent());
const textField = wrapper.find(TextField).at(1);
textField.instance().props.onChange({ target: null}, 'sb://testeventhub');
wrapper.update();
expect((wrapper.state() as DeviceEventsState).customEventHubConnectionString).toEqual('sb://testeventhub');
});

it('renders events', () => {
const wrapper = mountWithLocalization(getComponent());
const events = [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { DEFAULT_CONSUMER_GROUP } from '../../../../constants/apiConstants';
import { MILLISECONDS_IN_MINUTE } from '../../../../constants/shared';
import { appConfig, HostMode } from '../../../../../appConfig/appConfig';
import { HeaderView } from '../../../../shared/components/headerView';
import { isValidEventHubConnectionString } from '../../../../shared/utils/hubConnectionStringHelper';
import '../../../../css/_deviceEvents.scss';

const JSON_SPACES = 2;
Expand All @@ -40,6 +41,7 @@ export interface DeviceEventsState {
synchronizationStatus: SynchronizationStatus;
consumerGroup: string;
monitoringData: boolean;
customEventHubConnectionString?: string;
}

export default class DeviceEventsComponent extends React.Component<DeviceEventsDataProps & RouteComponentProps, DeviceEventsState> {
Expand Down Expand Up @@ -78,16 +80,8 @@ export default class DeviceEventsComponent extends React.Component<DeviceEventsD
headerText={ResourceKeys.deviceEvents.headerText}
tooltip={ResourceKeys.deviceEvents.tooltip}
/>
<TextField
className={'consumer-group-text-field'}
onRenderLabel={this.renderConsumerGroupLabel}
label={context.t(ResourceKeys.deviceEvents.consumerGroups.label)}
ariaLabel={context.t(ResourceKeys.deviceEvents.consumerGroups.label)}
underlined={true}
value={this.state.consumerGroup}
disabled={this.state.monitoringData}
onChange={this.consumerGroupChange}
/>
{this.renderConsumerGroup(context)}
{this.renderCustomEventHub(context)}
{this.renderInfiniteScroll(context)}
{this.state.loadingAnnounced}
</div>
Expand Down Expand Up @@ -159,26 +153,69 @@ export default class DeviceEventsComponent extends React.Component<DeviceEventsD
}
}

private consumerGroupChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
if (!!newValue) {
private renderConsumerGroup = (context: LocalizationContextInterface) => {
const renderConsumerGroupLabel = (props: ITextFieldProps) => (
<LabelWithTooltip
className={'consumer-group-label'}
tooltipText={context.t(ResourceKeys.deviceEvents.consumerGroups.tooltip)}
>
{props.label}
</LabelWithTooltip>
);

const consumerGroupChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
this.setState({
consumerGroup: newValue
});
}
};

return (
<TextField
className={'consumer-group-text-field'}
onRenderLabel={renderConsumerGroupLabel}
label={context.t(ResourceKeys.deviceEvents.consumerGroups.label)}
ariaLabel={context.t(ResourceKeys.deviceEvents.consumerGroups.label)}
underlined={true}
value={this.state.consumerGroup}
disabled={this.state.monitoringData}
onChange={consumerGroupChange}
/>
);
}

private renderConsumerGroupLabel = (props: ITextFieldProps) => {
private renderCustomEventHub = (context: LocalizationContextInterface) => {
const renderCustomEventHubLabel = (props: ITextFieldProps) => (
<LabelWithTooltip
className={'custom-event-hub-label'}
tooltipText={context.t(ResourceKeys.deviceEvents.customEventHub.tooltip)}
>
{props.label}
</LabelWithTooltip>
);

const customEventHubChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
this.setState({
customEventHubConnectionString: newValue
});
};

const renderError = () => {
return !isValidEventHubConnectionString(this.state.customEventHubConnectionString) && context.t(ResourceKeys.deviceEvents.customEventHub.error);
};

return (
<LocalizationContextConsumer>
{(context: LocalizationContextInterface) => (
<LabelWithTooltip
className={'consumer-group-label'}
tooltipText={context.t(ResourceKeys.deviceEvents.consumerGroups.tooltip)}
>
{props.label}
</LabelWithTooltip>
)}
</LocalizationContextConsumer>
<TextField
className={'custom-event-hub-text-field'}
onRenderLabel={renderCustomEventHubLabel}
label={context.t(ResourceKeys.deviceEvents.customEventHub.label)}
ariaLabel={context.t(ResourceKeys.deviceEvents.customEventHub.label)}
underlined={true}
value={this.state.customEventHubConnectionString}
disabled={this.state.monitoringData}
onChange={customEventHubChange}
placeholder={context.t(ResourceKeys.deviceEvents.customEventHub.placeHolder)}
errorMessage={renderError()}
/>
);
}

Expand Down Expand Up @@ -273,13 +310,14 @@ export default class DeviceEventsComponent extends React.Component<DeviceEventsD
() => {
monitorEvents({
consumerGroup: this.state.consumerGroup,
customEventHubConnectionString: this.state.customEventHubConnectionString,
deviceId: getDeviceIdFromQueryString(this.props),
fetchSystemProperties: this.state.showSystemProperties,
hubConnectionString: this.props.connectionString,
hubConnectionString: this.state.customEventHubConnectionString ? null : this.props.connectionString,
startTime: this.state.startTime
})
.then(results => {
const messages = results && results.reverse().map((message: Message) => message);
const messages = results ? results.reverse().map((message: Message) => message) : [];
if (this.isComponentMounted) {
this.setState({
events: [...messages, ...this.state.events],
Expand Down
8 changes: 7 additions & 1 deletion src/app/shared/utils/hubConnectionStringHelper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License
**********************************************************/
import 'jest';
import { generateConnectionStringValidationError } from './hubConnectionStringHelper';
import { generateConnectionStringValidationError, isValidEventHubConnectionString } from './hubConnectionStringHelper';
import { ResourceKeys } from '../../../localization/resourceKeys';

describe('hubConnectionStringHelper', () => {
Expand All @@ -27,4 +27,10 @@ describe('hubConnectionStringHelper', () => {
it('does not generate error when value is in right format', () => {
expect(generateConnectionStringValidationError('HostName=testhub.azure-devices.net;SharedAccessKeyName=123;SharedAccessKey=456')).toEqual(null);
});

it('validate event hub connection string', () => {
expect(isValidEventHubConnectionString(null)).toEqual(true);
expect(isValidEventHubConnectionString('Endpoint=sb://123/;SharedAccessKeyName=456;SharedAccessKey=789')).toEqual(true);
expect(isValidEventHubConnectionString('Endpoint=sb://123/;SharedAccessKeyName=456;SharedAccess=789')).toEqual(false);
});
});
Loading

0 comments on commit e662d66

Please sign in to comment.