Merge branch 'coar-notify-7' into coar-notify-7-part-two

This commit is contained in:
Stefano Maffei
2024-01-12 11:05:07 +01:00
11 changed files with 234 additions and 361 deletions

View File

@@ -1,32 +1,45 @@
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {I18nBreadcrumbResolver} from 'src/app/core/breadcrumbs/i18n-breadcrumb.resolver';
import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component';
import {LdnServiceFormComponent} from './ldn-service-form/ldn-service-form.component';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
import { NavigationBreadcrumbResolver } from '../../core/breadcrumbs/navigation-breadcrumb.resolver';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
const moduleRoutes: Routes = [
{
path: '',
pathMatch: 'full',
component: LdnServicesOverviewComponent,
resolve: {breadcrumb: I18nBreadcrumbResolver},
data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'},
},
{
path: 'new',
resolve: {breadcrumb: NavigationBreadcrumbResolver},
component: LdnServiceFormComponent,
data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'}
},
{
path: 'edit/:serviceId',
resolve: {breadcrumb: NavigationBreadcrumbResolver},
component: LdnServiceFormComponent,
data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'}
},
];
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
pathMatch: 'full',
component: LdnServicesOverviewComponent,
resolve: {breadcrumb: I18nBreadcrumbResolver},
data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'},
},
{
path: 'new',
resolve: {breadcrumb: I18nBreadcrumbResolver},
component: LdnServiceFormComponent,
data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'}
},
{
path: 'edit/:serviceId',
resolve: {breadcrumb: I18nBreadcrumbResolver},
component: LdnServiceFormComponent,
data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'}
},
]),
RouterModule.forChild(moduleRoutes.map(route => {
return {...route, data: {
...route.data,
relatedRoutes: moduleRoutes.filter(relatedRoute => relatedRoute.path !== route.path)
.map((relatedRoute) => {
return {path: relatedRoute.path, data: relatedRoute.data};
})
}};
}))
]
})
export class AdminLdnServicesRoutingModule {

View File

@@ -1,11 +1,11 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {AdminLdnServicesRoutingModule} from './admin-ldn-services-routing.module';
import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component';
import {SharedModule} from '../../shared/shared.module';
import {LdnServiceFormComponent} from './ldn-service-form/ldn-service-form.component';
import {FormsModule} from '@angular/forms';
import {LdnItemfiltersService} from './ldn-services-data/ldn-itemfilters-data.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminLdnServicesRoutingModule } from './admin-ldn-services-routing.module';
import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component';
import { SharedModule } from '../../shared/shared.module';
import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
import { FormsModule } from '@angular/forms';
import { LdnItemfiltersService } from './ldn-services-data/ldn-itemfilters-data.service';
@NgModule({

View File

@@ -114,7 +114,7 @@
<div class="col">
<div #inboundPatternDropdown="ngbDropdown" class="w-80" display="dynamic"
id="additionalInboundPattern{{i}}"
ngbDropdown placement="bottom-start">
ngbDropdown placement="top-start">
<div class="position-relative right-addon" role="combobox">
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
ngbDropdownToggle></i>
@@ -129,7 +129,7 @@
type="text"
/>
<div aria-labelledby="inboundPatternDropdownButton"
class="dropdown-menu scrollable-dropdown-menu w-100 "
class="dropdown-menu dropdown-menu-top w-100 "
ngbDropdownMenu>
<div class="scrollable-menu" role="listbox">
<button (click)="selectInboundPattern(pattern, i); $event.stopPropagation()"
@@ -152,7 +152,7 @@
<ng-container
*ngIf="!!(formModel.get('notifyServiceInboundPatterns')['controls'][i].value.pattern)">
<div #inboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}" ngbDropdown
placement="bottom-start">
placement="top-start">
<div class="position-relative right-addon" role="combobox">
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
ngbDropdownToggle></i>
@@ -225,145 +225,6 @@
<span (click)="addInboundPattern()"
class="add-pattern-link mb-2">{{ 'ldn-new-service.form.label.addPattern' | translate }}</span>
<!-- In the Outbound Patterns Labels section -->
<div class="row mb-1 mt-5" *ngIf="areControlsInitialized">
<div class="col">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.outboundPattern' | translate }} </label>
</div>
<ng-container *ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][0]?.value?.pattern)">
<div class="col">
<label class="font-weight-bold">{{ 'ldn-new-service.form.label.ItemFilter' | translate }}</label>
</div>
</ng-container>
<div class="col-sm-1 ">
<label class="label-box-2" style="visibility: hidden;">
{{ 'ldn-new-service.form.label.automatic' | translate }}
</label>
</div>
<div class="col-sm-2 ">
</div>
</div>
<!-- In the Outbound Patterns section -->
<div *ngIf="areControlsInitialized">
<div *ngFor="let patternGroup of formModel.get('notifyServiceOutboundPatterns')['controls']; let i = index"
[class.marked-for-deletion]="markedForDeletionOutboundPattern.includes(i)"
formGroupName="notifyServiceOutboundPatterns">
<ng-container [formGroupName]="i">
<div class="row mb-1 align-items-center">
<div class="col">
<div #outboundPatternDropdown="ngbDropdown" class="w-100" id="additionalOutboundPattern{{i}}"
ngbDropdown
placement="top-start">
<div class="position-relative right-addon" role="combobox">
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
ngbDropdownToggle></i>
<input
(click)="outboundPatternDropdown.open();"
[readonly]="true"
[value]="selectedOutboundPatterns"
class="form-control w-100 scrollable-dropdown-input"
formControlName="patternLabel"
id="outboundPatternDropdownButton"
ngbDropdownAnchor
type="text"
/>
<div aria-labelledby="outboundPatternDropdownButton"
class="dropdown-menu dropdown-menu-top w-100 "
ngbDropdownMenu>
<div class="scrollable-menu" role="listbox">
<button (click)="selectOutboundPattern(pattern, i); $event.stopPropagation()"
*ngFor="let pattern of outboundPatterns; let internalIndex = index"
[title]="'ldn-service.form.pattern.' + pattern + '.description' | translate"
class="dropdown-item collection-item text-truncate w-100"
ngbDropdownItem
type="button">
<div>{{ 'ldn-service.form.pattern.' + pattern + '.label' | translate }}</div>
<div
class="small-text">{{ 'ldn-service.form.pattern.' + pattern + '.description' | translate }}</div>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<ng-container
*ngIf="!!(formModel.get('notifyServiceOutboundPatterns')['controls'][i].value.pattern)">
<div #outboundItemfilterDropdown="ngbDropdown" class="w-100" id="constraint{{i}}"
ngbDropdown
placement="bottom-start">
<div class="position-relative right-addon" role="combobox">
<i aria-hidden="true" class="position-absolute scrollable-dropdown-toggle"
ngbDropdownToggle></i>
<input
(click)="outboundItemfilterDropdown.open();"
[readonly]="true"
[value]="selectedOutboundItemfilters"
class="form-control w-100 scrollable-dropdown-input"
formControlName="constraint"
id="outboundItemfilterDropdown"
ngbDropdownAnchor
type="text"
/>
<div aria-labelledby="outboundItemfilterDropdownButton"
class="dropdown-menu scrollable-dropdown-menu w-100 "
ngbDropdownMenu>
<div class="scrollable-menu" role="listbox">
<button (click)="selectOutboundItemFilter('', i); $event.stopPropagation()"
class="dropdown-item collection-item text-truncate w-100" ngbDropdownItem type="button">
<span> {{'ldn-service.control-constaint-select-none' | translate}} </span>
</button>
<button (click)="selectOutboundItemFilter(constraint.id, i); $event.stopPropagation()"
*ngFor="let constraint of (itemfiltersRD$ | async)?.payload?.page; let internalIndex = index"
class="dropdown-item collection-item text-truncate w-100"
ngbDropdownItem
type="button">
<div>{{ constraint.id }}</div>
</button>
</div>
</div>
</div>
</div>
</ng-container>
</div>
<div [style.visibility]="'hidden'" class="col-sm-1">
<input hidden id="automatic{{i}}" name="automatic{{i}}" type="checkbox">
<div
class="toggle-switch">
<div class="slider"></div>
</div>
</div>
<div class="col-sm-2">
<div class="btn-group">
<button (click)="markForOutboundPatternDeletion(i)"
class="btn btn-outline-dark trash-button" type="button">
<i class="fas fa-trash"></i>
</button>
<button (click)="unmarkForOutboundPatternDeletion(i)"
*ngIf="markedForDeletionOutboundPattern.includes(i)"
class="btn btn-warning "
type="button">
<i class="fas fa-undo"></i>
</button>
</div>
</div>
</div>
</ng-container>
</div>
</div>
<div
(click)="addOutboundPattern()"
class="add-pattern-link mb-5">{{ 'ldn-new-service.form.label.addPattern' | translate }}
</div>
<div class="submission-form-footer my-1 position-sticky d-flex justify-content-between" role="group">
<button (click)="openResetFormModal(resetFormModal)" class="btn btn-danger" type="button">
<span><i class="fas fa-trash"></i>&nbsp;{{ 'submission.general.discard.submit' | translate }}</span>

View File

@@ -57,7 +57,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
@ViewChild('resetFormModal', {static: true}) resetFormModal: TemplateRef<any>;
public inboundPatterns: string[] = notifyPatterns;
public outboundPatterns: string[] = notifyPatterns;
public isNewService: boolean;
public areControlsInitialized: boolean;
itemfiltersRD$: Observable<RemoteData<PaginatedList<Itemfilter>>>;
@@ -71,21 +70,16 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
@Input() public ldnUrl: string;
@Input() public score: number;
@Input() public inboundPattern: string;
@Input() public outboundPattern: 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[] = [];
markedForDeletionOutboundPattern: number[] = [];
selectedOutboundPatterns: string[];
selectedInboundPatterns: string[];
selectedInboundItemfilters: string[];
selectedOutboundItemfilters: string[];
protected serviceId: string;
private deletedInboundPatterns: number[] = [];
private deletedOutboundPatterns: number[] = [];
private modalRef: any;
private service: LdnService;
private selectPatternDefaultLabeli18Key = 'ldn-service.form.label.placeholder.default-select';
@@ -111,7 +105,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
url: ['', Validators.required],
ldnUrl: ['', Validators.required],
score: ['', [Validators.required, Validators.pattern('^0*(\.[0-9]+)?$|^1(\.0+)?$')]], inboundPattern: [''],
outboundPattern: [''],
constraintPattern: [''],
enabled: [''],
type: LDN_SERVICE.value,
@@ -125,7 +118,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
this.serviceId = params.serviceId;
this.isNewService = segment[0].path === 'new';
this.formModel.addControl('notifyServiceInboundPatterns', this.formBuilder.array([this.createInboundPatternFormGroup()]));
this.formModel.addControl('notifyServiceOutboundPatterns', this.formBuilder.array([this.createOutboundPatternFormGroup()]));
this.areControlsInitialized = true;
if (this.serviceId && !this.isNewService) {
this.fetchServiceData(this.serviceId);
@@ -159,14 +151,13 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
const ldnUrl = this.formModel.get('ldnUrl').value;
const hasInboundPattern = this.checkPatterns(this.formModel.get('notifyServiceInboundPatterns') as FormArray);
const hasOutboundPattern = this.checkPatterns(this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
if (!name || !url || !ldnUrl || (!score && score !== 0) || this.formModel.get('score').invalid) {
this.closeModal();
return;
}
if (!hasInboundPattern || !hasOutboundPattern) {
if (!hasInboundPattern) {
this.notificationService.warning(this.translateService.get('ldn-service-notification.created.warning.title'));
this.closeModal();
return;
@@ -181,14 +172,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
return rest;
});
this.formModel.value.notifyServiceOutboundPatterns = this.formModel.value.notifyServiceOutboundPatterns.map((pattern: {
pattern: string;
patternLabel: string
}) => {
const {patternLabel, ...rest} = pattern;
return rest;
});
const values = {...this.formModel.value, enabled: true};
const ldnServiceData = this.ldnServicesService.create(values);
@@ -250,8 +233,7 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
type: this.service.type,
enabled: this.service.enabled
});
this.filterPatternObjectsAndPickLabel('notifyServiceInboundPatterns', false);
this.filterPatternObjectsAndPickLabel('notifyServiceOutboundPatterns', true);
this.filterPatternObjectsAndPickLabel('notifyServiceInboundPatterns');
}
},
);
@@ -260,25 +242,16 @@ 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..
* @param formArrayName - The name of the form array to be populated
* @param isOutbound - A boolean indicating whether the patterns are outbound (true) or inbound (false)
*/
filterPatternObjectsAndPickLabel(formArrayName: string, isOutbound: boolean) {
filterPatternObjectsAndPickLabel(formArrayName: string) {
const PatternsArray = this.formModel.get(formArrayName) as FormArray;
PatternsArray.clear();
let servicesToUse;
if (isOutbound) {
servicesToUse = this.service.notifyServiceOutboundPatterns;
} else {
servicesToUse = this.service.notifyServiceInboundPatterns;
}
servicesToUse.forEach((patternObj: NotifyServicePattern) => {
let patternFormGroup;
if (isOutbound) {
patternFormGroup = this.initializeOutboundPatternFormGroup();
} else {
patternFormGroup = this.initializeInboundPatternFormGroup();
}
patternFormGroup = this.initializeInboundPatternFormGroup();
const newPatternObjWithLabel = Object.assign(new NotifyServicePattern(), {
...patternObj,
patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternObj?.pattern + '.label')
@@ -306,9 +279,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
this.createReplaceOperation(patchOperations, 'score', '/score');
this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
this.handlePatterns(patchOperations, 'notifyServiceOutboundPatterns');
this.deletedInboundPatterns.forEach(index => {
const removeOperation: Operation = {
op: 'remove',
@@ -317,14 +287,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
patchOperations.push(removeOperation);
});
this.deletedOutboundPatterns.forEach(index => {
const removeOperation: Operation = {
op: 'remove',
path: `notifyServiceOutboundPatterns[${index}]`
};
patchOperations.push(removeOperation);
});
return patchOperations;
}
@@ -343,36 +305,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
}
/**
* Adds a new outbound pattern form group to the array of outbound patterns in the form
*/
addOutboundPattern() {
const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
}
/**
* Selects an outbound pattern by updating its values based on the provided pattern value and index
* @param patternValue - The selected pattern value
* @param index - The index of the outbound pattern in the array
*/
selectOutboundPattern(patternValue: string, index: number): void {
const patternArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
patternArray.controls[index].patchValue({pattern: patternValue});
patternArray.controls[index].patchValue({patternLabel: this.translateService.instant('ldn-service.form.pattern.' + patternValue + '.label')});
}
/**
* Selects an outbound item filter by updating its value based on the provided filter value and index
* @param filterValue - The selected filter value
* @param index - The index of the inbound pattern in the array
*/
selectOutboundItemFilter(filterValue: string, index: number) {
const filterArray = (this.formModel.get('notifyServiceOutboundPatterns') as FormArray);
filterArray.controls[index].patchValue({constraint: filterValue});
}
/**
* Selects an inbound pattern by updating its values based on the provided pattern value and index
* @param patternValue - The selected pattern value
@@ -458,7 +390,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
*/
patchService() {
this.deleteMarkedInboundPatterns();
this.deleteMarkedOutboundPatterns();
const patchOperations = this.generatePatchOperations();
this.formModel.markAllAsTouched();
@@ -468,17 +399,12 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
return;
}
const notifyServiceOutboundPatterns = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
const notifyServiceInboundPatterns = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
const deletedInboundPatternsLength = this.deletedInboundPatterns.length;
const deletedOutboundPatternsLength = this.deletedOutboundPatterns.length;
// If no inbound or outbound patterns are specified, close the modal and return
// If no inbound patterns are specified, close the modal and return
// notify the user that no patterns are specified
if (
(notifyServiceOutboundPatterns.length === deletedOutboundPatternsLength ) ||
(notifyServiceInboundPatterns.length === deletedInboundPatternsLength)) {
if (notifyServiceInboundPatterns.length === deletedInboundPatternsLength) {
this.notificationService.warning(this.translateService.get('ldn-service-notification.created.warning.title'));
this.deletedOutboundPatterns = [];
this.deletedInboundPatterns = [];
this.closeModal();
return;
@@ -533,27 +459,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
}
}
/**
* Marks the specified outbound pattern for deletion
* @param index - The index of the outbound pattern in the array
*/
markForOutboundPatternDeletion(index: number) {
if (!this.markedForDeletionOutboundPattern.includes(index)) {
this.markedForDeletionOutboundPattern.push(index);
}
}
/**
* Unmarks the specified outbound pattern for deletion
* @param index - The index of the outbound pattern in the array
*/
unmarkForOutboundPatternDeletion(index: number) {
const i = this.markedForDeletionOutboundPattern.indexOf(index);
if (i !== -1) {
this.markedForDeletionOutboundPattern.splice(i, 1);
}
}
/**
* Deletes marked inbound patterns from the form model
*/
@@ -576,29 +481,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
this.markedForDeletionInboundPattern = [];
}
/**
* Deletes marked outbound patterns from the form model
*/
deleteMarkedOutboundPatterns() {
this.markedForDeletionOutboundPattern.sort((a, b) => b - a);
const patternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
for (const index of this.markedForDeletionOutboundPattern) {
if (index >= 0 && index < patternsArray.length) {
const patternGroup = patternsArray.at(index) as FormGroup;
const patternValue = patternGroup.value;
if (patternValue.isNew) {
patternsArray.removeAt(index);
} else {
this.deletedOutboundPatterns.push(index);
}
}
}
this.markedForDeletionOutboundPattern = [];
}
/**
* Creates a replace operation and adds it to the patch operations if the form control is dirty
* @param patchOperations - The array to store patch operations
@@ -656,25 +538,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
this.router.navigateByUrl('admin/ldn/services');
}
/**
* Creates a form group for outbound patterns
* @returns The form group for outbound patterns
*/
private createOutboundPatternFormGroup(): FormGroup {
const outBoundFormGroup = {
pattern: '',
patternLabel: this.translateService.instant(this.selectPatternDefaultLabeli18Key),
constraint: '',
isNew: true
};
if (this.isNewService) {
delete outBoundFormGroup.isNew;
}
return this.formBuilder.group(outBoundFormGroup);
}
/**
* Creates a form group for inbound patterns
* @returns The form group for inbound patterns
@@ -695,18 +558,6 @@ export class LdnServiceFormComponent implements OnInit, OnDestroy {
return this.formBuilder.group(inBoundFormGroup);
}
/**
* Initializes an existing form group for outbound patterns
* @returns The initialized form group for outbound patterns
*/
private initializeOutboundPatternFormGroup(): FormGroup {
return this.formBuilder.group({
pattern: '',
patternLabel: '',
constraint: '',
});
}
/**
* Initializes an existing form group for inbound patterns
* @returns The initialized form group for inbound patterns

View File

@@ -26,13 +26,6 @@ export const mockLdnService: LdnService = {
automatic: 'true',
},
],
notifyServiceOutboundPatterns: [
{
pattern: 'patternC',
constraint: 'itemFilterC',
automatic: 'true',
},
],
type: LDN_SERVICE,
_links: {
self: {
@@ -68,13 +61,6 @@ export const mockLdnServices: LdnService[] = [{
automatic: 'true',
},
],
notifyServiceOutboundPatterns: [
{
pattern: 'patternC',
constraint: 'itemFilterC',
automatic: 'true',
},
],
type: LDN_SERVICE,
_links: {
self: {
@@ -105,13 +91,6 @@ export const mockLdnServices: LdnService[] = [{
automatic: 'true',
},
],
notifyServiceOutboundPatterns: [
{
pattern: 'patternC',
constraint: 'itemFilterC',
automatic: 'true',
},
],
type: LDN_SERVICE,
_links: {
self: {

View File

@@ -44,9 +44,6 @@ export class LdnService extends CacheableObject {
@autoserialize
notifyServiceInboundPatterns?: NotifyServicePattern[];
@autoserialize
notifyServiceOutboundPatterns?: NotifyServicePattern[];
@deserialize
_links: {
self: {

View File

@@ -0,0 +1,52 @@
import { NavigationBreadcrumbResolver } from './navigation-breadcrumb.resolver';
describe('NavigationBreadcrumbResolver', () => {
describe('resolve', () => {
let resolver: NavigationBreadcrumbResolver;
let NavigationBreadcrumbService: any;
let i18nKey: string;
let relatedI18nKey: string;
let route: any;
let expectedPath;
let state;
beforeEach(() => {
i18nKey = 'example.key';
relatedI18nKey = 'related.key';
route = {
data: {
breadcrumbKey: i18nKey,
relatedRoutes: [
{
path: '',
data: {breadcrumbKey: relatedI18nKey},
}
]
},
routeConfig: {
path: 'example'
},
parent: {
routeConfig: {
path: ''
},
url: [{
path: 'base'
}]
} as any
};
state = {
url: '/base/example'
};
expectedPath = '/base/example:/base';
NavigationBreadcrumbService = {};
resolver = new NavigationBreadcrumbResolver(NavigationBreadcrumbService);
});
it('should resolve the breadcrumb config', () => {
const resolvedConfig = resolver.resolve(route, state);
const expectedConfig = { provider: NavigationBreadcrumbService, key: `${i18nKey}:${relatedI18nKey}`, url: expectedPath };
expect(resolvedConfig).toEqual(expectedConfig);
});
});
});

View File

@@ -0,0 +1,52 @@
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service';
/**
* The class that resolves a BreadcrumbConfig object with an i18n key string for a route and related parents
*/
@Injectable({
providedIn: 'root'
})
export class NavigationBreadcrumbResolver implements Resolve<BreadcrumbConfig<string>> {
private parentRoutes: ActivatedRouteSnapshot[] = [];
constructor(protected breadcrumbService: NavigationBreadcrumbsService) {
}
/**
* Method to collect all parent routes snapshot from current route snapshot
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
*/
private getParentRoutes(route: ActivatedRouteSnapshot): void {
if (route.parent) {
this.parentRoutes.push(route.parent);
this.getParentRoutes(route.parent);
}
}
/**
* Method for resolving an I18n breadcrumb configuration object
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns BreadcrumbConfig object
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig<string> {
this.getParentRoutes(route);
const relatedRoutes = route.data.relatedRoutes;
const parentPaths = this.parentRoutes.map(parent => parent.routeConfig?.path);
const relatedParentRoutes = relatedRoutes.filter(relatedRoute => parentPaths.includes(relatedRoute.path));
const baseUrlSegmentPath = route.parent.url[route.parent.url.length - 1].path;
const baseUrl = state.url.substring(0, state.url.lastIndexOf(baseUrlSegmentPath) + baseUrlSegmentPath.length);
const combinedParentBreadcrumbKeys = relatedParentRoutes.reduce((previous, current) => {
return `${previous}:${current.data.breadcrumbKey}`;
}, route.data.breadcrumbKey);
const combinedUrls = relatedParentRoutes.reduce((previous, current) => {
return `${previous}:${baseUrl}${current.path}`;
}, state.url);
return {provider: this.breadcrumbService, key: combinedParentBreadcrumbKeys, url: combinedUrls};
}
}

View File

@@ -0,0 +1,30 @@
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
import { BreadcrumbsProviderService } from './breadcrumbsProviderService';
import { Observable, of as observableOf } from 'rxjs';
import { Injectable } from '@angular/core';
/**
* The postfix for i18n breadcrumbs
*/
export const BREADCRUMB_MESSAGE_POSTFIX = '.breadcrumbs';
/**
* Service to calculate i18n breadcrumbs for a single part of the route
*/
@Injectable({
providedIn: 'root'
})
export class NavigationBreadcrumbsService implements BreadcrumbsProviderService<string> {
/**
* Method to calculate the breadcrumbs
* @param key The key used to resolve the breadcrumb
* @param url The url to use as a link for this breadcrumb
*/
getBreadcrumbs(key: string, url: string): Observable<Breadcrumb[]> {
const keys = key.split(':');
const urls = url.split(':');
const breadcrumbs = keys.map((currentKey, index) => new Breadcrumb(currentKey + BREADCRUMB_MESSAGE_POSTFIX, urls[index] ));
return observableOf(breadcrumbs.reverse());
}
}

View File

@@ -0,0 +1,43 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
import { getTestScheduler } from 'jasmine-marbles';
import { BREADCRUMB_MESSAGE_POSTFIX } from './i18n-breadcrumbs.service';
import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service';
describe('NavigationBreadcrumbsService', () => {
let service: NavigationBreadcrumbsService;
let exampleString;
let exampleURL;
let childrenString;
let childrenUrl;
let parentString;
let parentUrl;
function init() {
exampleString = 'example.string:parent.string';
exampleURL = 'example.com:parent.com';
childrenString = 'example.string';
childrenUrl = 'example.com';
parentString = 'parent.string';
parentUrl = 'parent.com';
}
beforeEach(waitForAsync(() => {
init();
TestBed.configureTestingModule({}).compileComponents();
}));
beforeEach(() => {
service = new NavigationBreadcrumbsService();
});
describe('getBreadcrumbs', () => {
it('should return an array of breadcrumbs based on strings by adding the postfix', () => {
const breadcrumbs = service.getBreadcrumbs(exampleString, exampleURL);
getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [
new Breadcrumb(childrenString + BREADCRUMB_MESSAGE_POSTFIX, childrenUrl),
new Breadcrumb(parentString + BREADCRUMB_MESSAGE_POSTFIX, parentUrl),
].reverse() });
});
});
});

View File

@@ -1005,8 +1005,6 @@
"ldn-new-service.form.error.score": "Please enter a valid score (between 0 and 1). Use the “.” as decimal separator",
"ldn-new-service.form.label.inboundPattern": "Inbound Pattern",
"ldn-new-service.form.label.outboundPattern": "Outbound Patterns",
"ldn-new-service.form.label.placeholder.outboundPattern": "Select an Outbound Pattern",
"ldn-new-service.form.label.addPattern": "+ Add more",
"ldn-new-service.form.label.removeItemFilter": "Remove",
"ldn-register-new-service.breadcrumbs": "New Service",
@@ -1026,7 +1024,7 @@
"ldn-service-notification.created.success.body": "The service has been created",
"ldn-service-notification.created.failure.title": "Failed Create",
"ldn-service-notification.created.failure.body": "The service has not been created",
"ldn-service-notification.created.warning.title": "Please select at least one Inbound Pattern and one Outbound Pattern",
"ldn-service-notification.created.warning.title": "Please select at least one Inbound Pattern",
"ldn-enable-service.notification.success.title": "Successful status updated",
"ldn-enable-service.notification.success.content": "The service status has been updated",
"ldn-service-delete.notification.success.title": "Successful Deletion",
@@ -1046,9 +1044,6 @@
"ldn-edit-service.form.label.selectItemFilter": "No Item Filter",
"ldn-edit-service.form.label.automatic": "Automatic",
"ldn-edit-service.form.label.addInboundPattern": "+ Add more",
"ldn-edit-service.form.label.outboundPattern": "Outbound Pattern",
"ldn-edit-service.form.label.noOutboundPatternSelected": "No Outbound Pattern",
"ldn-edit-service.form.label.addOutboundPattern": "+ Add more",
"ldn-edit-service.form.label.submit": "Save",
"ldn-edit-service.breadcrumbs": "Edit Service",
"ldn-service.control-constaint-select-none": "Select none",