Skip to content

Commit

Permalink
Madhvi/data feed webhook custom (#5663)
Browse files Browse the repository at this point in the history
* minio full code without tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* added unit and cypress tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed lint issues

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio integration test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio integration test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio working and reformatted

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* integration tests minio and reformatted code

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* modified unit tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed spell check

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed spell check 1090

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed save

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* changes for custom webhook

Signed-off-by: MadhviA <matroliy@progress.com>

* changes for custom webhook

Signed-off-by: MadhviA <matroliy@progress.com>

* Changes for custom webhook

Signed-off-by: MadhviA <matroliy@progress.com>

* minio full code without tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* added unit and cypress tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed lint issues

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio integration test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio integration test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio test

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* minio working and reformatted

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* integration tests minio and reformatted code

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* modified unit tests

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed spell check

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed spell check 1090

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed save

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* added more tests cases with more coverage improved code

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* fixed casing and html logic removed

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* cypress test

Signed-off-by: MadhviA <matroliy@progress.com>

* Conflicts changes

Signed-off-by: MadhviA <matroliy@progress.com>

* changed cypress test

Signed-off-by: MadhviA <matroliy@progress.com>

* switch case added

Signed-off-by: Vivek Shankar <vshankar@progress.com>

* resolved conflicts

Signed-off-by: MadhviA <matroliy@progress.com>

* Resolving conflicts

Signed-off-by: MadhviA <matroliy@progress.com>

* Resolving conflicts

Signed-off-by: MadhviA <matroliy@progress.com>

* Resolving conflicts

Signed-off-by: MadhviA <matroliy@progress.com>

* resolving conflits

Signed-off-by: MadhviA <matroliy@progress.com>

* generic code for webhooks

Signed-off-by: MadhviA <matroliy@progress.com>

* Changes to resolve PR comments

Signed-off-by: MadhviA <matroliy@progress.com>

* lint changes

Signed-off-by: MadhviA <matroliy@progress.com>

* header changes

Signed-off-by: MadhviA <matroliy@progress.com>

* resolving PR comments

Signed-off-by: MadhviA <matroliy@progress.com>

* lint changes

Signed-off-by: MadhviA <matroliy@progress.com>

* resolving PR comments

Signed-off-by: MadhviA <matroliy@progress.com>

* resolving headers sequence

Signed-off-by: MadhviA <matroliy@progress.com>

* resolving headers sequence in test class

Signed-off-by: MadhviA <matroliy@progress.com>

* adding test cases

Signed-off-by: MadhviA <matroliy@progress.com>

* correcting html changes

Signed-off-by: MadhviA <matroliy@progress.com>

* correcting cypress changes

Signed-off-by: MadhviA <matroliy@progress.com>

* correcting html changes

Signed-off-by: MadhviA <matroliy@progress.com>

* changed username logic

Signed-off-by: MadhviA <matroliy@progress.com>

* lint changes for buildkite

Signed-off-by: MadhviA <matroliy@progress.com>

* changes for buildkite

Signed-off-by: MadhviA <matroliy@progress.com>

Co-authored-by: Vivek Shankar <vshankar@progress.com>
Co-authored-by: MadhviA <matroliy@progress.com>
  • Loading branch information
3 people authored Sep 20, 2021
1 parent 05a4b2e commit 9e3ef9f
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,24 @@ export class DestinationRequests {
return this.testDestinationWithSecretId(destination);
}
}

public testDestinationWithUsernamePassword(url: string,
username: string, password: string): Observable<Object> {
return this.http.post(encodeURI(
this.joinToDataFeedUrl(['destinations', 'test'])),
{ url, 'username_password': {username, password} });
return this.http.post(encodeURI(
this.joinToDataFeedUrl(['destinations', 'test'])),
{ url, 'username_password': {username, password} });
}

public testDestinationWithUsernamePasswordWithHeaders(url: string,
username: string, password: string, headers: string): Observable<Object> {
if (headers !== '') {
return this.http.post(encodeURI(
this.joinToDataFeedUrl(['destinations', 'test'])),
{ url, headers, 'username_password': {username, password} });
} else {
return this.http.post(encodeURI(
this.joinToDataFeedUrl(['destinations', 'test'])),
{ url, 'username_password': {username, password} });
}
}

