Modify cron regex for UI (#14254)

Signed-off-by: sshijun <sshijun@vmware.com>
This commit is contained in:
Will Sun 2021-02-18 15:09:10 +08:00 committed by GitHub
parent 202342cf0c
commit 74b6bfe731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 43 additions and 36 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "harbor", "name": "harbor",
"version": "2.2.0", "version": "2.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -5391,6 +5391,11 @@
"object-assign": "^4.1.1" "object-assign": "^4.1.1"
} }
}, },
"cron-validator": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/cron-validator/-/cron-validator-1.2.1.tgz",
"integrity": "sha512-RqdpGSokGFICPc8qAkT38aXqZLLanXghQTK2q7a2x2FabSwDd2ARrazd5ElEWAXzToUcMG4cZIwDH+5RM0q1mA=="
},
"cross-spawn": { "cross-spawn": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",

View File

@ -46,6 +46,7 @@
"@webcomponents/webcomponentsjs": "^2.0.0", "@webcomponents/webcomponentsjs": "^2.0.0",
"buffer": "^5.2.1", "buffer": "^5.2.1",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"cron-validator": "^1.2.1",
"intl": "^1.2.5", "intl": "^1.2.5",
"jasmine-core": "^3.3.0", "jasmine-core": "^3.3.0",
"mutationobserver-shim": "^0.3.2", "mutationobserver-shim": "^0.3.2",

View File

@ -176,15 +176,16 @@
</select> </select>
</div> </div>
<div formGroupName="trigger_settings" class="clr-form-control"> <div formGroupName="trigger_settings" class="clr-form-control">
<div [hidden]="isNotSchedule()"> <div class="flex" [hidden]="isNotSchedule()">
<label class="required">Cron String</label> <label for="targetCron" class="required">Cron String</label>
<label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-sm tooltip-top-right" <div class="clr-control-container" [class.clr-error]="cronInputShouldShowError()">
[class.invalid]="!isNotSchedule() && cronTouched && !cronInputValid(ruleForm.value.trigger?.trigger_settings?.cron || '')"> <div class="clr-input-wrapper">
<input (input)="inputInvalid($event)" type="text" name=targetCron id="targetCron" required class="form-control cron-input clr-input" formControlName="cron"> <input autocomplete="off" (input)="inputInvalid($event)" type="text" name=targetCron id="targetCron" required class="form-control cron-input clr-input" formControlName="cron">
<span class="tooltip-content"> </div>
<clr-control-error *ngIf="cronInputShouldShowError()">
{{'TOOLTIP.CRON_REQUIRED' | translate }} {{'TOOLTIP.CRON_REQUIRED' | translate }}
</span> </clr-control-error>
</label> </div>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-left top-7 cron-tooltip"> <a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-top-left top-7 cron-tooltip">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon> <clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<div class="tooltip-content table-box"> <div class="tooltip-content table-box">

View File

@ -283,4 +283,6 @@ clr-modal {
.select-width { .select-width {
min-width:11rem; min-width:11rem;
} }
.flex {
display: flex;
}

View File

@ -183,7 +183,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
!this.isRuleNameValid || !this.isRuleNameValid ||
(!this.isPushMode && !sourceRegistry || (!this.isPushMode && !sourceRegistry ||
this.isPushMode && !destRegistry) this.isPushMode && !destRegistry)
|| !(!this.isNotSchedule() && cron && this.cronInputValid(this.ruleForm.value.trigger.trigger_settings.cron || '') || !(!this.isNotSchedule() && cron && cronRegex(this.ruleForm.value.trigger.trigger_settings.cron || '')
|| this.isNotSchedule())); || this.isNotSchedule()));
} }
@ -545,19 +545,13 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
} }
return filtersArray; return filtersArray;
} }
cronInputValid(cronValue): boolean { cronInputShouldShowError(): boolean {
return cronRegex(cronValue); return this.ruleForm && this.ruleForm.get('trigger')
} && this.ruleForm.get('trigger').get('trigger_settings')
get cronTouched(): boolean { && this.ruleForm.get('trigger').get('trigger_settings').get('cron')
let triggerControl = this.ruleForm.controls.trigger as FormGroup; && (this.ruleForm.get('trigger').get('trigger_settings').get('cron').touched
if (!triggerControl) { || this.ruleForm.get('trigger').get('trigger_settings').get('cron').dirty)
return false; && !cronRegex(this.ruleForm.get('trigger').get('trigger_settings').get('cron').value);
}
let trigger_settingsControls = triggerControl.controls.trigger_settings as FormGroup;
if (!trigger_settingsControls) {
return false;
}
return trigger_settingsControls.controls.cron.touched || trigger_settingsControls.controls.cron.dirty;
} }
stickLabel(value, index) { stickLabel(value, index) {
value.select = !value.select; value.select = !value.select;
@ -582,6 +576,10 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
if (!e.target.value || (e.target.value && e.target.value.indexOf(PREFIX)) !== 0) { if (!e.target.value || (e.target.value && e.target.value.indexOf(PREFIX)) !== 0) {
e.target.value = PREFIX; e.target.value = PREFIX;
} }
e.target.value = e.target.value.replace(/\s+/g, ' ');
if (e.target.value && e.target.value.split(/\s+/g).length > 6) {
e.target.value = e.target.value.trim();
}
} }
} }
} }

