mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-30 06:18:02 +02:00
Merge pull request #11027 from AllForNothing/webhook
Improve Webhook UI
This commit is contained in:
commit
aa73f16a20
@ -63,6 +63,7 @@ import {
|
|||||||
import { RepositoryDefaultService, RepositoryService } from "./repository/repository.service";
|
import { RepositoryDefaultService, RepositoryService } from "./repository/repository.service";
|
||||||
import { ArtifactDefaultService, ArtifactService } from "./repository/artifact/artifact.service";
|
import { ArtifactDefaultService, ArtifactService } from "./repository/artifact/artifact.service";
|
||||||
import { GridViewComponent } from "./repository/gridview/grid-view.component";
|
import { GridViewComponent } from "./repository/gridview/grid-view.component";
|
||||||
|
import { LastTriggerComponent } from "./webhook/last-trigger/last-trigger.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -110,6 +111,7 @@ import { GridViewComponent } from "./repository/gridview/grid-view.component";
|
|||||||
ValuesComponent,
|
ValuesComponent,
|
||||||
ArtifactVulnerabilitiesComponent,
|
ArtifactVulnerabilitiesComponent,
|
||||||
GridViewComponent,
|
GridViewComponent,
|
||||||
|
LastTriggerComponent
|
||||||
],
|
],
|
||||||
exports: [ProjectComponent, ListProjectComponent],
|
exports: [ProjectComponent, ListProjectComponent],
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="scanner">
|
<ng-container *ngIf="scanner">
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control">
|
||||||
<label for="select-full" class="clr-control-label name">{{'SCANNER.NAME' | translate}}</label>
|
<label class="clr-control-label name">{{'SCANNER.NAME' | translate}}</label>
|
||||||
<div class="clr-control-container">
|
<div class="clr-control-container">
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper">
|
||||||
<div class="clr-input-wrapper name">
|
<div class="clr-input-wrapper name">
|
||||||
|
@ -2,13 +2,57 @@
|
|||||||
<inline-alert class="modal-title"></inline-alert>
|
<inline-alert class="modal-title"></inline-alert>
|
||||||
<form #webhookForm="ngForm" class="clr-form clr-form-horizontal">
|
<form #webhookForm="ngForm" class="clr-form clr-form-horizontal">
|
||||||
<section class="form-block webhook-section">
|
<section class="form-block webhook-section">
|
||||||
|
<!-- name -->
|
||||||
|
<div class="clr-form-control">
|
||||||
|
<label for="edit_endpoint_url" class="clr-control-label required">{{'WEBHOOK.NAME' | translate}}</label>
|
||||||
|
<div class="clr-control-container" [class.clr-error]="name.errors && name.errors.required && (name.dirty || name.touched)">
|
||||||
|
<div class="clr-input-wrapper">
|
||||||
|
<input autocomplete="off" class="clr-input" type="text" id="name" [disabled]="checking" [(ngModel)]="webhook.name"
|
||||||
|
size="30" name="notify-type" #name="ngModel" required>
|
||||||
|
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<clr-control-error *ngIf="name.errors && name.errors.required && (name.dirty || name.touched)" class="tooltip-content">
|
||||||
|
{{'WEBHOOK.NAME_REQUIRED' | translate}}
|
||||||
|
</clr-control-error>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- description -->
|
||||||
|
<div class="clr-form-control">
|
||||||
|
<label for="edit_endpoint_url" class="clr-control-label">{{'WEBHOOK.DESCRIPTION' | translate}}</label>
|
||||||
|
<div class="clr-control-container">
|
||||||
|
<textarea autocomplete="off" class="clr-textarea width-238" type="text" id="description" [disabled]="checking" [(ngModel)]="webhook.description"
|
||||||
|
name="description"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- notify type -->
|
||||||
|
<clr-select-container>
|
||||||
|
<label class="required">{{'WEBHOOK.NOTIFY_TYPE' | translate}}</label>
|
||||||
|
<select clrSelect name="notifyType" id="notify_type" [(ngModel)]="webhook.targets[0].type" [disabled]="checking">
|
||||||
|
<option *ngFor="let type of metadata?.notify_type" value="{{type}}">{{type}}</option>
|
||||||
|
</select>
|
||||||
|
</clr-select-container>
|
||||||
|
|
||||||
|
<div class="clr-form-control">
|
||||||
|
<label class="clr-control-label required">{{'WEBHOOK.EVENT_TYPE' | translate}}</label>
|
||||||
|
<div class="clr-control-container clr-control-inline" [class.clr-error]="!hasEventType()">
|
||||||
|
<div class="clr-checkbox-wrapper" *ngFor="let item of metadata?.event_type">
|
||||||
|
<input type="checkbox" id="{{item}}" name="eventTypes" value="{{item}}" class="clr-checkbox" (change)="setEventType(item)" [checked]="getEventType(item)">
|
||||||
|
<label for="{{item}}" class="clr-control-label">{{item}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="clr-subtext-wrapper" *ngIf="!hasEventType()">
|
||||||
|
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||||
|
<span class="clr-subtext">{{'WEBHOOK.EVENT_TYPE_REQUIRED' | translate}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- endpoint URL -->
|
<!-- endpoint URL -->
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control">
|
||||||
<label for="edit_endpoint_url" class="clr-control-label required">{{'WEBHOOK.ENDPOINT_URL' | translate}}</label>
|
<label for="edit_endpoint_url" class="clr-control-label required">{{'WEBHOOK.ENDPOINT_URL' | translate}}</label>
|
||||||
<div class="clr-control-container" [class.clr-error]="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)">
|
<div class="clr-control-container" [class.clr-error]="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)">
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper">
|
||||||
<input class="clr-input" type="text" id="edit_endpoint_url" [disabled]="checking" [(ngModel)]="webhookTarget.address"
|
<input autocomplete="off" class="clr-input" type="text" id="edit_endpoint_url" [disabled]="checking" [(ngModel)]="webhook.targets[0].address"
|
||||||
size="30" name="edit_endpoint_url" #enpointURL="ngModel" required placeholder="http(s)://192.168.1.1">
|
size="30" name="edit_endpoint_url" #enpointURL="ngModel" required [placeholder]="webhook.targets[0].type ==='http'?'http(s)://192.168.1.1':''">
|
||||||
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
<clr-icon class="clr-validate-icon" shape="exclamation-circle"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<clr-control-error *ngIf="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)" class="tooltip-content">
|
<clr-control-error *ngIf="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)" class="tooltip-content">
|
||||||
@ -17,16 +61,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- auth_header -->
|
<!-- auth_header -->
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control" *ngIf="webhook?.targets[0]?.type ==='http'">
|
||||||
<label for="auth_header" class="clr-control-label">{{ 'WEBHOOK.AUTH_HEADER' |
|
<label for="auth_header" class="clr-control-label">{{ 'WEBHOOK.AUTH_HEADER' |
|
||||||
translate }}</label>
|
translate }}</label>
|
||||||
<div class="clr-control-container">
|
<div class="clr-control-container">
|
||||||
<div class="clr-input-wrapper">
|
<div class="clr-input-wrapper">
|
||||||
<input class="clr-input" type="text" id="auth_header" [disabled]="checking"
|
<input autocomplete="off" class="clr-input" type="text" id="auth_header" [disabled]="checking"
|
||||||
[(ngModel)]="webhookTarget.auth_header" size="30" name="auth_header">
|
[(ngModel)]="webhook.targets[0].auth_header" size="30" name="auth_header">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- verify remote cert -->
|
<!-- verify remote cert -->
|
||||||
<div class="clr-form-control">
|
<div class="clr-form-control">
|
||||||
<label for="verify_remote_cert" class="clr-control-label">
|
<label for="verify_remote_cert" class="clr-control-label">
|
||||||
@ -40,17 +86,18 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="clr-control-container padding-top-3">
|
<div class="clr-control-container padding-top-3">
|
||||||
<input type="checkbox" [disabled]="checking" clrCheckbox name="verify_remote_cert" id="verify_remote_cert"
|
<input type="checkbox" [disabled]="checking" clrCheckbox name="verify_remote_cert" id="verify_remote_cert"
|
||||||
(ngModelChange)="setCertValue($event)" [ngModel]="!webhookTarget.skip_cert_verify"/> </div>
|
(ngModelChange)="setCertValue($event)" [ngModel]="!webhook?.targets[0]?.skip_cert_verify"/> </div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
<div class="mt-1" *ngIf="!isModify">
|
<div class="mt-1" *ngIf="!isModify">
|
||||||
<button type="button" id="new-webhook-continue" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.CONTINUE' | translate}}</button>
|
<button type="button" id="webhook-test-add" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
||||||
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
<button type="button" id="new-webhook-continue" class="btn btn-primary" [disabled]="!isValid" (click)="add()">{{'BUTTON.ADD' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-outline" id="add-webhook-cancel" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 bottom-btn" *ngIf="isModify">
|
<div class="mt-1 bottom-btn" *ngIf="isModify">
|
||||||
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" id="webhook-test" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" id="webhook-test" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="edit-webhook-save" [disabled]="!isValid" (click)="save()">{{'BUTTON.SAVE' | translate}}</button>
|
||||||
<button type="button" class="btn btn-outline" id="edit-webhook-cancel" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
<button type="button" class="btn btn-outline" id="edit-webhook-cancel" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||||
<button type="button" class="btn btn-primary" id="edit-webhook-save" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.SAVE' | translate}}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -7,4 +7,7 @@
|
|||||||
.bottom-btn {
|
.bottom-btn {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: 3.4rem;
|
margin-right: 3.4rem;
|
||||||
|
}
|
||||||
|
.width-238 {
|
||||||
|
width: 238px;
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { AddWebhookFormComponent } from './add-webhook-form.component';
|
import { AddWebhookFormComponent } from './add-webhook-form.component';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
@ -10,6 +9,8 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { WebhookService } from "../webhook.service";
|
import { WebhookService } from "../webhook.service";
|
||||||
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
|
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
import { Webhook } from "../webhook";
|
||||||
|
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
|
||||||
|
|
||||||
describe('AddWebhookFormComponent', () => {
|
describe('AddWebhookFormComponent', () => {
|
||||||
let component: AddWebhookFormComponent;
|
let component: AddWebhookFormComponent;
|
||||||
@ -17,17 +18,60 @@ describe('AddWebhookFormComponent', () => {
|
|||||||
const mockWebhookService = {
|
const mockWebhookService = {
|
||||||
getCurrentUser: () => {
|
getCurrentUser: () => {
|
||||||
return of(null);
|
return of(null);
|
||||||
|
},
|
||||||
|
createWebhook() {
|
||||||
|
return of(null);
|
||||||
|
},
|
||||||
|
editWebhook() {
|
||||||
|
return of(null);
|
||||||
|
},
|
||||||
|
testEndpoint() {
|
||||||
|
return of(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const mockMessageHandlerService = {
|
const mockMessageHandlerService = {
|
||||||
handleError: () => { }
|
handleError: () => { }
|
||||||
};
|
};
|
||||||
|
const mockedWehook: Webhook = {
|
||||||
|
id: 1,
|
||||||
|
project_id: 1,
|
||||||
|
name: 'test',
|
||||||
|
description: 'just a test webhook',
|
||||||
|
targets: [{
|
||||||
|
address: 'https://test.com',
|
||||||
|
type: 'http',
|
||||||
|
attachment: null,
|
||||||
|
auth_header: null,
|
||||||
|
skip_cert_verify: true,
|
||||||
|
}],
|
||||||
|
event_types: [
|
||||||
|
'projectQuota'
|
||||||
|
],
|
||||||
|
creator: null,
|
||||||
|
creation_time: null,
|
||||||
|
update_time: null,
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
const mockedMetadata = {
|
||||||
|
"event_type": [
|
||||||
|
"projectQuota",
|
||||||
|
"pullImage",
|
||||||
|
"scanningFailed",
|
||||||
|
"uploadChart",
|
||||||
|
"deleteChart",
|
||||||
|
"downloadChart",
|
||||||
|
"scanningCompleted",
|
||||||
|
"pushImage",
|
||||||
|
"deleteImage"
|
||||||
|
],
|
||||||
|
"notify_type": [
|
||||||
|
"http",
|
||||||
|
"slack"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
schemas: [
|
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
|
||||||
],
|
|
||||||
imports: [
|
imports: [
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
ClarityModule,
|
ClarityModule,
|
||||||
@ -37,7 +81,9 @@ describe('AddWebhookFormComponent', () => {
|
|||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule
|
||||||
],
|
],
|
||||||
declarations: [AddWebhookFormComponent],
|
declarations: [AddWebhookFormComponent,
|
||||||
|
InlineAlertComponent,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TranslateService,
|
TranslateService,
|
||||||
{ provide: WebhookService, useValue: mockWebhookService },
|
{ provide: WebhookService, useValue: mockWebhookService },
|
||||||
@ -52,10 +98,51 @@ describe('AddWebhookFormComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AddWebhookFormComponent);
|
fixture = TestBed.createComponent(AddWebhookFormComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
component.metadata = mockedMetadata;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', async () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
|
const cancelButtonForAdd: HTMLButtonElement = fixture.nativeElement.querySelector("#add-webhook-cancel");
|
||||||
|
expect(cancelButtonForAdd).toBeTruthy();
|
||||||
|
component.isModify = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const cancelButtonForEdit: HTMLButtonElement = fixture.nativeElement.querySelector("#edit-webhook-cancel");
|
||||||
|
expect(cancelButtonForEdit).toBeTruthy();
|
||||||
|
});
|
||||||
|
it("should occur a 'name is required' error", async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.autoDetectChanges(true);
|
||||||
|
const nameInput: HTMLInputElement = fixture.nativeElement.querySelector("#name");
|
||||||
|
nameInput.value = "test";
|
||||||
|
nameInput.dispatchEvent(new Event('input'));
|
||||||
|
nameInput.value = null;
|
||||||
|
nameInput.dispatchEvent(new Event('input'));
|
||||||
|
nameInput.blur();
|
||||||
|
nameInput.dispatchEvent(new Event('blur'));
|
||||||
|
const errorEle: HTMLElement = fixture.nativeElement.querySelector("clr-control-error");
|
||||||
|
expect(errorEle.innerText).toEqual('WEBHOOK.NAME_REQUIRED');
|
||||||
|
});
|
||||||
|
it("test button should work", async () => {
|
||||||
|
const spy: jasmine.Spy = spyOn(component, 'onTestEndpoint').and.returnValue(undefined);
|
||||||
|
const testButton: HTMLButtonElement = fixture.nativeElement.querySelector("#webhook-test-add");
|
||||||
|
testButton.dispatchEvent(new Event('click'));
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(spy.calls.count()).toEqual(1);
|
||||||
|
});
|
||||||
|
it("add button should work", async () => {
|
||||||
|
const spy: jasmine.Spy = spyOn(component, 'add').and.returnValue(undefined);
|
||||||
|
component.webhook = mockedWehook;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(component.isValid).toBeTruthy();
|
||||||
|
const addButton: HTMLButtonElement = fixture.nativeElement.querySelector("#new-webhook-continue");
|
||||||
|
addButton.dispatchEvent(new Event('click'));
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
expect(spy.calls.count()).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,89 +1,62 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
OnInit,
|
OnInit,
|
||||||
OnChanges,
|
|
||||||
Input,
|
Input,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
Output,
|
Output,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
SimpleChanges
|
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { Webhook, Target } from "../webhook";
|
import { Webhook } from "../webhook";
|
||||||
import { NgForm } from "@angular/forms";
|
import { NgForm } from "@angular/forms";
|
||||||
import { ClrLoadingState } from "@clr/angular";
|
import { ClrLoadingState } from "@clr/angular";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { WebhookService } from "../webhook.service";
|
import { WebhookService } from "../webhook.service";
|
||||||
import { WebhookEventTypes } from '../../../shared/shared.const';
|
|
||||||
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
|
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
|
||||||
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
|
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'add-webhook-form',
|
selector: 'add-webhook-form',
|
||||||
templateUrl: './add-webhook-form.component.html',
|
templateUrl: './add-webhook-form.component.html',
|
||||||
styleUrls: ['./add-webhook-form.component.scss']
|
styleUrls: ['./add-webhook-form.component.scss']
|
||||||
})
|
})
|
||||||
export class AddWebhookFormComponent implements OnInit, OnChanges {
|
export class AddWebhookFormComponent implements OnInit {
|
||||||
closable: boolean = true;
|
closable: boolean = true;
|
||||||
staticBackdrop: boolean = true;
|
staticBackdrop: boolean = true;
|
||||||
checking: boolean = false;
|
checking: boolean = false;
|
||||||
checkBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
checkBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
webhookForm: NgForm;
|
webhookForm: NgForm;
|
||||||
submitting: boolean = false;
|
submitting: boolean = false;
|
||||||
webhookTarget: Target = new Target();
|
|
||||||
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() webhook: Webhook;
|
webhook: Webhook = new Webhook();
|
||||||
@Input() isModify: boolean;
|
isModify: boolean;
|
||||||
@Input() isOpen: boolean;
|
@Input() isOpen: boolean;
|
||||||
@Output() edit = new EventEmitter<boolean>();
|
|
||||||
@Output() close = new EventEmitter<boolean>();
|
@Output() close = new EventEmitter<boolean>();
|
||||||
@ViewChild("webhookForm", { static: true }) currentForm: NgForm;
|
@ViewChild("webhookForm", { static: true }) currentForm: NgForm;
|
||||||
@ViewChild(InlineAlertComponent, { static: false }) inlineAlert: InlineAlertComponent;
|
@ViewChild(InlineAlertComponent, { static: false }) inlineAlert: InlineAlertComponent;
|
||||||
|
@Input()
|
||||||
|
metadata: any;
|
||||||
|
@Output() notify = new EventEmitter<Webhook>();
|
||||||
constructor(
|
constructor(
|
||||||
private webhookService: WebhookService,
|
private webhookService: WebhookService,
|
||||||
private messageHandlerService: MessageHandlerService,
|
|
||||||
private translate: TranslateService
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
|
||||||
if (changes['isOpen'] && changes['isOpen'].currentValue) {
|
|
||||||
Object.assign(this.webhookTarget, this.webhook.targets[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onTestEndpoint() {
|
onTestEndpoint() {
|
||||||
this.checkBtnState = ClrLoadingState.LOADING;
|
this.checkBtnState = ClrLoadingState.LOADING;
|
||||||
this.checking = true;
|
this.checking = true;
|
||||||
|
|
||||||
this.webhookService
|
this.webhookService
|
||||||
.testEndpoint(this.projectId, {
|
.testEndpoint(this.projectId, {
|
||||||
targets: [this.webhookTarget]
|
targets: this.webhook.targets
|
||||||
})
|
})
|
||||||
.pipe(finalize(() => (this.checking = false)))
|
.pipe(finalize(() => (this.checking = false)))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
if (this.isModify) {
|
this.inlineAlert.showInlineSuccess({message: "WEBHOOK.TEST_ENDPOINT_SUCCESS"});
|
||||||
this.inlineAlert.showInlineSuccess({
|
|
||||||
message: "WEBHOOK.TEST_ENDPOINT_SUCCESS"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.translate.get("WEBHOOK.TEST_ENDPOINT_SUCCESS").subscribe((res: string) => {
|
|
||||||
this.messageHandlerService.info(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.checkBtnState = ClrLoadingState.SUCCESS;
|
this.checkBtnState = ClrLoadingState.SUCCESS;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
if (this.isModify) {
|
this.inlineAlert.showInlineError("WEBHOOK.TEST_ENDPOINT_FAILURE");
|
||||||
this.inlineAlert.showInlineError("WEBHOOK.TEST_ENDPOINT_FAILURE");
|
|
||||||
} else {
|
|
||||||
this.messageHandlerService.handleError(error);
|
|
||||||
}
|
|
||||||
this.checkBtnState = ClrLoadingState.DEFAULT;
|
this.checkBtnState = ClrLoadingState.DEFAULT;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -95,30 +68,38 @@ export class AddWebhookFormComponent implements OnInit, OnChanges {
|
|||||||
this.inlineAlert.close();
|
this.inlineAlert.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
add() {
|
||||||
const rx = this.isModify
|
this.submitting = true;
|
||||||
? this.webhookService.editWebhook(this.projectId, this.webhook.id, Object.assign(this.webhook, { targets: [this.webhookTarget] }))
|
this.webhookService.createWebhook(this.projectId, this.webhook)
|
||||||
: this.webhookService.createWebhook(this.projectId, {
|
.pipe(finalize(() => (this.submitting = false)))
|
||||||
targets: [this.webhookTarget],
|
|
||||||
event_types: Object.keys(WebhookEventTypes).map(key => WebhookEventTypes[key]),
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
rx.pipe(finalize(() => (this.submitting = false)))
|
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.edit.emit(this.isModify);
|
this.notify.emit();
|
||||||
this.inlineAlert.close();
|
this.inlineAlert.close();
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.isModify
|
this.inlineAlert.showInlineError(error);
|
||||||
? this.inlineAlert.showInlineError(error)
|
}
|
||||||
: this.messageHandlerService.handleError(error);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.submitting = true;
|
||||||
|
this.webhookService.editWebhook(this.projectId, this.webhook.id, this.webhook)
|
||||||
|
.pipe(finalize(() => (this.submitting = false)))
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
this.inlineAlert.close();
|
||||||
|
this.notify.emit();
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.inlineAlert.showInlineError(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCertValue($event: any): void {
|
setCertValue($event: any): void {
|
||||||
this.webhookTarget.skip_cert_verify = !$event;
|
this.webhook.targets[0].skip_cert_verify = !$event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isValid(): boolean {
|
public get isValid(): boolean {
|
||||||
@ -126,7 +107,26 @@ export class AddWebhookFormComponent implements OnInit, OnChanges {
|
|||||||
this.currentForm &&
|
this.currentForm &&
|
||||||
this.currentForm.valid &&
|
this.currentForm.valid &&
|
||||||
!this.submitting &&
|
!this.submitting &&
|
||||||
!this.checking
|
!this.checking &&
|
||||||
|
this.hasEventType()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEventType(eventType) {
|
||||||
|
if (this.webhook.event_types.indexOf(eventType) === -1) {
|
||||||
|
this.webhook.event_types.push(eventType);
|
||||||
|
} else {
|
||||||
|
this.webhook.event_types.splice(this.webhook.event_types.findIndex(item => item === eventType), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getEventType(eventType): boolean {
|
||||||
|
return eventType && this.webhook.event_types.indexOf(eventType) !== -1;
|
||||||
|
}
|
||||||
|
hasEventType(): boolean {
|
||||||
|
return this.metadata
|
||||||
|
&& this.metadata.event_type
|
||||||
|
&& this.metadata.event_type.length > 0
|
||||||
|
&& this.webhook.event_types
|
||||||
|
&& this.webhook.event_types.length > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
<clr-modal [(clrModalOpen)]="isOpen" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
<clr-modal [(clrModalOpen)]="isOpen" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||||
<h3 class="modal-title">{{'WEBHOOK.EDIT_WEBHOOK' | translate}}</h3>
|
<h3 *ngIf="isEdit" class="modal-title">{{'WEBHOOK.EDIT_WEBHOOK' | translate}}</h3>
|
||||||
|
<h3 *ngIf="!isEdit" class="modal-title">{{'WEBHOOK.ADD_WEBHOOK' | translate}}</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>{{'WEBHOOK.EDIT_WEBHOOK_DESC' | translate}}</div>
|
<div>{{'WEBHOOK.EDIT_WEBHOOK_DESC' | translate}}</div>
|
||||||
<add-webhook-form [projectId]="projectId"
|
<add-webhook-form [metadata]="metadata" [projectId]="projectId"
|
||||||
[isModify]="true"
|
[isOpen]="isOpen"
|
||||||
[isOpen]="isOpen"
|
(close)="closeModal()"
|
||||||
[webhook]="webhook"
|
(notify)="notifySuccess()"
|
||||||
(edit)="closeModal($event)"
|
|
||||||
(close)="closeModal($event)"
|
|
||||||
></add-webhook-form>
|
></add-webhook-form>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
@ -1,7 +1,7 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { AddWebhookComponent } from './add-webhook.component';
|
import { AddWebhookComponent } from './add-webhook.component';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ClarityModule } from '@clr/angular';
|
import { ClarityModule } from '@clr/angular';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
@ -14,7 +14,8 @@ describe('AddWebhookComponent', () => {
|
|||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
schemas: [
|
schemas: [
|
||||||
CUSTOM_ELEMENTS_SCHEMA
|
CUSTOM_ELEMENTS_SCHEMA,
|
||||||
|
NO_ERRORS_SCHEMA
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
@ -42,4 +43,14 @@ describe('AddWebhookComponent', () => {
|
|||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
it('should open modal and should be edit model', async () => {
|
||||||
|
component.isEdit = true;
|
||||||
|
component.isOpen = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
|
||||||
|
expect(body).toBeTruthy();
|
||||||
|
const title: HTMLElement = fixture.nativeElement.querySelector(".modal-title");
|
||||||
|
expect(title.innerText).toEqual('WEBHOOK.EDIT_WEBHOOK');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -15,16 +15,18 @@ import { AddWebhookFormComponent } from "../add-webhook-form/add-webhook-form.co
|
|||||||
styleUrls: ['./add-webhook.component.scss']
|
styleUrls: ['./add-webhook.component.scss']
|
||||||
})
|
})
|
||||||
export class AddWebhookComponent implements OnInit {
|
export class AddWebhookComponent implements OnInit {
|
||||||
|
isEdit: boolean;
|
||||||
isOpen: boolean = false;
|
isOpen: boolean = false;
|
||||||
closable: boolean = false;
|
closable: boolean = false;
|
||||||
staticBackdrop: boolean = true;
|
staticBackdrop: boolean = true;
|
||||||
|
|
||||||
@Input() projectId: number;
|
@Input() projectId: number;
|
||||||
@Input() webhook: Webhook;
|
webhook: Webhook;
|
||||||
@Output() modify = new EventEmitter<boolean>();
|
@Input()
|
||||||
|
metadata: any;
|
||||||
@ViewChild(AddWebhookFormComponent, { static: false })
|
@ViewChild(AddWebhookFormComponent, { static: false })
|
||||||
addWebhookFormComponent: AddWebhookFormComponent;
|
addWebhookFormComponent: AddWebhookFormComponent;
|
||||||
|
@Output() notify = new EventEmitter<Webhook>();
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
@ -38,12 +40,11 @@ export class AddWebhookComponent implements OnInit {
|
|||||||
onCancel() {
|
onCancel() {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
}
|
}
|
||||||
|
notifySuccess() {
|
||||||
closeModal(isModified: boolean): void {
|
this.isOpen = false;
|
||||||
if (isModified) {
|
this.notify.emit();
|
||||||
this.modify.emit(true);
|
}
|
||||||
}
|
closeModal() {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<h4>{{'WEBHOOK.LAST_TRIGGER' | translate}}</h4>
|
||||||
|
<clr-datagrid>
|
||||||
|
<clr-dg-column [clrDgField]="'event_type'">{{'WEBHOOK.TYPE' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'WEBHOOK.STATUS' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'WEBHOOK.LAST_TRIGGERED' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *clrDgItems="let item of lastTriggers">
|
||||||
|
<clr-dg-cell>{{item.event_type}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell [ngSwitch]="item.enabled">
|
||||||
|
<div *ngSwitchCase="true" class="icon-wrap">
|
||||||
|
<clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
|
||||||
|
<span>{{'WEBHOOK.ENABLED' | translate}}</span>
|
||||||
|
</div>
|
||||||
|
<div *ngSwitchCase="false" class="icon-wrap">
|
||||||
|
<clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
|
||||||
|
<span>{{'WEBHOOK.DISABLED' | translate}}</span>
|
||||||
|
</div>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{item.last_trigger_time | date: 'short'}}</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
{{'WEBHOOK.NO_TRIGGER' | translate}}
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<span *ngIf="lastTriggers.length">1 - {{lastTriggers.length}} {{'WEBHOOK.OF' | translate}} </span> {{lastTriggers.length}} {{'WEBHOOK.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination [clrDgPageSize]="10"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
|
import { ClarityModule } from "@clr/angular";
|
||||||
|
import { SharedModule } from "../../../shared/shared.module";
|
||||||
|
import { LastTriggerComponent } from "./last-trigger.component";
|
||||||
|
import { LastTrigger } from "../webhook";
|
||||||
|
import { SimpleChange } from "@angular/core";
|
||||||
|
|
||||||
|
describe('LastTriggerComponent', () => {
|
||||||
|
const mokedTriggers: LastTrigger[] = [
|
||||||
|
{
|
||||||
|
policy_name: 'http',
|
||||||
|
enabled: true,
|
||||||
|
event_type: 'pullImage',
|
||||||
|
creation_time: null,
|
||||||
|
last_trigger_time: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
policy_name: 'slack',
|
||||||
|
enabled: true,
|
||||||
|
event_type: 'pullImage',
|
||||||
|
creation_time: null,
|
||||||
|
last_trigger_time: null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let component: LastTriggerComponent;
|
||||||
|
let fixture: ComponentFixture<LastTriggerComponent>;
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
ClarityModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
LastTriggerComponent
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LastTriggerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should render one row', async () => {
|
||||||
|
component.inputLastTriggers = mokedTriggers;
|
||||||
|
component.webhookName = 'slack';
|
||||||
|
component.ngOnChanges({inputLastTriggers: new SimpleChange([], mokedTriggers, true)});
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
||||||
|
expect(rows.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
Component, Input, OnChanges,
|
||||||
|
OnInit, SimpleChanges
|
||||||
|
} from "@angular/core";
|
||||||
|
import { LastTrigger } from "../webhook";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'last-trigger',
|
||||||
|
templateUrl: 'last-trigger.component.html',
|
||||||
|
styleUrls: ['./last-trigger.component.scss']
|
||||||
|
})
|
||||||
|
export class LastTriggerComponent implements OnInit , OnChanges {
|
||||||
|
@Input() inputLastTriggers: LastTrigger[];
|
||||||
|
@Input() webhookName: string;
|
||||||
|
lastTriggers: LastTrigger[] = [];
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes && changes['inputLastTriggers']) {
|
||||||
|
this.lastTriggers = [];
|
||||||
|
this.inputLastTriggers.forEach(item => {
|
||||||
|
if (this.webhookName === item.policy_name) {
|
||||||
|
this.lastTriggers.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +1,109 @@
|
|||||||
<div *ngIf="loadingWebhook" class="clr-row mt-2 center">
|
<div class="row">
|
||||||
<span class="spinner spinner-md"></span>
|
<h4 class="mt-1">{{'WEBHOOK.WEBHOOKS' | translate}}</h4>
|
||||||
</div>
|
<clr-datagrid [clrDgLoading]="loadingWebhookList || loadingMetadata" [(clrDgSelected)]="selectedRow">
|
||||||
<div class="row" *ngIf="!loadingWebhook">
|
<clr-dg-action-bar>
|
||||||
<div *ngIf="!showCreate">
|
<div class="clr-row">
|
||||||
<div>
|
<div class="clr-col-7">
|
||||||
<div class="row flex-items-xs-between rightPos">
|
<button type="button" class="btn btn-secondary" (click)="newWebhook()">
|
||||||
<div class="flex-xs-middle option-left">
|
<clr-icon shape="plus" size="16"></clr-icon>
|
||||||
<div>
|
{{'WEBHOOK.NEW_WEBHOOK' | translate}}
|
||||||
<span class="endpoint-label">Webhook endpoint</span>: {{endpoint}}
|
</button>
|
||||||
<button class="btn btn-link" id="edit-webhook" (click)="openAddWebhookModal()">{{'WEBHOOK.EDIT_BUTTON' | translate}}</button>
|
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-link" clrDropdownTrigger>
|
||||||
</div>
|
<span id="action-scanner">{{'MEMBER.ACTION' | translate}}
|
||||||
<div [ngSwitch]="isEnabled">
|
<clr-icon class="clr-icon" shape="caret down"></clr-icon></span>
|
||||||
<button *ngSwitchCase="false" id="enable-webhook-action" class="btn btn-link" (click)="switchWebhookStatus(true)">{{'WEBHOOK.ENABLED_BUTTON' | translate}}</button>
|
<clr-dropdown-menu *clrIfOpen>
|
||||||
<button *ngSwitchCase="true" id="disable-webhook-action" class="btn btn-link disabled-btn" (click)="switchWebhookStatus(false)">{{'WEBHOOK.DISABLED_BUTTON' | translate}}</button>
|
<button clrDropdownItem (click)="switchWebhookStatus()"
|
||||||
</div>
|
[disabled]="!(selectedRow && selectedRow.length === 1)">
|
||||||
|
<span *ngIf="selectedRow[0] && !selectedRow[0].enabled">
|
||||||
|
<clr-icon class="margin-top-2" size="16" shape="success-standard"></clr-icon>
|
||||||
|
<span class="margin-left-10">{{'WEBHOOK.ENABLE' | translate}}</span>
|
||||||
|
</span>
|
||||||
|
<span *ngIf="!(selectedRow[0] && !selectedRow[0].enabled)">
|
||||||
|
<clr-icon class="margin-top-2" size="16" shape="ban"></clr-icon>
|
||||||
|
<span class="margin-left-10">{{'WEBHOOK.DISABLE' | translate}}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button clrDropdownItem (click)="editWebhook()"
|
||||||
|
class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length === 1)">
|
||||||
|
<clr-icon class="margin-top-0" size="16" shape="pencil"></clr-icon>
|
||||||
|
<span class="margin-left-10">{{'BUTTON.EDIT' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
<button clrDropdownItem (click)="deleteWebhook()"
|
||||||
|
class="btn btn-secondary" [disabled]="!(selectedRow && selectedRow.length >= 1)">
|
||||||
|
<clr-icon class="margin-top-0" size="16" shape="times"></clr-icon>
|
||||||
|
<span id="delete-scanner-action"
|
||||||
|
class="margin-left-10">{{'BUTTON.DELETE' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
</clr-dropdown-menu>
|
||||||
|
</clr-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="clr-col-5">
|
||||||
|
<div class="action-head-pos">
|
||||||
|
<span class="refresh-btn">
|
||||||
|
<clr-icon shape="refresh" (click)="success()" [hidden]="loadingWebhookList || loadingMetadata"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</clr-dg-action-bar>
|
||||||
</div>
|
<clr-dg-column [clrDgField]="'name'">{{'WEBHOOK.NAME' | translate}}</clr-dg-column>
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 datagrid-margin-top">
|
<clr-dg-column>{{'WEBHOOK.NOTIFY_TYPE' | translate}}</clr-dg-column>
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-column>{{'WEBHOOK.TARGET' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'WEBHOOK.TYPE' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'WEBHOOK.ENABLED' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'WEBHOOK.STATUS' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'WEBHOOK.EVENT_TYPES' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'WEBHOOK.CREATED' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'WEBHOOK.CREATED' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'WEBHOOK.LAST_TRIGGERED' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'WEBHOOK.DESCRIPTION' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *clrDgItems="let item of lastTriggers">
|
<clr-dg-placeholder>
|
||||||
<clr-dg-cell>{{item.event_type}}</clr-dg-cell>
|
{{'WEBHOOK.NO_WEBHOOK' | translate}}
|
||||||
<clr-dg-cell [ngSwitch]="item.enabled">
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-row *clrDgItems="let w of webhookList" [clrDgItem]="w">
|
||||||
|
<clr-dg-cell>{{w.name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{w?.targets[0].type}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{w?.targets[0].address}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell [ngSwitch]="w.enabled">
|
||||||
<div *ngSwitchCase="true" class="icon-wrap">
|
<div *ngSwitchCase="true" class="icon-wrap">
|
||||||
<clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
|
<clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
|
||||||
<span>{{'WEBHOOK.ENABLED' | translate}}</span>
|
<span>{{'WEBHOOK.ENABLED' | translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="false" class="icon-wrap">
|
<div *ngSwitchCase="false" class="icon-wrap">
|
||||||
<clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
|
<clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
|
||||||
<span>{{'WEBHOOK.DISABLED' | translate}}</span>
|
<span>{{'WEBHOOK.DISABLED' | translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{item.creation_time | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-dg-cell>{{item.last_trigger_time | date: 'short'}}</clr-dg-cell>
|
<div class="cell" *ngIf="w?.event_types?.length">
|
||||||
</clr-dg-row>
|
<div class="bar-state">
|
||||||
<clr-dg-footer>
|
<span class="label" *ngIf="w?.event_types[0]">{{w?.event_types[0]}}</span>
|
||||||
<span *ngIf="lastTriggerCount">1 - {{lastTriggerCount}} {{'WEBHOOK.OF' | translate}} </span> {{lastTriggerCount}} {{'WEBHOOK.ITEMS' | translate}}
|
</div>
|
||||||
<clr-dg-pagination [clrDgPageSize]="10"></clr-dg-pagination>
|
<div class="signpost-item" [hidden]="w?.event_types?.length<=1">
|
||||||
</clr-dg-footer>
|
<div class="trigger-item">
|
||||||
</clr-datagrid>
|
<clr-signpost>
|
||||||
</div>
|
<button class="btn btn-link font-size-20" clrSignpostTrigger>...</button>
|
||||||
</div>
|
<clr-signpost-content [clrPosition]="'left-top'" *clrIfOpen>
|
||||||
<div *ngIf="showCreate">
|
<div>
|
||||||
<p class="create-text pt-1">{{'WEBHOOK.CREATE_WEBHOOK_DESC' | translate}}</p>
|
<div *ngFor="let e of w?.event_types" class="bar-state">
|
||||||
<add-webhook-form class="webhook-form-wrap" [projectId]="projectId" [webhook]="webhook" [isModify]="false" (edit)="editWebhook($event)"></add-webhook-form>
|
<span class="label not-scan">{{e}}</span>
|
||||||
</div>
|
</div>
|
||||||
<add-webhook [projectId]="projectId" [webhook]="webhook" (modify)="editWebhook($event)"></add-webhook>
|
</div>
|
||||||
<confirmation-dialog #confirmationDialogComponent (confirmAction)="confirmSwitch($event)"></confirmation-dialog>
|
</clr-signpost-content>
|
||||||
|
</clr-signpost>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{w.creation_time | date: 'short'}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{w.description}}</clr-dg-cell>
|
||||||
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
|
<last-trigger class="w-100" [webhookName]="w.name" *clrIfExpanded [inputLastTriggers]="lastTriggers"></last-trigger>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<span *ngIf="webhookList?.length > 0">1 - {{webhookList?.length}} {{'WEBHOOK.OF' | translate}} </span> {{webhookList?.length}} {{'WEBHOOK.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination [clrDgPageSize]="10"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
</div>
|
</div>
|
||||||
|
<add-webhook (notify)="success()" [metadata]="metadata" [projectId]="projectId"></add-webhook>
|
||||||
|
<confirmation-dialog #confirmationDialogComponent (confirmAction)="confirmSwitch($event)"></confirmation-dialog>
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,3 +39,19 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.font-size-20 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.action-head-pos {
|
||||||
|
padding-right: 18px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@ -11,26 +11,69 @@ import { ClarityModule } from '@clr/angular';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { delay } from "rxjs/operators";
|
||||||
|
import { Webhook } from "./webhook";
|
||||||
|
import { AddWebhookFormComponent } from "./add-webhook-form/add-webhook-form.component";
|
||||||
|
import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.component";
|
||||||
|
import { AddWebhookComponent } from "./add-webhook/add-webhook.component";
|
||||||
|
import { ConfirmationDialogComponent } from "../../../lib/components/confirmation-dialog";
|
||||||
describe('WebhookComponent', () => {
|
describe('WebhookComponent', () => {
|
||||||
let component: WebhookComponent;
|
let component: WebhookComponent;
|
||||||
let fixture: ComponentFixture<WebhookComponent>;
|
let fixture: ComponentFixture<WebhookComponent>;
|
||||||
const mockMessageHandlerService = {
|
const mockMessageHandlerService = {
|
||||||
handleError: () => { }
|
handleError: () => { }
|
||||||
};
|
};
|
||||||
|
const mockedMetadata = {
|
||||||
|
"event_type": [
|
||||||
|
"projectQuota",
|
||||||
|
"pullImage",
|
||||||
|
"scanningFailed",
|
||||||
|
"uploadChart",
|
||||||
|
"deleteChart",
|
||||||
|
"downloadChart",
|
||||||
|
"scanningCompleted",
|
||||||
|
"pushImage",
|
||||||
|
"deleteImage"
|
||||||
|
],
|
||||||
|
"notify_type": [
|
||||||
|
"http",
|
||||||
|
"slack"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const mockedWehook: Webhook = {
|
||||||
|
id: 1,
|
||||||
|
project_id: 1,
|
||||||
|
name: 'test',
|
||||||
|
description: 'just a test webhook',
|
||||||
|
targets: [{
|
||||||
|
address: 'https://test.com',
|
||||||
|
type: 'http',
|
||||||
|
attachment: null,
|
||||||
|
auth_header: null,
|
||||||
|
skip_cert_verify: true,
|
||||||
|
}],
|
||||||
|
event_types: [
|
||||||
|
'projectQuota'
|
||||||
|
],
|
||||||
|
creator: null,
|
||||||
|
creation_time: null,
|
||||||
|
update_time: null,
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
const mockWebhookService = {
|
const mockWebhookService = {
|
||||||
listLastTrigger: () => {
|
listLastTrigger: () => {
|
||||||
return of([]);
|
return of([]).pipe(delay(0));
|
||||||
},
|
},
|
||||||
listWebhook: () => {
|
listWebhook: () => {
|
||||||
return of([
|
return of([mockedWehook
|
||||||
{
|
]).pipe(delay(0));
|
||||||
targets: [
|
|
||||||
{ address: "" }
|
|
||||||
],
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
},
|
},
|
||||||
|
getWebhookMetadata() {
|
||||||
|
return of(mockedMetadata).pipe(delay(0));
|
||||||
|
},
|
||||||
|
editWebhook() {
|
||||||
|
return of(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const mockActivatedRoute = {
|
const mockActivatedRoute = {
|
||||||
RouterparamMap: of({ get: (key) => 'value' }),
|
RouterparamMap: of({ get: (key) => 'value' }),
|
||||||
@ -61,7 +104,12 @@ describe('WebhookComponent', () => {
|
|||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule
|
||||||
],
|
],
|
||||||
declarations: [WebhookComponent],
|
declarations: [WebhookComponent,
|
||||||
|
AddWebhookComponent,
|
||||||
|
AddWebhookFormComponent,
|
||||||
|
InlineAlertComponent,
|
||||||
|
ConfirmationDialogComponent
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TranslateService,
|
TranslateService,
|
||||||
{ provide: WebhookService, useValue: mockWebhookService },
|
{ provide: WebhookService, useValue: mockWebhookService },
|
||||||
@ -72,13 +120,69 @@ describe('WebhookComponent', () => {
|
|||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
fixture = TestBed.createComponent(WebhookComponent);
|
fixture = TestBed.createComponent(WebhookComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.autoDetectChanges(true);
|
||||||
|
await fixture.whenStable();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', async () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
it('should get webhook list', async () => {
|
||||||
|
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
||||||
|
expect(rows.length).toEqual(1);
|
||||||
|
});
|
||||||
|
it('should open modal', async () => {
|
||||||
|
component.newWebhook();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
|
||||||
|
expect(body).toBeTruthy();
|
||||||
|
const title: HTMLElement = fixture.nativeElement.querySelector(".modal-title");
|
||||||
|
expect(title.innerText).toEqual('WEBHOOK.ADD_WEBHOOK');
|
||||||
|
});
|
||||||
|
it('should open edit modal', async () => {
|
||||||
|
component.webhookList[0].name = 'test';
|
||||||
|
component.selectedRow[0] = component.webhookList[0];
|
||||||
|
component.editWebhook();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
|
||||||
|
expect(body).toBeTruthy();
|
||||||
|
const title: HTMLElement = fixture.nativeElement.querySelector(".modal-title");
|
||||||
|
expect(title.innerText).toEqual('WEBHOOK.EDIT_WEBHOOK');
|
||||||
|
const nameInput: HTMLInputElement = fixture.nativeElement.querySelector("#name");
|
||||||
|
expect(nameInput.value).toEqual('test');
|
||||||
|
});
|
||||||
|
it('should disable webhook', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
component.selectedRow[0] = component.webhookList[0];
|
||||||
|
component.webhookList[0].enabled = true;
|
||||||
|
component.switchWebhookStatus();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const button: HTMLButtonElement = fixture.nativeElement.querySelector("#dialog-action-disable");
|
||||||
|
button.dispatchEvent(new Event('click'));
|
||||||
|
await fixture.whenStable();
|
||||||
|
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
|
||||||
|
expect(body).toBeFalsy();
|
||||||
|
});
|
||||||
|
it('should enable webhook', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
|
component.webhookList[0].enabled = false;
|
||||||
|
component.selectedRow[0] = component.webhookList[0];
|
||||||
|
component.switchWebhookStatus();
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
const buttonEnable: HTMLButtonElement = fixture.nativeElement.querySelector("#dialog-action-enable");
|
||||||
|
buttonEnable.dispatchEvent(new Event('click'));
|
||||||
|
await fixture.whenStable();
|
||||||
|
const bodyEnable: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
|
||||||
|
expect(bodyEnable).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,13 +28,13 @@ import {
|
|||||||
} from "../../shared/shared.const";
|
} from "../../shared/shared.const";
|
||||||
|
|
||||||
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message";
|
||||||
import { ConfirmationAcknowledgement } from "../../shared/confirmation-dialog/confirmation-state-message";
|
|
||||||
import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/confirmation-dialog.component";
|
import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/confirmation-dialog.component";
|
||||||
|
import { clone } from "../../../lib/utils/utils";
|
||||||
|
import { forkJoin, Observable } from "rxjs";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './webhook.component.html',
|
templateUrl: './webhook.component.html',
|
||||||
styleUrls: ['./webhook.component.scss'],
|
styleUrls: ['./webhook.component.scss']
|
||||||
// changeDetection: ChangeDetectionStrategy.OnPush
|
|
||||||
})
|
})
|
||||||
export class WebhookComponent implements OnInit {
|
export class WebhookComponent implements OnInit {
|
||||||
@ViewChild(AddWebhookComponent, { static: false } )
|
@ViewChild(AddWebhookComponent, { static: false } )
|
||||||
@ -43,16 +43,16 @@ export class WebhookComponent implements OnInit {
|
|||||||
addWebhookFormComponent: AddWebhookFormComponent;
|
addWebhookFormComponent: AddWebhookFormComponent;
|
||||||
@ViewChild("confirmationDialogComponent", { static: false })
|
@ViewChild("confirmationDialogComponent", { static: false })
|
||||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||||
webhook: Webhook;
|
|
||||||
endpoint: string = '';
|
|
||||||
lastTriggers: LastTrigger[] = [];
|
lastTriggers: LastTrigger[] = [];
|
||||||
lastTriggerCount: number = 0;
|
lastTriggerCount: number = 0;
|
||||||
isEnabled: boolean;
|
|
||||||
loading: boolean = false;
|
|
||||||
showCreate: boolean = false;
|
|
||||||
loadingWebhook: boolean = true;
|
|
||||||
projectId: number;
|
projectId: number;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
|
selectedRow: Webhook[] = [];
|
||||||
|
webhookList: Webhook[] = [];
|
||||||
|
metadata: any;
|
||||||
|
loadingMetadata: boolean = false;
|
||||||
|
loadingWebhookList: boolean = false;
|
||||||
|
loadingTriggers: boolean = false;
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
@ -66,19 +66,34 @@ export class WebhookComponent implements OnInit {
|
|||||||
let project = <Project>(resolverData["projectResolver"]);
|
let project = <Project>(resolverData["projectResolver"]);
|
||||||
this.projectName = project.name;
|
this.projectName = project.name;
|
||||||
}
|
}
|
||||||
this.getData(this.projectId);
|
this.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(projectId: number) {
|
getData() {
|
||||||
this.getLastTriggers(projectId);
|
this.getMetadata();
|
||||||
this.getWebhook(projectId);
|
this.getLastTriggers();
|
||||||
|
this.getWebhooks();
|
||||||
|
this.selectedRow = [];
|
||||||
|
}
|
||||||
|
getMetadata() {
|
||||||
|
this.loadingMetadata = true;
|
||||||
|
this.webhookService.getWebhookMetadata(this.projectId)
|
||||||
|
.pipe(finalize(() => (this.loadingMetadata = false)))
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
this.metadata = response;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.messageHandlerService.handleError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastTriggers(projectId: number) {
|
getLastTriggers() {
|
||||||
this.loading = true;
|
this.loadingTriggers = true;
|
||||||
this.webhookService
|
this.webhookService
|
||||||
.listLastTrigger(projectId)
|
.listLastTrigger(this.projectId)
|
||||||
.pipe(finalize(() => (this.loading = false)))
|
.pipe(finalize(() => (this.loadingTriggers = false)))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.lastTriggers = response;
|
this.lastTriggers = response;
|
||||||
@ -90,20 +105,14 @@ export class WebhookComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWebhook(projectId: number) {
|
getWebhooks() {
|
||||||
|
this.loadingWebhookList = true;
|
||||||
this.webhookService
|
this.webhookService
|
||||||
.listWebhook(projectId)
|
.listWebhook(this.projectId)
|
||||||
.pipe(finalize(() => (this.loadingWebhook = false)))
|
.pipe(finalize(() => (this.loadingWebhookList = false)))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
if (response.length) {
|
this.webhookList = response;
|
||||||
this.webhook = response[0];
|
|
||||||
this.endpoint = this.webhook.targets[0].address;
|
|
||||||
this.isEnabled = this.webhook.enabled;
|
|
||||||
this.showCreate = false;
|
|
||||||
} else {
|
|
||||||
this.showCreate = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.messageHandlerService.handleError(error);
|
this.messageHandlerService.handleError(error);
|
||||||
@ -111,46 +120,103 @@ export class WebhookComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
switchWebhookStatus(enabled = false) {
|
switchWebhookStatus() {
|
||||||
let content = '';
|
let content = '';
|
||||||
this.translate.get(
|
this.translate.get(
|
||||||
enabled
|
!this.selectedRow[0].enabled
|
||||||
? 'WEBHOOK.ENABLED_WEBHOOK_SUMMARY'
|
? 'WEBHOOK.ENABLED_WEBHOOK_SUMMARY'
|
||||||
: 'WEBHOOK.DISABLED_WEBHOOK_SUMMARY'
|
: 'WEBHOOK.DISABLED_WEBHOOK_SUMMARY'
|
||||||
).subscribe((res) => content = res + this.projectName);
|
, {name: this.selectedRow[0].name}).subscribe((res) => {
|
||||||
let message = new ConfirmationMessage(
|
content = res;
|
||||||
enabled ? 'WEBHOOK.ENABLED_WEBHOOK_TITLE' : 'WEBHOOK.DISABLED_WEBHOOK_TITLE',
|
let message = new ConfirmationMessage(
|
||||||
content,
|
!this.selectedRow[0].enabled ? 'WEBHOOK.ENABLED_WEBHOOK_TITLE' : 'WEBHOOK.DISABLED_WEBHOOK_TITLE',
|
||||||
'',
|
content,
|
||||||
{},
|
'',
|
||||||
ConfirmationTargets.WEBHOOK,
|
{},
|
||||||
enabled ? ConfirmationButtons.ENABLE_CANCEL : ConfirmationButtons.DISABLE_CANCEL
|
ConfirmationTargets.WEBHOOK,
|
||||||
);
|
!this.selectedRow[0].enabled ? ConfirmationButtons.ENABLE_CANCEL : ConfirmationButtons.DISABLE_CANCEL
|
||||||
this.confirmationDialogComponent.open(message);
|
);
|
||||||
|
this.confirmationDialogComponent.open(message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmSwitch(message: ConfirmationAcknowledgement) {
|
confirmSwitch(message) {
|
||||||
if (message &&
|
if (message &&
|
||||||
message.source === ConfirmationTargets.WEBHOOK &&
|
message.source === ConfirmationTargets.WEBHOOK &&
|
||||||
message.state === ConfirmationState.CONFIRMED) {
|
message.state === ConfirmationState.CONFIRMED) {
|
||||||
this.webhookService
|
if (JSON.stringify(message.data) === '{}') {
|
||||||
.editWebhook(this.projectId, this.webhook.id, Object.assign({}, this.webhook, { enabled: !this.isEnabled }))
|
this.webhookService
|
||||||
.subscribe(
|
.editWebhook(this.projectId, this.selectedRow[0].id,
|
||||||
|
Object.assign({}, this.selectedRow[0], { enabled: !this.selectedRow[0].enabled }))
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
this.getData();
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.messageHandlerService.handleError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const observableLists: Observable<any>[] = [];
|
||||||
|
message.data.forEach(item => {
|
||||||
|
observableLists.push(this.webhookService.deleteWebhook(this.projectId, item.id));
|
||||||
|
});
|
||||||
|
forkJoin(...observableLists).subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.getData(this.projectId);
|
this.getData();
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.messageHandlerService.handleError(error);
|
this.messageHandlerService.handleError(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editWebhook(isModify: boolean): void {
|
editWebhook() {
|
||||||
this.getData(this.projectId);
|
if (this.metadata) {
|
||||||
|
this.addWebhookComponent.isOpen = true;
|
||||||
|
this.addWebhookComponent.isEdit = true;
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.isModify = true;
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.webhook = clone(this.selectedRow[0]);
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.webhook.event_types = clone(this.selectedRow[0].event_types);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openAddWebhookModal(): void {
|
openAddWebhookModal(): void {
|
||||||
this.addWebhookComponent.openAddWebhookModal();
|
this.addWebhookComponent.openAddWebhookModal();
|
||||||
}
|
}
|
||||||
|
newWebhook() {
|
||||||
|
if (this.metadata) {
|
||||||
|
this.addWebhookComponent.isOpen = true;
|
||||||
|
this.addWebhookComponent.isEdit = false;
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.isModify = false;
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.currentForm.reset({notifyType: this.metadata.notify_type[0]});
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.webhook = new Webhook();
|
||||||
|
this.addWebhookComponent.addWebhookFormComponent.webhook.event_types = clone(this.metadata.event_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success() {
|
||||||
|
this.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteWebhook() {
|
||||||
|
const names: string[] = [];
|
||||||
|
this.selectedRow.forEach(item => {
|
||||||
|
names.push(item.name);
|
||||||
|
});
|
||||||
|
let content = '';
|
||||||
|
this.translate.get(
|
||||||
|
'WEBHOOK.DELETE_WEBHOOK_SUMMARY'
|
||||||
|
, {names: names.join(',')}).subscribe((res) => content = res);
|
||||||
|
const msg: ConfirmationMessage = new ConfirmationMessage(
|
||||||
|
"SCANNER.CONFIRM_DELETION",
|
||||||
|
content,
|
||||||
|
names.join(','),
|
||||||
|
this.selectedRow,
|
||||||
|
ConfirmationTargets.WEBHOOK,
|
||||||
|
ConfirmationButtons.DELETE_CANCEL
|
||||||
|
);
|
||||||
|
this.confirmationDialogComponent.open(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { throwError as observableThrowError, Observable } from "rxjs";
|
import { throwError as observableThrowError, Observable, of } from "rxjs";
|
||||||
import { map, catchError } from "rxjs/operators";
|
import { map, catchError, delay } from "rxjs/operators";
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Webhook, LastTrigger } from "./webhook";
|
import { Webhook, LastTrigger } from "./webhook";
|
||||||
@ -42,6 +42,12 @@ export class WebhookService {
|
|||||||
.pipe(catchError(error => observableThrowError(error)));
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteWebhook(projectId: number, policyId: number): Observable<any> {
|
||||||
|
return this.http
|
||||||
|
.delete(`${ CURRENT_BASE_HREF }/projects/${projectId}/webhook/policies/${policyId}`)
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
public createWebhook(projectId: number, data: any): Observable<any> {
|
public createWebhook(projectId: number, data: any): Observable<any> {
|
||||||
return this.http
|
return this.http
|
||||||
.post(`${ CURRENT_BASE_HREF }/projects/${projectId}/webhook/policies`, data)
|
.post(`${ CURRENT_BASE_HREF }/projects/${projectId}/webhook/policies`, data)
|
||||||
@ -54,4 +60,10 @@ export class WebhookService {
|
|||||||
.post(`${ CURRENT_BASE_HREF }/projects/${projectId}/webhook/policies/test`, param)
|
.post(`${ CURRENT_BASE_HREF }/projects/${projectId}/webhook/policies/test`, param)
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWebhookMetadata(projectId: number): Observable<any> {
|
||||||
|
return this.http
|
||||||
|
.get(`${CURRENT_BASE_HREF}/projects/${projectId}/webhook/events`)
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import { WebhookEventTypes } from '../../shared/shared.const';
|
|
||||||
|
|
||||||
export class Webhook {
|
export class Webhook {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
project_id: number;
|
project_id: number;
|
||||||
description: string;
|
description: string;
|
||||||
targets: Target[];
|
targets: Target[];
|
||||||
event_types: WebhookEventTypes[];
|
event_types: string[];
|
||||||
creator: string;
|
creator: string;
|
||||||
creation_time: Date;
|
creation_time: Date;
|
||||||
update_time: Date;
|
update_time: Date;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
constructor () {
|
||||||
|
this.targets = [];
|
||||||
|
this.targets.push(new Target());
|
||||||
|
this.event_types = [];
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Target {
|
export class Target {
|
||||||
@ -28,6 +32,7 @@ export class Target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LastTrigger {
|
export class LastTrigger {
|
||||||
|
policy_name: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
event_type: string;
|
event_type: string;
|
||||||
creation_time: Date;
|
creation_time: Date;
|
||||||
|
@ -99,15 +99,3 @@ export enum ResourceType {
|
|||||||
CHART_VERSION = 2,
|
CHART_VERSION = 2,
|
||||||
REPOSITORY_TAG = 3,
|
REPOSITORY_TAG = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WebhookEventTypes {
|
|
||||||
DOWNLOAD_CHART = "downloadChart",
|
|
||||||
DELETE_CHART = "deleteChart",
|
|
||||||
UPLOAD_CHART = "uploadChart",
|
|
||||||
DELETE_IMAGE = "deleteImage",
|
|
||||||
PULL_IMAGE = "pullImage",
|
|
||||||
PUSH_IMAGE = "pushImage",
|
|
||||||
SCANNING_FAILED = "scanningFailed",
|
|
||||||
SCANNING_COMPLETED = "scanningCompleted",
|
|
||||||
PROJECT_QUOTA = "projectQuota",
|
|
||||||
}
|
|
||||||
|
@ -363,7 +363,8 @@
|
|||||||
"OF": "of",
|
"OF": "of",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"LAST_TRIGGERED": "Last Triggered",
|
"LAST_TRIGGERED": "Last Triggered",
|
||||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
"EDIT_WEBHOOK": "Edit Webhook",
|
||||||
|
"ADD_WEBHOOK": "Add Webhook",
|
||||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||||
@ -377,10 +378,28 @@
|
|||||||
"SAVE_BUTTON": "SAVE",
|
"SAVE_BUTTON": "SAVE",
|
||||||
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
||||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
"ENABLED_WEBHOOK_TITLE": "Enable Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
"DISABLED_WEBHOOK_TITLE": "Disable Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "Delete Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "Do you want to delete webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "New Webhook",
|
||||||
|
"ENABLE": "Enable",
|
||||||
|
"DISABLE": "Disable",
|
||||||
|
"NAME": "Name",
|
||||||
|
"TARGET": "Target",
|
||||||
|
"EVENT_TYPES": "Event types",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"NO_WEBHOOK": "No Webhook",
|
||||||
|
"LAST_TRIGGER": "Last Trigger",
|
||||||
|
"WEBHOOK_NAME": "Webhook Name",
|
||||||
|
"NO_TRIGGER": "No Trigger",
|
||||||
|
"NAME_REQUIRED": "Name is required",
|
||||||
|
"NOTIFY_TYPE": "Notify Type",
|
||||||
|
"EVENT_TYPE": "Event Type",
|
||||||
|
"EVENT_TYPE_REQUIRED": "Require at least one event type"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Group",
|
"GROUP": "Group",
|
||||||
|
@ -364,7 +364,8 @@
|
|||||||
"OF": "of",
|
"OF": "of",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"LAST_TRIGGERED": "Last Triggered",
|
"LAST_TRIGGERED": "Last Triggered",
|
||||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
"EDIT_WEBHOOK": "Edit Webhook",
|
||||||
|
"ADD_WEBHOOK": "Add Webhook",
|
||||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||||
@ -378,10 +379,28 @@
|
|||||||
"SAVE_BUTTON": "SAVE",
|
"SAVE_BUTTON": "SAVE",
|
||||||
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
||||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
"ENABLED_WEBHOOK_TITLE": "Enable Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
"DISABLED_WEBHOOK_TITLE": "Disable Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "Delete Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "Do you want to delete webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "New Webhook",
|
||||||
|
"ENABLE": "Enable",
|
||||||
|
"DISABLE": "Disable",
|
||||||
|
"NAME": "Name",
|
||||||
|
"TARGET": "Target",
|
||||||
|
"EVENT_TYPES": "Event types",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"NO_WEBHOOK": "No Webhook",
|
||||||
|
"LAST_TRIGGER": "Last Trigger",
|
||||||
|
"WEBHOOK_NAME": "Webhook Name",
|
||||||
|
"NO_TRIGGER": "No Trigger",
|
||||||
|
"NAME_REQUIRED": "Name is required",
|
||||||
|
"NOTIFY_TYPE": "Notify Type",
|
||||||
|
"EVENT_TYPE": "Event Type",
|
||||||
|
"EVENT_TYPE_REQUIRED": "Require at least one event type"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Group",
|
"GROUP": "Group",
|
||||||
|
@ -355,7 +355,8 @@
|
|||||||
"OF": "of",
|
"OF": "of",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"LAST_TRIGGERED": "Last Triggered",
|
"LAST_TRIGGERED": "Last Triggered",
|
||||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
"EDIT_WEBHOOK": "Edit Webhook",
|
||||||
|
"ADD_WEBHOOK": "Add Webhook",
|
||||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||||
@ -369,10 +370,28 @@
|
|||||||
"SAVE_BUTTON": "SAVE",
|
"SAVE_BUTTON": "SAVE",
|
||||||
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
||||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
"ENABLED_WEBHOOK_TITLE": "Enable Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
"DISABLED_WEBHOOK_TITLE": "Disable Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "Delete Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "Do you want to delete webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "New Webhook",
|
||||||
|
"ENABLE": "Enable",
|
||||||
|
"DISABLE": "Disable",
|
||||||
|
"NAME": "Name",
|
||||||
|
"TARGET": "Target",
|
||||||
|
"EVENT_TYPES": "Event types",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"NO_WEBHOOK": "No Webhook",
|
||||||
|
"LAST_TRIGGER": "Last Trigger",
|
||||||
|
"WEBHOOK_NAME": "Webhook Name",
|
||||||
|
"NO_TRIGGER": "No Trigger",
|
||||||
|
"NAME_REQUIRED": "Name is required",
|
||||||
|
"NOTIFY_TYPE": "Notify Type",
|
||||||
|
"EVENT_TYPE": "Event Type",
|
||||||
|
"EVENT_TYPE_REQUIRED": "Require at least one event type"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"Group": "Group",
|
"Group": "Group",
|
||||||
|
@ -393,7 +393,8 @@
|
|||||||
"OF": "of",
|
"OF": "of",
|
||||||
"ITEMS": "items",
|
"ITEMS": "items",
|
||||||
"LAST_TRIGGERED": "Last Triggered",
|
"LAST_TRIGGERED": "Last Triggered",
|
||||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
"EDIT_WEBHOOK": "Edit Webhook",
|
||||||
|
"ADD_WEBHOOK": "Add Webhook",
|
||||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||||
@ -407,10 +408,28 @@
|
|||||||
"SAVE_BUTTON": "SAVE",
|
"SAVE_BUTTON": "SAVE",
|
||||||
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
||||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
"ENABLED_WEBHOOK_TITLE": "Enable Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
"DISABLED_WEBHOOK_TITLE": "Disable Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "Delete Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "Do you want to delete webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "New Webhook",
|
||||||
|
"ENABLE": "Enable",
|
||||||
|
"DISABLE": "Disable",
|
||||||
|
"NAME": "Name",
|
||||||
|
"TARGET": "Target",
|
||||||
|
"EVENT_TYPES": "Event types",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"NO_WEBHOOK": "No Webhook",
|
||||||
|
"LAST_TRIGGER": "Last Trigger",
|
||||||
|
"WEBHOOK_NAME": "Webhook Name",
|
||||||
|
"NO_TRIGGER": "No Trigger",
|
||||||
|
"NAME_REQUIRED": "Name is required",
|
||||||
|
"NOTIFY_TYPE": "Notify Type",
|
||||||
|
"EVENT_TYPE": "Event Type",
|
||||||
|
"EVENT_TYPE_REQUIRED": "Require at least one event type"
|
||||||
},
|
},
|
||||||
"AUDIT_LOG": {
|
"AUDIT_LOG": {
|
||||||
"USERNAME": "Nome do usuário",
|
"USERNAME": "Nome do usuário",
|
||||||
|
@ -363,7 +363,8 @@
|
|||||||
"OF": "of",
|
"OF": "of",
|
||||||
"ITEMS": "adetler",
|
"ITEMS": "adetler",
|
||||||
"LAST_TRIGGERED": "Son Tetiklenen",
|
"LAST_TRIGGERED": "Son Tetiklenen",
|
||||||
"EDIT_WEBHOOK": "Ağ Kancası Uç Noktası",
|
"EDIT_WEBHOOK": "Edit Webhook",
|
||||||
|
"ADD_WEBHOOK": "Add Webhook",
|
||||||
"CREATE_WEBHOOK": "Ağ kancaları ile başladı",
|
"CREATE_WEBHOOK": "Ağ kancaları ile başladı",
|
||||||
"EDIT_WEBHOOK_DESC": "Ağ kancası bildirimleri almak için bitiş noktasını belirtin",
|
"EDIT_WEBHOOK_DESC": "Ağ kancası bildirimleri almak için bitiş noktasını belirtin",
|
||||||
"CREATE_WEBHOOK_DESC": "Ağ kancalarına başlamak için, web kanca sunucusuna erişmek için bir uç nokta ve kimlik bilgisi sağlayın.",
|
"CREATE_WEBHOOK_DESC": "Ağ kancalarına başlamak için, web kanca sunucusuna erişmek için bir uç nokta ve kimlik bilgisi sağlayın.",
|
||||||
@ -377,10 +378,28 @@
|
|||||||
"SAVE_BUTTON": "KAYDET",
|
"SAVE_BUTTON": "KAYDET",
|
||||||
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
"TEST_ENDPOINT_SUCCESS": "Connection tested successfully.",
|
||||||
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
"TEST_ENDPOINT_FAILURE": "Failed to ping endpoint.",
|
||||||
"ENABLED_WEBHOOK_TITLE": "Proje Ağ Kancalarını Etkinleştir",
|
"ENABLED_WEBHOOK_TITLE": "Enable Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "Proje için ağ kancalarını etkinleştirmek istiyor musunuz?",
|
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "Proje Ağ kancalarını Devre Dışı Bırak",
|
"DISABLED_WEBHOOK_TITLE": "Disable Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "Proje için ağ kancalarını devre dışı bırakmak istiyor musunuz?"
|
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "Delete Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "Do you want to delete webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "New Webhook",
|
||||||
|
"ENABLE": "Enable",
|
||||||
|
"DISABLE": "Disable",
|
||||||
|
"NAME": "Name",
|
||||||
|
"TARGET": "Target",
|
||||||
|
"EVENT_TYPES": "Event types",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"NO_WEBHOOK": "No Webhook",
|
||||||
|
"LAST_TRIGGER": "Last Trigger",
|
||||||
|
"WEBHOOK_NAME": "Webhook Name",
|
||||||
|
"NO_TRIGGER": "No Trigger",
|
||||||
|
"NAME_REQUIRED": "Name is required",
|
||||||
|
"NOTIFY_TYPE": "Notify Type",
|
||||||
|
"EVENT_TYPE": "Event Type",
|
||||||
|
"EVENT_TYPE_REQUIRED": "Require at least one event type"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Grup",
|
"GROUP": "Grup",
|
||||||
|
@ -361,8 +361,9 @@
|
|||||||
"DISABLED": "停用",
|
"DISABLED": "停用",
|
||||||
"OF": "共计",
|
"OF": "共计",
|
||||||
"ITEMS": "条记录",
|
"ITEMS": "条记录",
|
||||||
"LAST_TRIGGERED": "最近触发事件",
|
"LAST_TRIGGERED": "最近触发时间",
|
||||||
"EDIT_WEBHOOK": "Webhook 目标",
|
"EDIT_WEBHOOK": "编辑 Webhook",
|
||||||
|
"ADD_WEBHOOK": "新建 Webhook",
|
||||||
"CREATE_WEBHOOK": "创建 Webhooks",
|
"CREATE_WEBHOOK": "创建 Webhooks",
|
||||||
"EDIT_WEBHOOK_DESC": "指定接收 Webhook 通知的目标",
|
"EDIT_WEBHOOK_DESC": "指定接收 Webhook 通知的目标",
|
||||||
"CREATE_WEBHOOK_DESC": "为了启用 webhook, 请提供 Endpoint 和凭据以访问 Webhook 服务器。",
|
"CREATE_WEBHOOK_DESC": "为了启用 webhook, 请提供 Endpoint 和凭据以访问 Webhook 服务器。",
|
||||||
@ -376,10 +377,28 @@
|
|||||||
"SAVE_BUTTON": "保存",
|
"SAVE_BUTTON": "保存",
|
||||||
"TEST_ENDPOINT_SUCCESS": "测试连接成功。",
|
"TEST_ENDPOINT_SUCCESS": "测试连接成功。",
|
||||||
"TEST_ENDPOINT_FAILURE": "测试连接失败。",
|
"TEST_ENDPOINT_FAILURE": "测试连接失败。",
|
||||||
"ENABLED_WEBHOOK_TITLE": "启用项目的 Webhooks",
|
"ENABLED_WEBHOOK_TITLE": "启用 Webhook",
|
||||||
"ENABLED_WEBHOOK_SUMMARY": "你希望开启项目的 Webhooks 吗?",
|
"ENABLED_WEBHOOK_SUMMARY": "确认启用 webhook {{name}}?",
|
||||||
"DISABLED_WEBHOOK_TITLE": "停用项目的 Webhooks",
|
"DISABLED_WEBHOOK_TITLE": "停用 Webhook",
|
||||||
"DISABLED_WEBHOOK_SUMMARY": "你希望停用项目的 Webhooks 吗?"
|
"DISABLED_WEBHOOK_SUMMARY": "确认停用 webhook {{name}}?",
|
||||||
|
"DELETE_WEBHOOK_TITLE": "删除 Webhook(s)",
|
||||||
|
"DELETE_WEBHOOK_SUMMARY": "确认删除 webhook(s) {{names}}?",
|
||||||
|
"WEBHOOKS": "Webhooks",
|
||||||
|
"NEW_WEBHOOK": "新建 Webhook",
|
||||||
|
"ENABLE": "启用",
|
||||||
|
"DISABLE": "禁用",
|
||||||
|
"NAME": "名称",
|
||||||
|
"TARGET": "目标地址",
|
||||||
|
"EVENT_TYPES": "事件类型",
|
||||||
|
"DESCRIPTION": "简介",
|
||||||
|
"NO_WEBHOOK": "暂无 Webhook 记录",
|
||||||
|
"LAST_TRIGGER": "最新触发",
|
||||||
|
"WEBHOOK_NAME": "Webhook 名称",
|
||||||
|
"NO_TRIGGER": "暂无触发记录",
|
||||||
|
"NAME_REQUIRED": "名称为必填项",
|
||||||
|
"NOTIFY_TYPE": "通知类型",
|
||||||
|
"EVENT_TYPE": "事件类型",
|
||||||
|
"EVENT_TYPE_REQUIRED": "请至少选择一种事件类型"
|
||||||
},
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "组",
|
"GROUP": "组",
|
||||||
|
Loading…
Reference in New Issue
Block a user