public testDestinationWithHeaders(url: string,
Expand Down
5 changes: 4 additions & 1 deletion components/automate-ui/src/app/helpers/auth/regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export class Regex {
VALID_IP_ADDRESS: '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',

// Allow valid versions only Eg: 1.2.3
VALID_VERSION: /^\d{1,9}\.\d{1,9}\.\d{1,9}$/
VALID_VERSION: /^\d{1,9}\.\d{1,9}\.\d{1,9}$/,

// Allow valid header input for Custom webhook
VALID_HEADER: /([a-zA-Z]):([a-zA-Z])[^\r\n]/
};

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h2 class="bolder-font">Webhook Integration</h2>
<div class="container image-footer">
<h3 class="bolder-font">{{item.name}}</h3>
</div>
<div class="overlay" *ngIf="item.name !== 'ServiceNow' && item.name !== 'Splunk'">
<div class="overlay" *ngIf="item.name !== 'ServiceNow' && item.name !== 'Splunk' && item.name !== 'Custom'">
<div class="text">Coming Soon</div>
</div>
</div>
Expand Down Expand Up @@ -126,7 +126,7 @@ <h3 class="bolder-font">{{item.name}}</h3>
</div>
</div>
</div>
<div class="input-margin" *ngIf="showTokenInput('tokenType')">
<div class="input-margin" *ngIf="showTokenInput('tokenType')" [ngClass]="{'customtokentypeblock' : showFields.customToken}">
<chef-form-field>
<label>
<span class="label">Token Type (Prefix)<span aria-hidden="true">*</span></span>
Expand All @@ -138,15 +138,19 @@ <h3 class="bolder-font">{{item.name}}</h3>
data-cy="add-token-type"
id="token-type-input"
autocomplete="off"
[readonly]="tokenToggle"/>
<chef-button secondary data-cy="toggle-type" (click)="toggleTokenType()">
[readonly]="tokenToggle && !showFields.customToken"/>
<chef-button secondary data-cy="toggle-type" (click)="toggleTokenType()" *ngIf="!showFields.customToken">
Edit
</chef-button>
</div>
</label>
<chef-error
*ngIf="createForm.get('tokenType').hasError('required') && createForm.get('tokenType').dirty">
token type is required.
</chef-error>
</chef-form-field>
</div>
<div class="input-margin" *ngIf="showTokenInput('token')">
<div class="input-margin" *ngIf="showTokenInput('token')" [ngClass]="{'customtokenblock' : showFields.customToken}">
<chef-form-field>
<label>
<span class="label">Token <span aria-hidden="true">*</span></span>
Expand All @@ -168,7 +172,7 @@ <h3 class="bolder-font">{{item.name}}</h3>
</chef-error>
</chef-form-field>
</div>
<div class="input-margin" *ngIf="showUserPassInput('username')">
<div class="input-margin" *ngIf="showUserPassInput('username')" [ngClass]="{'custompasswordblock' : showFields.customToken}">
<chef-form-field>
<label>
<span class="label">Username <span aria-hidden="true">*</span></span>
Expand All @@ -190,7 +194,7 @@ <h3 class="bolder-font">{{item.name}}</h3>
</chef-error>
</chef-form-field>
</div>
<div class="input-margin" *ngIf="showUserPassInput('password')">
<div class="input-margin" *ngIf="showUserPassInput('password')" [ngClass]="{'custompasswordblock' : showFields.customToken}">
<chef-form-field>
<label>
<span class="label">Password <span aria-hidden="true">*</span></span>
Expand Down Expand Up @@ -230,6 +234,30 @@ <h3 class="bolder-font">{{item.name}}</h3>
</chef-error>
</chef-form-field>
</div>
<div *ngIf="showFields.useHeaders" class="input-margin" [ngClass]="{'error' : !flagHeaders}">
<chef-form-field>
<label>
<chef-checkbox id="headers-checkbox" (change)="updateHeaderCheckbox($event.detail)" [checked]="headerChecked"> Use Headers?</chef-checkbox>
<div *ngIf="headerChecked">
<textarea
class="chef-inputtextarea"
formControlName="headers"
data-cy="add-headers"
type="text"
id="headers-input"
placeholder="key:value"
autocomplete="off"
(input)="validateHeaders($event.target.value)"
rows="5"
></textarea>
</div>
</label>
<chef-error
*ngIf="!flagHeaders && headerChecked">
Headers are invalid.
</chef-error>
</chef-form-field>
</div>
<div class="input-margin" *ngIf="showFields.accessKey">
<chef-form-field>
<label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ main {
}

.input-margin {
display: flex;
display: inline-block;
margin: 15px 0;
vertical-align: top;
width: 70%;
flex-direction: row-reverse;

Expand Down Expand Up @@ -154,6 +155,42 @@ main {
}
}
}

