forked from hazza/dspace-angular
Merged in CST-12867-ip-range-for-ldn-service (pull request #1239)
CST-12867 ip range for ldn service Approved-by: Stefano Maffei
This commit is contained in:
@@ -48,6 +48,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- In the IP range section -->
|
||||||
|
<div class="mb-5 mt-5">
|
||||||
|
<label for="lowerIp" class="font-weight-bold">{{ 'ldn-new-service.form.label.ip-range' | translate }}</label>
|
||||||
|
<div class="d-flex">
|
||||||
|
<input [class.invalid-field]="formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched"
|
||||||
|
[placeholder]="'ldn-new-service.form.placeholder.lowerIp' | translate" class="form-control mr-2"
|
||||||
|
formControlName="lowerIp"
|
||||||
|
id="lowerIp"
|
||||||
|
name="lowerIp"
|
||||||
|
type="text">
|
||||||
|
<input [class.invalid-field]="formModel.get('upperIp').invalid && formModel.get('upperIp').touched"
|
||||||
|
[placeholder]="'ldn-new-service.form.placeholder.upperIp' | translate" class="form-control"
|
||||||
|
formControlName="upperIp"
|
||||||
|
id="upperIp"
|
||||||
|
name="upperIp"
|
||||||
|
type="text">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="(formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched) || (formModel.get('upperIp').invalid && formModel.get('upperIp').touched)" class="error-text">
|
||||||
|
{{ 'ldn-new-service.form.error.ipRange' | translate }}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!(formModel.get('lowerIp').invalid && formModel.get('lowerIp').touched) || (formModel.get('upperIp').invalid && formModel.get('upperIp').touched)" class="text-muted">
|
||||||
|
{{ 'ldn-new-service.form.hint.ipRange' | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- In the ldnUrl section -->
|
<!-- In the ldnUrl section -->
|
||||||
<div class="mb-5 mt-5">
|
<div class="mb-5 mt-5">
|
||||||
<label for="ldnUrl" class="font-weight-bold">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
<label for="ldnUrl" class="font-weight-bold">{{ 'ldn-new-service.form.label.ldnUrl' | translate }}</label>
|
||||||
@@ -84,7 +110,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.inboundPattern' | translate }} </label>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern)">
|
<ng-container *ngIf="formModel.get('notifyServiceInboundPatterns')['controls'][0]?.value?.pattern">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,7 +171,7 @@
|
|||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern)">
|
*ngIf="formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern">
|
||||||
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
|
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
|
||||||
placement="top-start">
|
placement="top-start">
|
||||||
<div class="position-relative right-addon" role="combobox">
|
<div class="position-relative right-addon" role="combobox">
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
|
||||||
Input,
|
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
@@ -29,6 +26,7 @@ import {combineLatestWith, Observable, Subscription} from 'rxjs';
|
|||||||
import {PaginationService} from '../../../core/pagination/pagination.service';
|
import {PaginationService} from '../../../core/pagination/pagination.service';
|
||||||
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
import {FindListOptions} from '../../../core/data/find-list-options.model';
|
||||||
import {NotifyServicePattern} from '../ldn-services-model/ldn-service-patterns.model';
|
import {NotifyServicePattern} from '../ldn-services-model/ldn-service-patterns.model';
|
||||||
|
import { IpV4Validator } from '../../../shared/utils/ipV4.validator';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,35 +46,26 @@ import {NotifyServicePattern} from '../ldn-services-model/ldn-service-patterns.m
|
|||||||
})
|
})
|
||||||
export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
||||||
formModel: FormGroup;
|
formModel: FormGroup;
|
||||||
|
|
||||||
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
@ViewChild('confirmModal', {static: true}) confirmModal: TemplateRef<any>;
|
||||||
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
|
||||||
|
|
||||||
public inboundPatterns: string[] = notifyPatterns;
|
public inboundPatterns: string[] = notifyPatterns;
|
||||||
public isNewService: boolean;
|
public isNewService: boolean;
|
||||||
public areControlsInitialized: boolean;
|
public areControlsInitialized: boolean;
|
||||||
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
public itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
|
||||||
config: FindListOptions = Object.assign(new FindListOptions(), {
|
public config: FindListOptions = Object.assign(new FindListOptions(), {
|
||||||
elementsPerPage: 20
|
elementsPerPage: 20
|
||||||
});
|
});
|
||||||
|
public markedForDeletionInboundPattern: number[] = [];
|
||||||
|
public selectedInboundPatterns: string[];
|
||||||
|
public selectedInboundItemfilters: string[];
|
||||||
|
|
||||||
@Input() public name: string;
|
|
||||||
@Input() public description: string;
|
|
||||||
@Input() public url: string;
|
|
||||||
@Input() public ldnUrl: string;
|
|
||||||
@Input() public score: number;
|
|
||||||
@Input() public inboundPattern: string;
|
|
||||||
@Input() public constraint: string;
|
|
||||||
@Input() public automatic: boolean;
|
|
||||||
@Input() public headerKey: string;
|
|
||||||
@Output() submitForm: EventEmitter<any> = new EventEmitter();
|
|
||||||
@Output() cancelForm: EventEmitter<any> = new EventEmitter();
|
|
||||||
markedForDeletionInboundPattern: number[] = [];
|
|
||||||
selectedInboundPatterns: string[];
|
|
||||||
selectedInboundItemfilters: string[];
|
|
||||||
protected serviceId: string;
|
protected serviceId: string;
|
||||||
|
|
||||||
private deletedInboundPatterns: number[] = [];
|
private deletedInboundPatterns: number[] = [];
|
||||||
private modalRef: any;
|
private modalRef: any;
|
||||||
private service: LdnService;
|
private ldnService: LdnService;
|
||||||
private selectPatternDefaultLabeli18Key = 'ldn-service.form.label.placeholder.default-select';
|
private selectPatternDefaultLabeli18Key = 'ldn-service.form.label.placeholder.default-select';
|
||||||
private routeSubscription: Subscription;
|
private routeSubscription: Subscription;
|
||||||
|
|
||||||
@@ -99,6 +88,8 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
description: [''],
|
description: [''],
|
||||||
url: ['', Validators.required],
|
url: ['', Validators.required],
|
||||||
ldnUrl: ['', Validators.required],
|
ldnUrl: ['', Validators.required],
|
||||||
|
lowerIp: ['', [Validators.required, new IpV4Validator()]],
|
||||||
|
upperIp: ['', [Validators.required, new IpV4Validator()]],
|
||||||
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''],
|
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''],
|
||||||
constraintPattern: [''],
|
constraintPattern: [''],
|
||||||
enabled: [''],
|
enabled: [''],
|
||||||
@@ -139,15 +130,9 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
createService() {
|
createService() {
|
||||||
this.formModel.markAllAsTouched();
|
this.formModel.markAllAsTouched();
|
||||||
|
|
||||||
const name = this.formModel.get('name').value;
|
|
||||||
const url = this.formModel.get('url').value;
|
|
||||||
const score = this.formModel.get('score').value;
|
|
||||||
const ldnUrl = this.formModel.get('ldnUrl').value;
|
|
||||||
|
|
||||||
const hasInboundPattern = this.checkPatterns(this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
const hasInboundPattern = this.checkPatterns(this.formModel.get('notifyServiceInboundPatterns') as FormArray);
|
||||||
|
|
||||||
if (!name || !url || !ldnUrl || (!score && score !== 0) || this.formModel.get('score').invalid) {
|
if (this.formModel.invalid) {
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -177,9 +162,8 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
if (rd.hasSucceeded) {
|
if (rd.hasSucceeded) {
|
||||||
this.notificationService.success(this.translateService.get('ldn-service-notification.created.success.title'),
|
this.notificationService.success(this.translateService.get('ldn-service-notification.created.success.title'),
|
||||||
this.translateService.get('ldn-service-notification.created.success.body'));
|
this.translateService.get('ldn-service-notification.created.success.body'));
|
||||||
|
|
||||||
this.sendBack();
|
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
this.sendBack();
|
||||||
} else {
|
} else {
|
||||||
this.notificationService.error(this.translateService.get('ldn-service-notification.created.failure.title'),
|
this.notificationService.error(this.translateService.get('ldn-service-notification.created.failure.title'),
|
||||||
this.translateService.get('ldn-service-notification.created.failure.body'));
|
this.translateService.get('ldn-service-notification.created.failure.body'));
|
||||||
@@ -214,18 +198,21 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
).subscribe(
|
).subscribe(
|
||||||
(data: RemoteData<LdnService>) => {
|
(data: RemoteData<LdnService>) => {
|
||||||
if (data.hasSucceeded) {
|
if (data.hasSucceeded) {
|
||||||
this.service = data.payload;
|
this.ldnService = data.payload;
|
||||||
|
|
||||||
this.formModel.patchValue({
|
this.formModel.patchValue({
|
||||||
id: this.service.id,
|
id: this.ldnService.id,
|
||||||
name: this.service.name,
|
name: this.ldnService.name,
|
||||||
description: this.service.description,
|
description: this.ldnService.description,
|
||||||
url: this.service.url,
|
url: this.ldnService.url,
|
||||||
score: this.service.score, ldnUrl: this.service.ldnUrl,
|
score: this.ldnService.score,
|
||||||
type: this.service.type,
|
ldnUrl: this.ldnService.ldnUrl,
|
||||||
enabled: this.service.enabled
|
type: this.ldnService.type,
|
||||||
|
enabled: this.ldnService.enabled,
|
||||||
|
lowerIp: this.ldnService.lowerIp,
|
||||||
|
upperIp: this.ldnService.upperIp,
|
||||||
});
|
});
|
||||||
this.filterPatternObjectsAndPickLabel('notifyServiceInboundPatterns');
|
this.filterPatternObjectsAndAssignLabel('notifyServiceInboundPatterns');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -235,11 +222,11 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
* Filters pattern objects, initializes form groups, assigns labels, and adds them to the specified form array so the correct string is shown in the dropdown..
|
* Filters pattern objects, initializes form groups, assigns labels, and adds them to the specified form array so the correct string is shown in the dropdown..
|
||||||
* @param formArrayName - The name of the form array to be populated
|
* @param formArrayName - The name of the form array to be populated
|
||||||
*/
|
*/
|
||||||
filterPatternObjectsAndPickLabel(formArrayName: string) {
|
filterPatternObjectsAndAssignLabel(formArrayName: string) {
|
||||||
const PatternsArray = this.formModel.get(formArrayName) as FormArray;
|
const PatternsArray = this.formModel.get(formArrayName) as FormArray;
|
||||||
PatternsArray.clear();
|
PatternsArray.clear();
|
||||||
let servicesToUse;
|
|
||||||
servicesToUse = this.service.notifyServiceInboundPatterns;
|
let servicesToUse = this.ldnService.notifyServiceInboundPatterns;
|
||||||
|
|
||||||
servicesToUse.forEach((patternObj: NotifyServicePattern) => {
|
servicesToUse.forEach((patternObj: NotifyServicePattern) => {
|
||||||
let patternFormGroup;
|
let patternFormGroup;
|
||||||
@@ -253,8 +240,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
PatternsArray.push(patternFormGroup);
|
PatternsArray.push(patternFormGroup);
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,6 +254,8 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
this.createReplaceOperation(patchOperations, 'ldnUrl', '/ldnurl');
|
this.createReplaceOperation(patchOperations, 'ldnUrl', '/ldnurl');
|
||||||
this.createReplaceOperation(patchOperations, 'url', '/url');
|
this.createReplaceOperation(patchOperations, 'url', '/url');
|
||||||
this.createReplaceOperation(patchOperations, 'score', '/score');
|
this.createReplaceOperation(patchOperations, 'score', '/score');
|
||||||
|
this.createReplaceOperation(patchOperations, 'lowerIp', '/lowerIp');
|
||||||
|
this.createReplaceOperation(patchOperations, 'upperIp', '/upperIp');
|
||||||
|
|
||||||
this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
|
this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
|
||||||
this.deletedInboundPatterns.forEach(index => {
|
this.deletedInboundPatterns.forEach(index => {
|
||||||
@@ -342,11 +329,10 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
value: newStatus,
|
value: newStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ldnServicesService.patch(this.service, [patchOperation]).pipe(
|
this.ldnServicesService.patch(this.ldnService, [patchOperation]).pipe(
|
||||||
getFirstCompletedRemoteData()
|
getFirstCompletedRemoteData()
|
||||||
).subscribe(
|
).subscribe(
|
||||||
() => {
|
() => {
|
||||||
|
|
||||||
this.formModel.get('enabled').setValue(newStatus);
|
this.formModel.get('enabled').setValue(newStatus);
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
}
|
}
|
||||||
@@ -402,7 +388,7 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ldnServicesService.patch(this.service, patchOperations).pipe(
|
this.ldnServicesService.patch(this.ldnService, patchOperations).pipe(
|
||||||
getFirstCompletedRemoteData()
|
getFirstCompletedRemoteData()
|
||||||
).subscribe(
|
).subscribe(
|
||||||
(rd: RemoteData<LdnService>) => {
|
(rd: RemoteData<LdnService>) => {
|
||||||
|
@@ -10,6 +10,8 @@ export const mockLdnService: LdnService = {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
score: 0,
|
score: 0,
|
||||||
id: 1,
|
id: 1,
|
||||||
|
lowerIp: '192.0.2.146',
|
||||||
|
upperIp: '192.0.2.255',
|
||||||
name: 'Service Name',
|
name: 'Service Name',
|
||||||
description: 'Service Description',
|
description: 'Service Description',
|
||||||
url: 'Service URL',
|
url: 'Service URL',
|
||||||
@@ -45,6 +47,8 @@ export const mockLdnServices: LdnService[] = [{
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
score: 0,
|
score: 0,
|
||||||
id: 1,
|
id: 1,
|
||||||
|
lowerIp: '192.0.2.146',
|
||||||
|
upperIp: '192.0.2.255',
|
||||||
name: 'Service Name',
|
name: 'Service Name',
|
||||||
description: 'Service Description',
|
description: 'Service Description',
|
||||||
url: 'Service URL',
|
url: 'Service URL',
|
||||||
@@ -75,6 +79,8 @@ export const mockLdnServices: LdnService[] = [{
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
score: 0,
|
score: 0,
|
||||||
id: 2,
|
id: 2,
|
||||||
|
lowerIp: '192.0.2.146',
|
||||||
|
upperIp: '192.0.2.255',
|
||||||
name: 'Service Name',
|
name: 'Service Name',
|
||||||
description: 'Service Description',
|
description: 'Service Description',
|
||||||
url: 'Service URL',
|
url: 'Service URL',
|
||||||
|
@@ -0,0 +1,89 @@
|
|||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { LdnItemfiltersService } from './ldn-itemfilters-data.service';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { RequestEntry } from '../../../core/data/request-entry.model';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { RequestEntryState } from '../../../core/data/request-entry-state.model';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
|
import { FindAllData } from '../../../core/data/base/find-all-data';
|
||||||
|
import { testFindAllDataImplementation } from '../../../core/data/base/find-all-data.spec';
|
||||||
|
|
||||||
|
describe('LdnItemfiltersService test', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let service: LdnItemfiltersService;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let rdbService: RemoteDataBuildService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let notificationsService: NotificationsService;
|
||||||
|
let responseCacheEntry: RequestEntry;
|
||||||
|
|
||||||
|
const endpointURL = `https://rest.api/rest/api/ldn/itemfilters`;
|
||||||
|
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
|
||||||
|
|
||||||
|
const remoteDataMocks = {
|
||||||
|
Success: new RemoteData(null, null, null, RequestEntryState.Success, null, null, 200),
|
||||||
|
};
|
||||||
|
|
||||||
|
function initTestService() {
|
||||||
|
return new LdnItemfiltersService(
|
||||||
|
requestService,
|
||||||
|
rdbService,
|
||||||
|
objectCache,
|
||||||
|
halService,
|
||||||
|
notificationsService,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
|
||||||
|
objectCache = {} as ObjectCacheService;
|
||||||
|
notificationsService = {} as NotificationsService;
|
||||||
|
responseCacheEntry = new RequestEntry();
|
||||||
|
responseCacheEntry.request = { href: 'https://rest.api/' } as any;
|
||||||
|
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||||
|
|
||||||
|
requestService = jasmine.createSpyObj('requestService', {
|
||||||
|
generateRequestId: requestUUID,
|
||||||
|
send: true,
|
||||||
|
removeByHrefSubstring: {},
|
||||||
|
getByHref: of(responseCacheEntry),
|
||||||
|
getByUUID: of(responseCacheEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getEndpoint: of(endpointURL)
|
||||||
|
});
|
||||||
|
|
||||||
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
|
buildSingle: createSuccessfulRemoteDataObject$({}, 500),
|
||||||
|
buildList: cold('a', { a: remoteDataMocks.Success })
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
service = initTestService();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('composition', () => {
|
||||||
|
const initFindAllService = () => new LdnItemfiltersService(null, null, null, null, null) as unknown as FindAllData<any>;
|
||||||
|
testFindAllDataImplementation(initFindAllService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get endpoint', () => {
|
||||||
|
it('should retrieve correct endpoint', (done) => {
|
||||||
|
service.getEndpoint().subscribe(() => {
|
||||||
|
expect(halService.getEndpoint).toHaveBeenCalledWith('itemfilters');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,116 @@
|
|||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { RequestService } from '../../../core/data/request.service';
|
||||||
|
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
|
||||||
|
import { ObjectCacheService } from '../../../core/cache/object-cache.service';
|
||||||
|
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import { RequestEntry } from '../../../core/data/request-entry.model';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { RequestEntryState } from '../../../core/data/request-entry-state.model';
|
||||||
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { RestResponse } from '../../../core/cache/response.models';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
|
||||||
|
import { FindAllData } from '../../../core/data/base/find-all-data';
|
||||||
|
import { testFindAllDataImplementation } from '../../../core/data/base/find-all-data.spec';
|
||||||
|
import { LdnServicesService } from './ldn-services-data.service';
|
||||||
|
import { testDeleteDataImplementation } from '../../../core/data/base/delete-data.spec';
|
||||||
|
import { DeleteData } from '../../../core/data/base/delete-data';
|
||||||
|
import { testSearchDataImplementation } from '../../../core/data/base/search-data.spec';
|
||||||
|
import { SearchData } from '../../../core/data/base/search-data';
|
||||||
|
import { testPatchDataImplementation } from '../../../core/data/base/patch-data.spec';
|
||||||
|
import { PatchData } from '../../../core/data/base/patch-data';
|
||||||
|
import { CreateData } from '../../../core/data/base/create-data';
|
||||||
|
import { testCreateDataImplementation } from '../../../core/data/base/create-data.spec';
|
||||||
|
import { FindListOptions } from '../../../core/data/find-list-options.model';
|
||||||
|
import { RequestParam } from '../../../core/cache/models/request-param.model';
|
||||||
|
import { mockLdnService } from '../ldn-service-serviceMock/ldnServicesRD$-mock';
|
||||||
|
import { createPaginatedList } from '../../../shared/testing/utils.test';
|
||||||
|
|
||||||
|
|
||||||
|
describe('LdnServicesService test', () => {
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
let service: LdnServicesService;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let rdbService: RemoteDataBuildService;
|
||||||
|
let objectCache: ObjectCacheService;
|
||||||
|
let halService: HALEndpointService;
|
||||||
|
let notificationsService: NotificationsService;
|
||||||
|
let responseCacheEntry: RequestEntry;
|
||||||
|
|
||||||
|
const endpointURL = `https://rest.api/rest/api/ldn/ldnservices`;
|
||||||
|
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
|
||||||
|
|
||||||
|
const remoteDataMocks = {
|
||||||
|
Success: new RemoteData(null, null, null, RequestEntryState.Success, null, null, 200),
|
||||||
|
};
|
||||||
|
|
||||||
|
function initTestService() {
|
||||||
|
return new LdnServicesService(
|
||||||
|
requestService,
|
||||||
|
rdbService,
|
||||||
|
objectCache,
|
||||||
|
halService,
|
||||||
|
notificationsService,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
|
||||||
|
objectCache = {} as ObjectCacheService;
|
||||||
|
notificationsService = {} as NotificationsService;
|
||||||
|
responseCacheEntry = new RequestEntry();
|
||||||
|
responseCacheEntry.request = { href: 'https://rest.api/' } as any;
|
||||||
|
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||||
|
|
||||||
|
requestService = jasmine.createSpyObj('requestService', {
|
||||||
|
generateRequestId: requestUUID,
|
||||||
|
send: true,
|
||||||
|
removeByHrefSubstring: {},
|
||||||
|
getByHref: observableOf(responseCacheEntry),
|
||||||
|
getByUUID: observableOf(responseCacheEntry),
|
||||||
|
});
|
||||||
|
|
||||||
|
halService = jasmine.createSpyObj('halService', {
|
||||||
|
getEndpoint: observableOf(endpointURL)
|
||||||
|
});
|
||||||
|
|
||||||
|
rdbService = jasmine.createSpyObj('rdbService', {
|
||||||
|
buildSingle: createSuccessfulRemoteDataObject$({}, 500),
|
||||||
|
buildList: cold('a', { a: remoteDataMocks.Success })
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
service = initTestService();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('composition', () => {
|
||||||
|
const initFindAllService = () => new LdnServicesService(null, null, null, null, null) as unknown as FindAllData<any>;
|
||||||
|
const initDeleteService = () => new LdnServicesService(null, null, null, null, null) as unknown as DeleteData<any>;
|
||||||
|
const initSearchService = () => new LdnServicesService(null, null, null, null, null) as unknown as SearchData<any>;
|
||||||
|
const initPatchService = () => new LdnServicesService(null, null, null, null, null) as unknown as PatchData<any>;
|
||||||
|
const initCreateService = () => new LdnServicesService(null, null, null, null, null) as unknown as CreateData<any>;
|
||||||
|
|
||||||
|
testFindAllDataImplementation(initFindAllService);
|
||||||
|
testDeleteDataImplementation(initDeleteService);
|
||||||
|
testSearchDataImplementation(initSearchService);
|
||||||
|
testPatchDataImplementation(initPatchService);
|
||||||
|
testCreateDataImplementation(initCreateService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('custom methods', () => {
|
||||||
|
it('should find service by inbound pattern', (done) => {
|
||||||
|
const params = [new RequestParam('pattern', 'testPattern')];
|
||||||
|
const findListOptions = Object.assign(new FindListOptions(), {}, {searchParams: params});
|
||||||
|
spyOn(service, 'searchBy').and.returnValue(observableOf(null));
|
||||||
|
spyOn((service as any).searchData, 'searchBy').and.returnValue(createSuccessfulRemoteDataObject$(createPaginatedList([mockLdnService])));
|
||||||
|
|
||||||
|
service.findByInboundPattern('testPattern').subscribe(() => {
|
||||||
|
expect(service.searchBy).toHaveBeenCalledWith('byInboundPattern', findListOptions, undefined, undefined );
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -29,8 +29,6 @@ import {Operation} from 'fast-json-patch';
|
|||||||
import {RestRequestMethod} from '../../../core/data/rest-request-method';
|
import {RestRequestMethod} from '../../../core/data/rest-request-method';
|
||||||
import {CreateData, CreateDataImpl} from '../../../core/data/base/create-data';
|
import {CreateData, CreateDataImpl} from '../../../core/data/base/create-data';
|
||||||
import {LdnServiceConstrain} from '../ldn-services-model/ldn-service.constrain.model';
|
import {LdnServiceConstrain} from '../ldn-services-model/ldn-service.constrain.model';
|
||||||
import {getFirstCompletedRemoteData} from '../../../core/shared/operators';
|
|
||||||
import {hasValue} from '../../../shared/empty.util';
|
|
||||||
import {SearchDataImpl} from '../../../core/data/base/search-data';
|
import {SearchDataImpl} from '../../../core/data/base/search-data';
|
||||||
import {RequestParam} from '../../../core/cache/models/request-param.model';
|
import {RequestParam} from '../../../core/cache/models/request-param.model';
|
||||||
|
|
||||||
@@ -77,10 +75,11 @@ export class LdnServicesService extends IdentifiableDataService<LdnService> impl
|
|||||||
* Creates an LDN service by sending a POST request to the REST API.
|
* Creates an LDN service by sending a POST request to the REST API.
|
||||||
*
|
*
|
||||||
* @param {LdnService} object - The LDN service object to be created.
|
* @param {LdnService} object - The LDN service object to be created.
|
||||||
|
* @param params Array with additional params to combine with query string
|
||||||
* @returns {Observable<RemoteData<LdnService>>} - Observable containing the result of the creation operation.
|
* @returns {Observable<RemoteData<LdnService>>} - Observable containing the result of the creation operation.
|
||||||
*/
|
*/
|
||||||
create(object: LdnService): Observable<RemoteData<LdnService>> {
|
create(object: LdnService, ...params: RequestParam[]): Observable<RemoteData<LdnService>> {
|
||||||
return this.createData.create(object);
|
return this.createData.create(object, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,7 +148,7 @@ export class LdnServicesService extends IdentifiableDataService<LdnService> impl
|
|||||||
findByInboundPattern(pattern: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
findByInboundPattern(pattern: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
||||||
const params = [new RequestParam('pattern', pattern)];
|
const params = [new RequestParam('pattern', pattern)];
|
||||||
const findListOptions = Object.assign(new FindListOptions(), options, {searchParams: params});
|
const findListOptions = Object.assign(new FindListOptions(), options, {searchParams: params});
|
||||||
return this.searchData.searchBy(this.findByPatternEndpoint, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
return this.searchBy(this.findByPatternEndpoint, findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,6 +173,25 @@ export class LdnServicesService extends IdentifiableDataService<LdnService> impl
|
|||||||
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a new FindListRequest with given search method
|
||||||
|
*
|
||||||
|
* @param searchMethod The search method for the object
|
||||||
|
* @param options The [[FindListOptions]] object
|
||||||
|
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||||
|
* no valid cached version. Defaults to true
|
||||||
|
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||||
|
* requested after the response becomes stale
|
||||||
|
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
|
||||||
|
* {@link HALLink}s should be automatically resolved
|
||||||
|
* @return {Observable<RemoteData<PaginatedList<T>>}
|
||||||
|
* Return an observable that emits response from the server
|
||||||
|
*/
|
||||||
|
public searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<LdnService>[]): Observable<RemoteData<PaginatedList<LdnService>>> {
|
||||||
|
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
|
}
|
||||||
|
|
||||||
public invoke(serviceName: string, serviceId: string, parameters: LdnServiceConstrain[], files: File[]): Observable<RemoteData<LdnService>> {
|
public invoke(serviceName: string, serviceId: string, parameters: LdnServiceConstrain[], files: File[]): Observable<RemoteData<LdnService>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
this.getBrowseEndpoint().pipe(
|
this.getBrowseEndpoint().pipe(
|
||||||
@@ -188,15 +206,6 @@ export class LdnServicesService extends IdentifiableDataService<LdnService> impl
|
|||||||
return this.rdbService.buildFromRequestUUID<LdnService>(requestId);
|
return this.rdbService.buildFromRequestUUID<LdnService>(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ldnServiceWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
|
||||||
return this.findById(scriptName).pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
map((rd: RemoteData<LdnService>) => {
|
|
||||||
return hasValue(rd.payload);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getInvocationFormData(constrain: LdnServiceConstrain[], files: File[]): FormData {
|
private getInvocationFormData(constrain: LdnServiceConstrain[], files: File[]): FormData {
|
||||||
const form: FormData = new FormData();
|
const form: FormData = new FormData();
|
||||||
form.set('properties', JSON.stringify(constrain));
|
form.set('properties', JSON.stringify(constrain));
|
||||||
|
@@ -46,6 +46,12 @@ export class LdnService extends CacheableObject {
|
|||||||
@autoserialize
|
@autoserialize
|
||||||
ldnUrl: string;
|
ldnUrl: string;
|
||||||
|
|
||||||
|
@autoserialize
|
||||||
|
lowerIp: string;
|
||||||
|
|
||||||
|
@autoserialize
|
||||||
|
upperIp: string;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
notifyServiceInboundPatterns?: NotifyServicePattern[];
|
notifyServiceInboundPatterns?: NotifyServicePattern[];
|
||||||
|
|
||||||
|
@@ -285,6 +285,7 @@ import { NgxPaginationModule } from 'ngx-pagination';
|
|||||||
import { SplitPipe } from './utils/split.pipe';
|
import { SplitPipe } from './utils/split.pipe';
|
||||||
import { ThemedUserMenuComponent } from './auth-nav-menu/user-menu/themed-user-menu.component';
|
import { ThemedUserMenuComponent } from './auth-nav-menu/user-menu/themed-user-menu.component';
|
||||||
import { ThemedLangSwitchComponent } from './lang-switch/themed-lang-switch.component';
|
import { ThemedLangSwitchComponent } from './lang-switch/themed-lang-switch.component';
|
||||||
|
import { IpV4Validator } from './utils/ipV4.validator';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -495,6 +496,7 @@ const DIRECTIVES = [
|
|||||||
MetadataFieldValidator,
|
MetadataFieldValidator,
|
||||||
HoverClassDirective,
|
HoverClassDirective,
|
||||||
ContextHelpDirective,
|
ContextHelpDirective,
|
||||||
|
IpV4Validator
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
36
src/app/shared/utils/ipV4.validator.spec.ts
Normal file
36
src/app/shared/utils/ipV4.validator.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { IpV4Validator } from './ipV4.validator';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
|
||||||
|
describe('IpV4 validator', () => {
|
||||||
|
|
||||||
|
let ipV4Validator: IpV4Validator;
|
||||||
|
const validIp = '192.168.0.1';
|
||||||
|
const formGroup = new UntypedFormGroup({
|
||||||
|
ip: new UntypedFormControl(''),
|
||||||
|
});
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
IpV4Validator,
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
ipV4Validator = TestBed.inject(IpV4Validator);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null for valid ipV4', () => {
|
||||||
|
formGroup.controls.ip.setValue(validIp);
|
||||||
|
expect(ipV4Validator.validate(formGroup.controls.ip as UntypedFormControl)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return {isValidIp: false} for invalid Ip', () => {
|
||||||
|
formGroup.controls.ip.setValue('100.260.45.1');
|
||||||
|
expect(ipV4Validator.validate(formGroup.controls.ip as UntypedFormControl)).toEqual({isValidIp: false});
|
||||||
|
formGroup.controls.ip.setValue('100');
|
||||||
|
expect(ipV4Validator.validate(formGroup.controls.ip as UntypedFormControl)).toEqual({isValidIp: false});
|
||||||
|
formGroup.controls.ip.setValue('testString');
|
||||||
|
expect(ipV4Validator.validate(formGroup.controls.ip as UntypedFormControl)).toEqual({isValidIp: false});
|
||||||
|
});
|
||||||
|
});
|
26
src/app/shared/utils/ipV4.validator.ts
Normal file
26
src/app/shared/utils/ipV4.validator.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import {Directive} from '@angular/core';
|
||||||
|
import {NG_VALIDATORS, Validator, UntypedFormControl} from '@angular/forms';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||||
|
selector: '[ipV4format]',
|
||||||
|
providers: [
|
||||||
|
{ provide: NG_VALIDATORS, useExisting: IpV4Validator, multi: true },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Validator to validate if an Ip is in the right format
|
||||||
|
*/
|
||||||
|
export class IpV4Validator implements Validator {
|
||||||
|
validate(formControl: UntypedFormControl): {[key: string]: boolean} | null {
|
||||||
|
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||||
|
const ipValue = formControl.value;
|
||||||
|
const ipParts = ipValue?.split('.');
|
||||||
|
|
||||||
|
if (ipv4Regex.test(ipValue) && ipParts.every(part => parseInt(part, 10) <= 255)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {isValidIp: false};
|
||||||
|
}
|
||||||
|
}
|
@@ -935,11 +935,14 @@
|
|||||||
"ldn-new-service.form.label.name": "Name",
|
"ldn-new-service.form.label.name": "Name",
|
||||||
"ldn-new-service.form.label.description": "Description",
|
"ldn-new-service.form.label.description": "Description",
|
||||||
"ldn-new-service.form.label.url": "Service URL",
|
"ldn-new-service.form.label.url": "Service URL",
|
||||||
|
"ldn-new-service.form.label.ip-range": "Service IP range",
|
||||||
"ldn-new-service.form.label.score": "Level of trust",
|
"ldn-new-service.form.label.score": "Level of trust",
|
||||||
"ldn-new-service.form.label.ldnUrl": "LDN Inbox URL",
|
"ldn-new-service.form.label.ldnUrl": "LDN Inbox URL",
|
||||||
"ldn-new-service.form.placeholder.name": "Please provide service name",
|
"ldn-new-service.form.placeholder.name": "Please provide service name",
|
||||||
"ldn-new-service.form.placeholder.description": "Please provide a description regarding your service",
|
"ldn-new-service.form.placeholder.description": "Please provide a description regarding your service",
|
||||||
"ldn-new-service.form.placeholder.url": "Please input the URL for users to check out more information about the service",
|
"ldn-new-service.form.placeholder.url": "Please input the URL for users to check out more information about the service",
|
||||||
|
"ldn-new-service.form.placeholder.lowerIp": "IPv4 range lower bound",
|
||||||
|
"ldn-new-service.form.placeholder.upperIp": "IPv4 range upper bound",
|
||||||
"ldn-new-service.form.placeholder.ldnUrl": "Please specify the URL of the LDN Inbox",
|
"ldn-new-service.form.placeholder.ldnUrl": "Please specify the URL of the LDN Inbox",
|
||||||
"ldn-new-service.form.placeholder.score": "Please enter a value between 0 and 1. Use the “.” as decimal separator",
|
"ldn-new-service.form.placeholder.score": "Please enter a value between 0 and 1. Use the “.” as decimal separator",
|
||||||
"ldn-service.form.label.placeholder.default-select": "Select a pattern",
|
"ldn-service.form.label.placeholder.default-select": "Select a pattern",
|
||||||
@@ -1001,6 +1004,8 @@
|
|||||||
"ldn-new-service.form.label.automatic": "Automatic",
|
"ldn-new-service.form.label.automatic": "Automatic",
|
||||||
"ldn-new-service.form.error.name": "Name is required",
|
"ldn-new-service.form.error.name": "Name is required",
|
||||||
"ldn-new-service.form.error.url": "URL is required",
|
"ldn-new-service.form.error.url": "URL is required",
|
||||||
|
"ldn-new-service.form.error.ipRange": "Please enter a valid IP range",
|
||||||
|
"ldn-new-service.form.hint.ipRange": "Please enter a valid IpV4 in both range bounds (note: for single IP, please enter the same value in both fields)",
|
||||||
"ldn-new-service.form.error.ldnurl": "LDN URL is required",
|
"ldn-new-service.form.error.ldnurl": "LDN URL is required",
|
||||||
"ldn-new-service.form.error.patterns": "At least a pattern is required",
|
"ldn-new-service.form.error.patterns": "At least a pattern is required",
|
||||||
"ldn-new-service.form.error.score": "Please enter a valid score (between 0 and 1). Use the “.” as decimal separator",
|
"ldn-new-service.form.error.score": "Please enter a valid score (between 0 and 1). Use the “.” as decimal separator",
|
||||||
|
Reference in New Issue
Block a user