Skip to content

Commit

Permalink
feat(slack): Introduce slack support channel selector (#7658)
Browse files Browse the repository at this point in the history
* feat(slack): Introduce slack support channel selector

* Use ReactSelectInput

* Use hooks instead of class compoonent

* Disable slack in settings

* Add useLatestPromise hook

* Rebase with updated react-select modules

* Rebase with updated react-select modules

* Update test

* Update test

* Refactor test
  • Loading branch information
caseyhebebrand committed Dec 2, 2019
1 parent fc0c9d5 commit bf2ffe2
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
<pager-duty-tag api-key="vm.application.attributes.pdApiKey"></pager-duty-tag>
</dd>
</render-if-feature>
<render-if-feature feature="slack">
<dt ng-if="vm.application.attributes.slackChannel && vm.application.attributes.slackChannel.name">Slack Channel</dt>
<dd ng-if="vm.application.attributes.slackChannel && vm.application.attributes.slackChannel.name">
<a
target="_blank"
href="https://netflix.slack.com/app_redirect?channel={{vm.application.attributes.slackChannel.name}}"
>#{{vm.application.attributes.slackChannel.name}}</a
>
</dd>
</render-if-feature>
<dt ng-if="vm.application.attributes.repoType">Source Repo Type</dt>
<dd ng-if="vm.application.attributes.repoType">{{vm.application.attributes.repoType}}</dd>
<dt ng-if="vm.application.attributes.repoProjectKey">Source Repo Project</dt>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,8 @@ module.exports = angular
}
this.createApplicationForTests();
};

// Enables setting the attributes as a callback in react components
this.setAttribute = (name, value) => (this.application[name] = value);
},
]);
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ <h3 data-purpose="modal-header">Edit Application</h3>
<pager-duty-select-field component="editApp.applicationAttributes"></pager-duty-select-field>
</render-if-feature>

<render-if-feature feature="slack">
<slack-channel-selector
channel="editApp.applicationAttributes.slackChannel"
callback="editApp.setAttribute"
></slack-channel-selector>
</render-if-feature>

<div class="form-group row">
<div class="col-sm-3 sm-label-right">Description</div>
<div class="col-sm-9">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ <h3 data-purpose="modal-header">New Application</h3>
<pager-duty-select-field component="newAppModal.application"></pager-duty-select-field>
</render-if-feature>

<render-if-feature feature="slack">
<slack-channel-selector
channel="newAppModal.application.slackChannel"
callback="newAppModal.setAttribute"
></slack-channel-selector>
</render-if-feature>