.customtokenblock {
width: 40%;
}

.customtokentypeblock {
width: calc(30% - 16px);
margin-right: 16px;
}

.custompasswordblock {
width: 34%;
margin-right: 16px;
}
}
}

.chef-inputtextarea {
width: 100%;
height: auto;
background-color: #FFFFFF;
padding: 1em;
font-size: 14px;
border: 1px solid #E6EBEE;
border-radius: 4px;
box-sizing: border-box;
font-family: inherit;
transition: border 0.4s ease;
}

.error {
.chef-inputtextarea {
border-color: #DC267F;
border-radius: 4px 4px 0 0;
outline: none;
margin-bottom: -7px;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('DataFeedCreateComponent', () => {
const secretKey = 'test123';
const userName = 'test123';
const password = 'test123';
const header = '{“test”:”123”}';
const destination = <Destination> {
id: '1',
name: 'new data feed',
Expand Down Expand Up @@ -150,6 +151,30 @@ describe('DataFeedCreateComponent', () => {
expect(component.validateForm()).toBeTruthy();
});

it('should be valid when all fields are filled out for custom with Access token', () => {
component.name = jasmine.createSpyObj('name', ['nativeElement']);
component.name.nativeElement = { focus: () => { }};
component.selectIntegration('Custom');
component.createForm.controls['name'].setValue(destination.name);
component.createForm.controls['url'].setValue(destination.url);
component.createForm.controls['tokenType'].setValue(tokenType);
component.createForm.controls['token'].setValue(token);
expect(component.validateForm()).toBeTruthy();
});

it('should be valid when all fields are filled out for custom with UsernamePassword', () => {
component.name = jasmine.createSpyObj('name', ['nativeElement']);
component.name.nativeElement = { focus: () => { }};
component.selectIntegration('Custom');
component.selectChangeHandlers('Username and Password');
component.createForm.controls['name'].setValue(destination.name);
component.createForm.controls['url'].setValue(destination.url);
component.createForm.controls['username'].setValue(userName);
component.createForm.controls['password'].setValue(password);
component.createForm.controls['headers'].setValue(header);
expect(component.validateForm()).toBeTruthy();
});

it('slider resets name, url, username and password to empty string for servicenow', () => {
component.createForm.controls['name'].setValue('any');
component.createForm.controls['url'].setValue('any');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Revision } from 'app/entities/revisions/revision.model';
import { Regex } from 'app/helpers/auth/regex';

export enum WebhookIntegrationTypes {
SERVICENOW = 'ServiceNow',
Expand Down Expand Up @@ -43,6 +44,7 @@ export class DataFeedCreateComponent {
@Input() createForm: FormGroup;
@Output() saveDestinationEvent = new EventEmitter<any>();
@Output() testDestinationEvent = new EventEmitter<any>();
@Output() checkEvent = new EventEmitter<any>();

public revisions: Revision[] = [];
public revisionsListLoading = true;
Expand All @@ -56,11 +58,15 @@ export class DataFeedCreateComponent {
public hideNotification = true;
public authSelected: string = AuthTypes.ACCESSTOKEN;
public showSelect = false;
public flagHeaders = true;
public validHeadersValue = false;

private saveInProgress = false;
private testInProgress = false;
public headerChecked = false;
public notificationShow = false;
public notificationMessage = '';
public notificationType = 'error';
private saveInProgress = false;
private testInProgress = false;

public integrations = {
webhook: [
Expand Down Expand Up @@ -88,7 +94,9 @@ export class DataFeedCreateComponent {
headers: false,
bucketName: false,
accessKey: false,
secretKey: false
secretKey: false,
useHeaders: false,
customToken: false
};

set saveDone(done: boolean) {
Expand Down Expand Up @@ -166,6 +174,12 @@ export class DataFeedCreateComponent {
}};
}

updateHeaderCheckbox(event: boolean): void {
this.checkEvent.emit(event);
this.headerChecked = event;
this.createForm.controls.headerChecked.setValue(true);
}

showFieldStorage() {
Object.keys(this.showFields).forEach(v => this.showFields[v] = false);
this.showFields = {...this.showFields, ...{
Expand All @@ -183,6 +197,8 @@ export class DataFeedCreateComponent {
this.showFields.headers = false;
this.integTitle = integration;
this.createForm.reset();
this.headerChecked = false;
this.flagHeaders = true;
setTimeout(() => {
this.showSelect = true;
this.name.nativeElement.focus();
Expand All @@ -203,6 +219,15 @@ export class DataFeedCreateComponent {
this.integrationSelected = true;
break;
}
case WebhookIntegrationTypes.CUSTOM: {
this.showFieldWebhook();
this.showFields.customToken = true;
this.showFields.useHeaders = true;
this.authSelected = AuthTypes.ACCESSTOKEN;
this.createForm.controls['tokenType'].setValue('');
this.integrationSelected = true;
break;
}
case StorageIntegrationTypes.MINIO: {
this.showFieldStorage();
this.showFields.region = false;
Expand Down Expand Up @@ -236,31 +261,43 @@ export class DataFeedCreateComponent {
switch (this.integTitle) {
case WebhookIntegrationTypes.SERVICENOW:
case WebhookIntegrationTypes.SPLUNK:
case WebhookIntegrationTypes.ELK_KIBANA: {
case WebhookIntegrationTypes.ELK_KIBANA:
case WebhookIntegrationTypes.CUSTOM: {
// handling access token and user pass auth
// for servicenow, splunk and elk
// for servicenow, splunk, elk and custom
switch (this.authSelected) {
case AuthTypes.ACCESSTOKEN: {
if (this.createForm.get('name').valid && this.createForm.get('url').valid &&
this.createForm.get('tokenType').valid && this.createForm.get('token').valid) {
return true;
if (this.integTitle === WebhookIntegrationTypes.CUSTOM && this.headerChecked &&
this.validHeadersValue && this.flagHeaders) {
return true;
} else if (this.integTitle === WebhookIntegrationTypes.CUSTOM &&
!this.headerChecked && this.flagHeaders) {
return true;
} else if (this.integTitle !== WebhookIntegrationTypes.CUSTOM) {
return true;
}
}
break;
}
case AuthTypes.USERNAMEANDPASSWORD: {
if (this.createForm.get('name').valid && this.createForm.get('url').valid &&
this.createForm.get('username').valid && this.createForm.get('password').valid) {
return true;
if (this.integTitle === WebhookIntegrationTypes.CUSTOM && this.headerChecked &&
this.validHeadersValue && this.flagHeaders) {
return true;
} else if (this.integTitle === WebhookIntegrationTypes.CUSTOM &&
!this.headerChecked && this.flagHeaders) {
return true;
} else if (this.integTitle !== WebhookIntegrationTypes.CUSTOM) {
return true;
}
}
}
}
break;
}
case WebhookIntegrationTypes.CUSTOM: {
// handling access token and user pass auth
// with headers for webhooks
break;
}
case StorageIntegrationTypes.MINIO: {
// handling minio
if (this.createForm.get('name').valid && this.createForm.get('endpoint').valid &&
Expand Down Expand Up @@ -296,5 +333,19 @@ export class DataFeedCreateComponent {
public showUserPassInput(field: string) {
return this.showFields[field] && this.authSelected === AuthTypes.USERNAMEANDPASSWORD;
}
}

public validateHeaders(customHeaders: string): void {
const headersVal = customHeaders.split('\n');
for (const values in headersVal) {
if (this.headerChecked && headersVal[values]) {
this.flagHeaders = Regex.patterns.VALID_HEADER.test(headersVal[values]);
this.validHeadersValue = this.flagHeaders;
if (this.flagHeaders === false) {
break;
}
} else if (headersVal[values] === '') {
this.flagHeaders = true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
<app-data-feed-create
[createForm]="createDataFeedForm"
(saveDestinationEvent)="saveDestination($event)"
(testDestinationEvent)="sendTestForDataFeed($event)">
(testDestinationEvent)="sendTestForDataFeed($event)"
(checkEvent)="setCheck($event)">
</app-data-feed-create>
</main>
</div>
Expand Down
Loading

0 comments on commit 9e3ef9f

Please sign in to comment.