View File

@ -40,7 +40,7 @@
<div class="height-1rem clr-input-wrapper"> <div class="height-1rem clr-input-wrapper">
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.clr-error]="dateInvalid" <label for="targetCron" aria-haspopup="true" role="tooltip" [class.clr-error]="dateInvalid"
class="tooltip tooltip-validation tooltip-md tooltip-top-left cron-label"> class="tooltip tooltip-validation tooltip-md tooltip-top-left cron-label">
<input type="text" (blur)="blurInvalid()" (input)="inputInvalid($event)" name="targetCron" id="targetCron" <input autocomplete="off" type="text" (blur)="blurInvalid()" (input)="inputInvalid($event)" name="targetCron" id="targetCron"
#cronStringInput="ngModel" required class="clr-input form-control ml-1" [(ngModel)]="cronString"> #cronStringInput="ngModel" required class="clr-input form-control ml-1" [(ngModel)]="cronString">
<clr-tooltip> <clr-tooltip>
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon> <clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>

View File

@ -3,8 +3,9 @@ import { HttpHeaders } from '@angular/common/http';
import { RequestQueryParams } from '../services'; import { RequestQueryParams } from '../services';
import { DebugElement } from '@angular/core'; import { DebugElement } from '@angular/core';
import { Comparator, State, HttpOptionInterface, HttpOptionTextInterface, QuotaUnitInterface } from '../services'; import { Comparator, State, HttpOptionInterface, HttpOptionTextInterface, QuotaUnitInterface } from '../services';
import { QuotaUnits, StorageMultipleConstant, LimitCount } from '../entities/shared.const'; import { QuotaUnits, StorageMultipleConstant } from '../entities/shared.const';
import { AbstractControl } from "@angular/forms"; import { AbstractControl } from "@angular/forms";
import { isValidCron } from 'cron-validator';
/** /**
* Api levels * Api levels
*/ */
@ -463,17 +464,16 @@ export function getChanges(original: any, afterChange: any): { [key: string]: an
return changes; return changes;
} }
/**
* validate cron expressions
* @param testValue
*/
export function cronRegex(testValue: any): boolean { export function cronRegex(testValue: any): boolean {
const regSecond = "^((([0-9])*|(\\*))(\\-|\\,|\\/)?([0-9])*)*\\s+"; // must have 6 fields
const regMinute = "((([0-9])*|(\\*))(\\-|\\,|\\/)?([0-9])*)*\\s+"; if (testValue && testValue.trim().split(/\s+/g).length < 6) {
const regHour = "((([0-9])*|(\\*))(\\-|\\,|\\/)?([0-9])*)*\\s+"; return false;
const regDay = "((([0-9])*|(\\*|\\?))(\\-|\\,|\\/)?([0-9])*)*\\s+"; }
const regMonth = "((([0-9a-zA-Z])*|(\\*))(\\-|\\,|\\/)?([0-9a-zA-Z])*)*\\s+"; return isValidCron(testValue, {seconds: true, alias: true, allowBlankDay: true});
const regWeek = "(((([0-9a-zA-Z])*|(\\*|\\?))(\\-|\\,|\\/)?([0-9a-zA-Z])*))*(|\\s)+";
const regYear = "((([0-9])*|(\\*|\\?))(\\-|\\,|\\/)?([0-9])*)$";
const regEx = regSecond + regMinute + regHour + regDay + regMonth + regWeek + regYear;
let reg = new RegExp(regEx, "i");
return reg.test(testValue.trim());
} }
/** /**