<div class="form-group row">
<div class="col-sm-3 sm-label-right">Description</div>
<div class="col-sm-9">
Expand Down
1 change: 1 addition & 0 deletions app/scripts/modules/core/src/config/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface IFeatures {
pipelineTemplates?: boolean;
quietPeriod?: boolean;
roscoMode?: boolean;
slack?: boolean;
snapshots?: boolean;
travis?: boolean;
versionedProviders?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/modules/core/src/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { REACT_MODULE } from './reactShims';
import { REGION_MODULE } from './region/region.module';
import { SERVERGROUP_MODULE } from './serverGroup/serverGroup.module';
import { SERVER_GROUP_MANAGER_MODULE } from './serverGroupManager/serverGroupManager.module';
import { SLACK_COMPONENT } from './slack';
import { STYLEGUIDE_MODULE } from './styleguide/styleguide.module';
import { SUBNET_MODULE } from './subnet/subnet.module';

Expand Down Expand Up @@ -139,6 +140,7 @@ module(CORE_MODULE, [
require('./securityGroup/securityGroup.module').name,
SERVERGROUP_MODULE,
SERVER_GROUP_MANAGER_MODULE,
SLACK_COMPONENT,
STYLEGUIDE_MODULE,
SUBNET_MODULE,

Expand Down
1 change: 1 addition & 0 deletions app/scripts/modules/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export * from './serverGroup';
export * from './serverGroupManager';
export * from './serviceAccount';
export * from './services';
export * from './slack';
export * from './state';
export * from './storage';
export * from './subnet';
Expand Down
45 changes: 45 additions & 0 deletions app/scripts/modules/core/src/slack/SlackChannelSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';
import * as Select from 'react-select';
import { ReactSelectInput, useLatestPromise } from '@spinnaker/core';

import { ISlackChannel, SlackReader } from './SlackReader';

export interface ISlackChannelSelectorProps {
channel: ISlackChannel;
callback: (name: string, value: any) => void;
}

export interface ISlackChannelSelectorState {
channels: ISlackChannel[];
selected: ISlackChannel;
loading: boolean;
}

export default function SlackChannelSelector({ channel, callback }: ISlackChannelSelectorProps) {
const [selectedChannel, setSelectedChannel] = React.useState(channel);
const fetchChannels = useLatestPromise(() => SlackReader.getChannels(), []);
const channels = fetchChannels.result;
const isLoading = fetchChannels.status === 'PENDING';

const onInputChange = (evt: Select.Option<ISlackChannel>) => {
const newChannel = evt ? evt.target.value : null;
callback('slackChannel', newChannel || {});
setSelectedChannel(newChannel);
};

return (
<div className="form-group row">
<div className="col-sm-3 sm-label-right">Slack Channel</div>
<div className="col-sm-9">
<ReactSelectInput
inputClassName="form-control input-sm"
mode="VIRTUALIZED"
options={(channels || []).map((ch: ISlackChannel) => ({ value: ch, label: ch.name }))}
value={selectedChannel && { value: selectedChannel, label: selectedChannel.name }}
onChange={onInputChange}
isLoading={isLoading}
/>
</div>
</div>
);
}
38 changes: 38 additions & 0 deletions app/scripts/modules/core/src/slack/SlackReader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { API } from '@spinnaker/core';
import { ISlackChannel, SlackReader } from './SlackReader';

const mockSlackChannels: ISlackChannel[] = [
{
id: '0-test',
name: 'testchannel',
is_channel: true,
creator: 'creator',
is_archived: false,
name_normalized: 'testchannel',
num_members: 25,
},
{
id: '1-test',
name: 'fakechannel',
is_channel: true,
creator: 'creator2',
is_archived: false,
name_normalized: 'fakechannel',
num_members: 25,
},
];

describe('SlackReader', () => {
it('should fetch a list of public Slack channels', () => {
spyOn(SlackReader, 'getChannels').and.callThrough();
spyOn(API, 'one')
.and.callThrough()
.and.returnValue({ getList: () => Promise.resolve(mockSlackChannels) });

SlackReader.getChannels().then((channels: ISlackChannel[]) => {
expect(SlackReader.getChannels).toHaveBeenCalled();
expect(channels.length).toEqual(2);
expect(API.one).toHaveBeenCalledWith('slack/channels');
});
});
});
20 changes: 20 additions & 0 deletions app/scripts/modules/core/src/slack/SlackReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IPromise } from 'angular';
import { API } from '@spinnaker/core';

export interface ISlackChannel {
id: string;
name: string;
is_channel: boolean;
creator: string;
is_archived: boolean;
name_normalized: string;
num_members: number;
}

export class SlackReader {
public static getChannels(): IPromise<ISlackChannel[]> {
return API.one('slack/channels')
.getList()
.catch(() => [] as ISlackChannel[]);
}
}
2 changes: 2 additions & 0 deletions app/scripts/modules/core/src/slack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './slackChannelSelector.component';
export * from './SlackReader';
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { module } from 'angular';
import { react2angular } from 'react2angular';

import SlackChannelSelector from './SlackChannelSelector';

export const SLACK_COMPONENT = 'spinnaker.application.slackChannelSelector.component';

module(SLACK_COMPONENT, []).component(
'slackChannelSelector',
react2angular(SlackChannelSelector, ['channel', 'callback']),
);
1 change: 1 addition & 0 deletions halconfig/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ window.spinnakerSettings = {
pipelines: true,
pipelineTemplates: pipelineTemplatesEnabled,
roscoMode: true,
slack: false,
snapshots: false,
travis: travisEnabled,
versionedProviders: true,
Expand Down
1 change: 1 addition & 0 deletions settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ window.spinnakerSettings = {
pipelines: true,
quietPeriod: false,
roscoMode: false,
slack: false,
snapshots: false,
travis: false,
versionedProviders: true,
Expand Down

0 comments on commit bf2ffe2

Please sign in to comment.