diff --git a/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts b/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts
new file mode 100644
index 0000000000..4f05bca9e3
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts
@@ -0,0 +1,37 @@
+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 { LdnServicesGuard } from './ldn-services-guard/ldn-services-guard.service';
+import { LdnServiceNewComponent } from './ldn-service-new/ldn-service-new.component';
+import { LdnServiceFormEditComponent } from './ldn-service-form-edit/ldn-service-form-edit.component';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([
+ {
+ path: '',
+ pathMatch: 'full',
+ component: LdnServicesOverviewComponent,
+ resolve: { breadcrumb: I18nBreadcrumbResolver },
+ data: { title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new' },
+ canActivate: [LdnServicesGuard]
+ },
+ {
+ path: 'new',
+ resolve: { breadcrumb: I18nBreadcrumbResolver },
+ component: LdnServiceNewComponent,
+ data: { title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service' }
+ },
+ {
+ path: 'edit/:serviceId',
+ resolve: { breadcrumb: I18nBreadcrumbResolver },
+ component: LdnServiceFormEditComponent,
+ data: { title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service' }
+ },
+ ]),
+ ]
+})
+export class AdminLdnServicesRoutingModule {
+
+}
diff --git a/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts b/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts
new file mode 100644
index 0000000000..1fd67d53b9
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts
@@ -0,0 +1,25 @@
+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 { LdnServiceNewComponent } from './ldn-service-new/ldn-service-new.component';
+import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component';
+import { LdnServiceFormEditComponent } from './ldn-service-form-edit/ldn-service-form-edit.component';
+
+
+
+@NgModule({
+ imports: [
+ CommonModule,
+ SharedModule,
+ AdminLdnServicesRoutingModule,
+ ],
+ declarations: [
+ LdnServicesOverviewComponent,
+ LdnServiceNewComponent,
+ LdnServiceFormComponent,
+ LdnServiceFormEditComponent,
+ ]
+})
+export class AdminLdnServicesModule { }
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.html b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.html
new file mode 100644
index 0000000000..6402e0b953
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.html
@@ -0,0 +1,155 @@
+
+
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.scss b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.scss
new file mode 100644
index 0000000000..92ac744aed
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.scss
@@ -0,0 +1,114 @@
+form {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ max-width: 800px;
+ font-size: 14px;
+ margin-left: 300px;
+
+ & > * {
+ width: 100%;
+ }
+}
+
+input[type="text"],
+select {
+ max-width: 100%;
+ width: 100%;
+ padding: 8px;
+ font-size: 14px;
+}
+
+option:not(:first-child) {
+ font-weight: bold;
+}
+
+.trash-button {
+ width: 40px;
+ height: 40px;
+}
+
+textarea {
+ height: 200px;
+ resize: none;
+}
+
+.add-pattern-link{
+ color: #0048ff;
+ cursor: pointer;
+ margin-left: 10px;
+}
+.remove-pattern-link{
+ color: #e34949;
+ cursor: pointer;
+ margin-left: 10px;
+}
+
+.status-checkbox {
+ margin-top: 5px;
+}
+
+
+.invalid-field {
+ border: 1px solid red;
+ color: #000000;
+}
+
+.toggle-switch {
+ display: flex;
+ align-items: center;
+ opacity: 0.8;
+ position: relative;
+ width: 60px;
+ height: 30px;
+ background-color: #ccc;
+ border-radius: 15px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.toggle-switch.checked {
+ background-color: #24cc9a;
+}
+
+.slider {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ background-color: #fff;
+ transition: transform 0.3s;
+}
+
+
+
+.toggle-switch .slider {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ margin: 0 auto;
+}
+
+.toggle-switch.checked .slider {
+ transform: translateX(30px);
+}
+
+.toggle-switch-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-end;
+ margin-top: 10px;
+}
+
+.toggle-switch {
+ cursor: pointer;
+}
+
+
+
+
+
+
+
+
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.spec.ts b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.spec.ts
new file mode 100644
index 0000000000..8320ec9042
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LdnServiceFormEditComponent } from './ldn-service-form-edit.component';
+
+describe('LdnServiceFormEditComponent', () => {
+ let component: LdnServiceFormEditComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LdnServiceFormEditComponent ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(LdnServiceFormEditComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.ts b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.ts
new file mode 100644
index 0000000000..90a0546671
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form-edit/ldn-service-form-edit.component.ts
@@ -0,0 +1,374 @@
+import { ChangeDetectorRef, Component, Input } from '@angular/core';
+import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
+import { Router } from '@angular/router';
+import { HttpClient } from '@angular/common/http';
+import { LdnDirectoryService } from '../ldn-services-services/ldn-directory.service';
+import { LdnServicesService } from '../ldn-services-data/ldn-services-data.service';
+import { LdnServiceConstraint } from '../ldn-services-model/ldn-service-constraint.model';
+import { notifyPatterns } from '../ldn-services-patterns/ldn-service-coar-patterns';
+import { ActivatedRoute } from '@angular/router';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+
+@Component({
+ selector: 'ds-ldn-service-form-edit',
+ templateUrl: './ldn-service-form-edit.component.html',
+ styleUrls: ['./ldn-service-form-edit.component.scss'],
+ animations: [
+ trigger('toggleAnimation', [
+ state('true', style({})),
+ state('false', style({})),
+ transition('true <=> false', animate('300ms ease-in')),
+ ]),
+ ],
+})
+export class LdnServiceFormEditComponent {
+ formModel: FormGroup;
+
+ showItemFilterDropdown = false;
+
+ private originalInboundPatterns: any[] = [];
+ private originalOutboundPatterns: any[] = [];
+ public inboundPatterns: object[] = notifyPatterns;
+ public outboundPatterns: object[] = notifyPatterns;
+ public itemFilterList: LdnServiceConstraint[];
+
+ @Input() public name: string;
+ @Input() public description: string;
+ @Input() public url: string;
+ @Input() public ldnUrl: string;
+ @Input() public inboundPattern: string;
+ @Input() public outboundPattern: string;
+ @Input() public constraint: string;
+ @Input() public automatic: boolean;
+
+ @Input() public headerKey: string;
+ private serviceId: string;
+
+ constructor(
+ private ldnServicesService: LdnServicesService,
+ private ldnDirectoryService: LdnDirectoryService,
+ private formBuilder: FormBuilder,
+ private http: HttpClient,
+ private router: Router,
+ private route: ActivatedRoute,
+ private cdRef: ChangeDetectorRef
+ ) {
+
+ this.formModel = this.formBuilder.group({
+ id: [''],
+ name: ['', Validators.required],
+ description: ['', Validators.required],
+ url: ['', Validators.required],
+ ldnUrl: ['', Validators.required],
+ inboundPattern: [''],
+ outboundPattern: [''],
+ constraintPattern: [''],
+ enabled: [''],
+ notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
+ notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
+ type: LDN_SERVICE.value,
+ });
+ }
+
+ ngOnInit(): void {
+ this.route.params.subscribe((params) => {
+ this.serviceId = params.serviceId;
+ if (this.serviceId) {
+ this.fetchServiceData(this.serviceId);
+ }
+ });
+ this.ldnDirectoryService.getItemFilters().subscribe((itemFilters) => {
+ this.itemFilterList = itemFilters._embedded.itemfilters.map((filter: { id: string; }) => ({
+ name: filter.id
+ }));
+ this.cdRef.detectChanges();
+
+ });
+ }
+
+ private getOriginalPattern(formArrayName: string, patternId: number): any {
+ let originalPatterns: any[] = [];
+
+ if (formArrayName === 'notifyServiceInboundPatterns') {
+ originalPatterns = this.originalInboundPatterns;
+ } else if (formArrayName === 'notifyServiceOutboundPatterns') {
+ originalPatterns = this.originalOutboundPatterns;
+ }
+
+ return originalPatterns.find((pattern) => pattern.id === patternId);
+ }
+
+ private patternsAreEqual(patternA: any, patternB: any): boolean {
+ return (
+ patternA.pattern === patternB.pattern &&
+ patternA.constraint === patternB.constraint &&
+ patternA.automatic === patternB.automatic
+ );
+ }
+
+ fetchServiceData(serviceId: string): void {
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${serviceId}`;
+
+ this.http.get(apiUrl).subscribe(
+ (data: any) => {
+ console.log(data);
+
+ this.formModel.patchValue({
+ id: data.id,
+ name: data.name,
+ description: data.description,
+ url: data.url,
+ ldnUrl: data.ldnUrl,
+ type: data.type,
+ enabled: data.enabled
+ });
+
+ const inboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ inboundPatternsArray.clear(); // Clear existing rows
+
+ data.notifyServiceInboundPatterns.forEach((pattern: any) => {
+ console.log(pattern);
+ const patternFormGroup = this.initializeInboundPatternFormGroup();
+ console.log();
+ patternFormGroup.patchValue(pattern);
+ inboundPatternsArray.push(patternFormGroup);
+ this.cdRef.detectChanges();
+ });
+
+ const outboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
+ outboundPatternsArray.clear();
+
+ data.notifyServiceOutboundPatterns.forEach((pattern: any) => {
+ const patternFormGroup = this.initializeOutboundPatternFormGroup();
+ patternFormGroup.patchValue(pattern);
+ outboundPatternsArray.push(patternFormGroup);
+
+ this.cdRef.detectChanges();
+ });
+ this.originalInboundPatterns = [...data.notifyServiceInboundPatterns];
+
+ this.originalOutboundPatterns = [...data.notifyServiceOutboundPatterns];
+ },
+ (error) => {
+ console.error('Error fetching service data:', error);
+ }
+ );
+ }
+
+
+ generatePatchOperations(): any[] {
+ const patchOperations: any[] = [];
+
+ this.addReplaceOperation(patchOperations, 'name', '/name');
+ this.addReplaceOperation(patchOperations, 'description', '/description');
+ this.addReplaceOperation(patchOperations, 'ldnUrl', '/ldnurl');
+ this.addReplaceOperation(patchOperations, 'url', '/url');
+
+ this.handlePatterns(patchOperations, 'notifyServiceInboundPatterns');
+
+ this.handlePatterns(patchOperations, 'notifyServiceOutboundPatterns');
+
+ return patchOperations;
+ }
+
+ private addReplaceOperation(patchOperations: any[], formControlName: string, path: string): void {
+ if (this.formModel.get(formControlName).dirty) {
+ patchOperations.push({
+ op: 'replace',
+ path,
+ value: this.formModel.get(formControlName).value,
+ });
+ }
+ }
+
+ private handlePatterns(patchOperations: any[], formArrayName: string): void {
+ const patternsArray = this.formModel.get(formArrayName) as FormArray;
+
+ if (patternsArray.dirty) {
+ for (let i = 0; i < patternsArray.length; i++) {
+ const patternGroup = patternsArray.at(i) as FormGroup;
+ const patternValue = patternGroup.value;
+
+ if (patternValue.isNew) {
+ console.log(this.getOriginalPatternsForFormArray(formArrayName));
+ console.log(patternGroup);
+ delete patternValue.isNew;
+ const addOperation = {
+ op: 'add',
+ path: `${formArrayName}/-`,
+ value: patternValue,
+ };
+ patchOperations.push(addOperation);
+ } else if (patternGroup.dirty) {
+ const replaceOperation = {
+ op: 'replace',
+ path: `${formArrayName}[${i}]`,
+ value: patternValue,
+ };
+ patchOperations.push(replaceOperation);
+ console.log(patternValue.id);
+ }
+ }
+ }
+ }
+
+ private getOriginalPatternsForFormArray(formArrayName: string): any[] {
+ if (formArrayName === 'notifyServiceInboundPatterns') {
+ return this.originalInboundPatterns;
+ } else if (formArrayName === 'notifyServiceOutboundPatterns') {
+ return this.originalOutboundPatterns;
+ }
+ return [];
+ }
+
+ submitForm() {
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${this.serviceId}`;
+ const patchOperations = this.generatePatchOperations();
+
+ this.http.patch(apiUrl, patchOperations).subscribe(
+ (response) => {
+ console.log('Service updated successfully:', response);
+ this.sendBack();
+ },
+ (error) => {
+ console.error('Error updating service:', error);
+ }
+ );
+
+ }
+
+
+ addInboundPattern() {
+ const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
+ }
+
+ removeInboundPattern(index: number) {
+ const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ if (index >= 0 && index < notifyServiceInboundPatternsArray.length) {
+ const serviceId = this.formModel.get('id').value;
+
+ const patchOperation = [
+ {
+ op: 'remove',
+ path: `notifyServiceInboundPatterns[${index}]`
+ }
+ ];
+
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${serviceId}`;
+
+ this.http.patch(apiUrl, patchOperation).subscribe(
+ (response) => {
+ console.log('Pattern removed successfully:', response);
+
+ notifyServiceInboundPatternsArray.removeAt(index);
+ },
+ (error) => {
+ console.error('Error removing pattern:', error);
+ }
+ );
+ }
+ }
+
+ addOutboundPattern() {
+ const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
+ notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
+ }
+
+ removeOutboundPattern(index: number) {
+ const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
+ if (index >= 0 && index < notifyServiceOutboundPatternsArray.length) {
+ const serviceId = this.formModel.get('id').value;
+
+
+ const patchOperation = [
+ {
+ op: 'remove',
+ path: `notifyServiceOutboundPatterns[${index}]`
+ }
+ ];
+
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${serviceId}`;
+
+ this.http.patch(apiUrl, patchOperation).subscribe(
+ (response) => {
+ console.log('Pattern removed successfully:', response);
+
+ notifyServiceOutboundPatternsArray.removeAt(index);
+ },
+ (error) => {
+ console.error('Error removing pattern:', error);
+ }
+ );
+ }
+ }
+
+ private sendBack() {
+ this.router.navigateByUrl('admin/ldn/services');
+ }
+
+ private createOutboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: '',
+ constraint: '',
+ isNew: true,
+ });
+ }
+
+ private createInboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: '',
+ constraint: '',
+ automatic: '',
+ isNew: true
+ });
+ }
+
+ private initializeOutboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: '',
+ constraint: '',
+ });
+ }
+
+ private initializeInboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: '',
+ constraint: '',
+ automatic: '',
+ });
+ }
+
+ toggleAutomatic(i: number) {
+ const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
+ if (automaticControl) {
+ automaticControl.setValue(!automaticControl.value);
+ }
+ }
+
+ toggleEnabled() {
+ const newStatus = !this.formModel.get('enabled').value;
+ const serviceId = this.formModel.get('id').value;
+ const status = this.formModel.get('enabled').value;
+
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${serviceId}`;
+ const patchOperation = {
+ op: 'replace',
+ path: '/enabled',
+ value: newStatus,
+ };
+
+ this.http.patch(apiUrl, [patchOperation]).subscribe(
+ () => {
+ console.log('Status updated successfully.');
+ this.formModel.get('enabled').setValue(newStatus);
+ console.log(this.formModel.get('enabled'));
+ this.cdRef.detectChanges();
+ },
+ (error) => {
+ console.error('Error updating status:', error);
+ }
+ );
+ }
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.html b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.html
new file mode 100644
index 0000000000..d1f13baf32
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.html
@@ -0,0 +1,163 @@
+
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.scss b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.scss
new file mode 100644
index 0000000000..92ac744aed
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.scss
@@ -0,0 +1,114 @@
+form {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ max-width: 800px;
+ font-size: 14px;
+ margin-left: 300px;
+
+ & > * {
+ width: 100%;
+ }
+}
+
+input[type="text"],
+select {
+ max-width: 100%;
+ width: 100%;
+ padding: 8px;
+ font-size: 14px;
+}
+
+option:not(:first-child) {
+ font-weight: bold;
+}
+
+.trash-button {
+ width: 40px;
+ height: 40px;
+}
+
+textarea {
+ height: 200px;
+ resize: none;
+}
+
+.add-pattern-link{
+ color: #0048ff;
+ cursor: pointer;
+ margin-left: 10px;
+}
+.remove-pattern-link{
+ color: #e34949;
+ cursor: pointer;
+ margin-left: 10px;
+}
+
+.status-checkbox {
+ margin-top: 5px;
+}
+
+
+.invalid-field {
+ border: 1px solid red;
+ color: #000000;
+}
+
+.toggle-switch {
+ display: flex;
+ align-items: center;
+ opacity: 0.8;
+ position: relative;
+ width: 60px;
+ height: 30px;
+ background-color: #ccc;
+ border-radius: 15px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.toggle-switch.checked {
+ background-color: #24cc9a;
+}
+
+.slider {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ background-color: #fff;
+ transition: transform 0.3s;
+}
+
+
+
+.toggle-switch .slider {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ margin: 0 auto;
+}
+
+.toggle-switch.checked .slider {
+ transform: translateX(30px);
+}
+
+.toggle-switch-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-end;
+ margin-top: 10px;
+}
+
+.toggle-switch {
+ cursor: pointer;
+}
+
+
+
+
+
+
+
+
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.spec.ts b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.spec.ts
new file mode 100644
index 0000000000..3ae834d642
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LdnServiceFormComponent } from './ldn-service-form.component';
+
+describe('LdnServiceFormComponent', () => {
+ let component: LdnServiceFormComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LdnServiceFormComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LdnServiceFormComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.ts b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.ts
new file mode 100644
index 0000000000..608a7ada23
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.ts
@@ -0,0 +1,171 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
+import { HttpClient } from '@angular/common/http';
+import { Router } from '@angular/router';
+
+import { LdnServicesService } from '../ldn-services-data/ldn-services-data.service';
+import { LdnServiceConstraint } from '../ldn-services-model/ldn-service-constraint.model';
+import { notifyPatterns } from '../ldn-services-patterns/ldn-service-coar-patterns';
+import { LdnDirectoryService } from '../ldn-services-services/ldn-directory.service';
+import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+
+@Component({
+ selector: 'ds-ldn-service-form',
+ templateUrl: './ldn-service-form.component.html',
+ styleUrls: ['./ldn-service-form.component.scss'],
+ animations: [
+ trigger('toggleAnimation', [
+ state('true', style({})), // Define animation states (empty style)
+ state('false', style({})),
+ transition('true <=> false', animate('300ms ease-in')), // Define animation transition with duration
+ ]),
+ ],
+})
+export class LdnServiceFormComponent implements OnInit {
+ formModel: FormGroup;
+
+
+
+ //showItemFilterDropdown = false;
+
+ public inboundPatterns: object[] = notifyPatterns;
+ public outboundPatterns: object[] = notifyPatterns;
+ public itemFilterList: LdnServiceConstraint[];
+ //additionalOutboundPatterns: FormGroup[] = [];
+ //additionalInboundPatterns: FormGroup[] = [];
+
+
+ //@Input() public status: boolean;
+ @Input() public name: string;
+ @Input() public description: string;
+ @Input() public url: string;
+ @Input() public ldnUrl: string;
+ @Input() public inboundPattern: string;
+ @Input() public outboundPattern: string;
+ @Input() public constraint: string;
+ @Input() public automatic: boolean;
+
+ @Input() public headerKey: string;
+
+ /*
+ get notifyServiceInboundPatternsFormArray(): FormArray {
+ return this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ }
+ */
+
+ constructor(
+ private ldnServicesService: LdnServicesService,
+ private ldnDirectoryService: LdnDirectoryService,
+ private formBuilder: FormBuilder,
+ private http: HttpClient,
+ private router: Router
+ ) {
+
+ this.formModel = this.formBuilder.group({
+ enabled: true,
+ id: [''],
+ name: ['', Validators.required],
+ description: [''],
+ url: ['', Validators.required],
+ ldnUrl: ['', Validators.required],
+ inboundPattern: [''],
+ outboundPattern: [''],
+ constraintPattern: [''],
+ notifyServiceInboundPatterns: this.formBuilder.array([this.createInboundPatternFormGroup()]),
+ notifyServiceOutboundPatterns: this.formBuilder.array([this.createOutboundPatternFormGroup()]),
+ type: LDN_SERVICE.value,
+ });
+ }
+
+ ngOnInit(): void {
+ this.ldnDirectoryService.getItemFilters().subscribe((itemFilters) => {
+ console.log(itemFilters);
+ this.itemFilterList = itemFilters._embedded.itemfilters.map((filter: { id: string; }) => ({
+ name: filter.id
+ }));
+ });
+
+ }
+
+ submitForm() {
+ this.formModel.get('name').markAsTouched();
+ this.formModel.get('url').markAsTouched();
+ this.formModel.get('ldnUrl').markAsTouched();
+
+ const name = this.formModel.get('name').value;
+ const url = this.formModel.get('url').value;
+ const ldnUrl = this.formModel.get('ldnUrl').value;
+
+ if (!name || !url || !ldnUrl) {
+ return;
+ }
+
+ this.formModel.removeControl('inboundPattern');
+ this.formModel.removeControl('outboundPattern');
+ this.formModel.removeControl('constraintPattern');
+ console.log('JSON Data:', this.formModel.value);
+
+ const apiUrl = 'http://localhost:8080/server/api/ldn/ldnservices';
+
+ this.http.post(apiUrl, this.formModel.value).subscribe(
+ (response) => {
+ console.log('Service created successfully:', response);
+ this.formModel.reset();
+ this.sendBack();
+ },
+ (error) => {
+ console.error('Error creating service:', error);
+ }
+ );
+ }
+
+
+ private sendBack() {
+ this.router.navigateByUrl('admin/ldn/services');
+ }
+
+ addInboundPattern() {
+ const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ notifyServiceInboundPatternsArray.push(this.createInboundPatternFormGroup());
+ }
+
+ removeInboundPattern(patternGroup: FormGroup) {
+ const notifyServiceInboundPatternsArray = this.formModel.get('notifyServiceInboundPatterns') as FormArray;
+ notifyServiceInboundPatternsArray.removeAt(notifyServiceInboundPatternsArray.controls.indexOf(patternGroup));
+ }
+
+ addOutboundPattern() {
+ const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
+ notifyServiceOutboundPatternsArray.push(this.createOutboundPatternFormGroup());
+ }
+
+ removeOutboundPattern(patternGroup: FormGroup) {
+ const notifyServiceOutboundPatternsArray = this.formModel.get('notifyServiceOutboundPatterns') as FormArray;
+ notifyServiceOutboundPatternsArray.removeAt(notifyServiceOutboundPatternsArray.controls.indexOf(patternGroup));
+ }
+
+ private createOutboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: [''],
+ constraint: [''],
+ });
+ }
+
+ private createInboundPatternFormGroup(): FormGroup {
+ return this.formBuilder.group({
+ pattern: [''],
+ constraint: [''],
+ automatic: false
+ });
+ }
+
+ toggleAutomatic(i: number) {
+ const automaticControl = this.formModel.get(`notifyServiceInboundPatterns.${i}.automatic`);
+ if (automaticControl) {
+ automaticControl.setValue(!automaticControl.value);
+ }
+ }
+
+
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.html b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.html
new file mode 100644
index 0000000000..9567cb1abe
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.scss b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.spec.ts b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.spec.ts
new file mode 100644
index 0000000000..4994823004
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LdnServiceNewComponent } from './ldn-service-new.component';
+
+describe('LdnServiceNewComponent', () => {
+ let component: LdnServiceNewComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LdnServiceNewComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LdnServiceNewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.ts b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.ts
new file mode 100644
index 0000000000..daf1653cd1
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-new/ldn-service-new.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { LdnService } from "../ldn-services-model/ldn-services.model";
+import { ActivatedRoute } from "@angular/router";
+import { ProcessDataService } from "../../../core/data/processes/process-data.service";
+import { LinkService } from "../../../core/cache/builders/link.service";
+import { getFirstSucceededRemoteDataPayload } from "../../../core/shared/operators";
+
+@Component({
+ selector: 'ds-ldn-service-new',
+ templateUrl: './ldn-service-new.component.html',
+ styleUrls: ['./ldn-service-new.component.scss']
+})
+export class LdnServiceNewComponent implements OnInit {
+ /**
+ * Emits preselected process if there is one
+ */
+ ldnService$?: Observable;
+
+ constructor(private route: ActivatedRoute, private processService: ProcessDataService, private linkService: LinkService) {
+ }
+
+ /**
+ * If there's an id parameter, use this the process with this identifier as presets for the form
+ */
+ ngOnInit() {
+ }
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-service-serviceMock/ldnServicesRD$-mock.ts b/src/app/admin/admin-ldn-services/ldn-service-serviceMock/ldnServicesRD$-mock.ts
new file mode 100644
index 0000000000..fccb374b5f
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-service-serviceMock/ldnServicesRD$-mock.ts
@@ -0,0 +1,73 @@
+import { LdnService } from '../ldn-services-model/ldn-services.model';
+import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PaginatedList } from '../../../core/data/paginated-list.model';
+import { Observable, of } from 'rxjs';
+// Create a mock data object for a single LDN notify service
+export const mockLdnService: LdnService = {
+ id: 1,
+ name: 'Service Name',
+ description: 'Service Description',
+ url: 'Service URL',
+ ldnUrl: 'Service LDN URL',
+ notifyServiceInboundPatterns: [
+ {
+ pattern: 'patternA',
+ constraint: 'itemFilterA',
+ automatic: false,
+ },
+ {
+ pattern: 'patternB',
+ constraint: 'itemFilterB',
+ automatic: true,
+ },
+ ],
+ notifyServiceOutboundPatterns: [
+ {
+ pattern: 'patternC',
+ constraint: 'itemFilterC',
+ },
+ ],
+ type: LDN_SERVICE,
+ _links: {
+ self: {
+ href: 'http://localhost/api/ldn/ldnservices/1',
+ },
+ },
+};
+
+
+
+
+const mockLdnServices = {
+ payload: {
+ elementsPerPage: 20,
+ totalPages: 1,
+ totalElements: 1,
+ currentPage: 1,
+ first: undefined,
+ prev: undefined,
+ next: undefined,
+ last: undefined,
+ page: [mockLdnService],
+ type: LDN_SERVICE,
+ self: undefined,
+ getPageLength: function() {
+ return this.page.length;
+ },
+ _links: {
+ self: {
+ href: 'http://localhost/api/ldn/ldnservices/1',
+ },
+ page: [],
+ },
+ },
+ hasSucceeded: true,
+ msToLive: 0,
+};
+
+
+
+
+// Create a mock ldnServicesRD$ observable
+export const mockLdnServicesRD$: Observable>> = of((mockLdnServices as unknown) as RemoteData>);
diff --git a/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts b/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts
new file mode 100644
index 0000000000..43755adb3c
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service.ts
@@ -0,0 +1,90 @@
+ import { Injectable } from '@angular/core';
+import { dataService } from '../../../core/data/base/data-service.decorator';
+import { LDN_SERVICE } from '../ldn-services-model/ldn-service.resource-type';
+import { IdentifiableDataService } from '../../../core/data/base/identifiable-data.service';
+import { FindAllData, FindAllDataImpl } from '../../../core/data/base/find-all-data';
+import { DeleteData, DeleteDataImpl } from '../../../core/data/base/delete-data';
+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 { FindListOptions } from '../../../core/data/find-list-options.model';
+import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
+import { Observable } from 'rxjs';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PaginatedList } from '../../../core/data/paginated-list.model';
+import { NoContent } from '../../../core/shared/NoContent.model';
+import { map, take } from 'rxjs/operators';
+import { URLCombiner } from '../../../core/url-combiner/url-combiner';
+import { MultipartPostRequest } from '../../../core/data/request.models';
+import { RestRequest } from '../../../core/data/rest-request.model';
+import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
+import { hasValue } from '../../../shared/empty.util';
+
+import { LdnService } from '../ldn-services-model/ldn-services.model';
+import { LdnServiceConstraint } from '../ldn-services-model/ldn-service-constraint.model';
+
+@Injectable()
+@dataService(LDN_SERVICE)
+export class LdnServicesService extends IdentifiableDataService implements FindAllData, DeleteData {
+ private findAllData: FindAllDataImpl; // Corrected the type
+ private deleteData: DeleteDataImpl; // Corrected the type
+
+ constructor(
+ protected requestService: RequestService,
+ protected rdbService: RemoteDataBuildService,
+ protected objectCache: ObjectCacheService,
+ protected halService: HALEndpointService,
+ protected notificationsService: NotificationsService,
+ ) {
+ super('ldnservices', requestService, rdbService, objectCache, halService);
+
+ this.findAllData = new FindAllDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
+ this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
+ }
+
+ findAll(options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig[]): Observable>> {
+ return this.findAllData.findAll(options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
+ }
+
+ public delete(objectId: string, copyVirtualMetadata?: string[]): Observable> {
+ return this.deleteData.delete(objectId, copyVirtualMetadata);
+ }
+
+ public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable> {
+ return this.deleteData.deleteByHref(href, copyVirtualMetadata);
+ }
+
+ public invoke(serviceName: string, parameters: LdnServiceConstraint[], files: File[]): Observable> {
+ const requestId = this.requestService.generateRequestId();
+ this.getBrowseEndpoint().pipe(
+ take(1),
+ map((endpoint: string) => new URLCombiner(endpoint, serviceName, 'processes').toString()),
+ map((endpoint: string) => {
+ const body = this.getInvocationFormData(parameters, files);
+ return new MultipartPostRequest(requestId, endpoint, body);
+ })
+ ).subscribe((request: RestRequest) => this.requestService.send(request));
+
+ return this.rdbService.buildFromRequestUUID(requestId);
+ }
+
+ private getInvocationFormData(constrain: LdnServiceConstraint[], files: File[]): FormData {
+ const form: FormData = new FormData();
+ form.set('properties', JSON.stringify(constrain));
+ files.forEach((file: File) => {
+ form.append('file', file);
+ });
+ return form;
+ }
+
+ public ldnServiceWithNameExistsAndCanExecute(scriptName: string): Observable {
+ return this.findById(scriptName).pipe(
+ getFirstCompletedRemoteData(),
+ map((rd: RemoteData) => {
+ return hasValue(rd.payload);
+ }),
+ );
+ }
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html
new file mode 100644
index 0000000000..e3795d6e96
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html
@@ -0,0 +1,80 @@
+
+
+
{{ 'ldn-registered-services.title' | translate }}
+
+
+
+
+
0"
+ [collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
+ [hideGear]="true"
+ [hidePagerWhenSinglePage]="true"
+ [pageInfoState]="(ldnServicesRD$ | async)?.payload"
+ [paginationOptions]="pageConfig">
+
+
+
+
+ {{ 'service.overview.table.name' | translate }} |
+ {{ 'service.overview.table.description' | translate }} |
+ {{ 'service.overview.table.status' | translate }} |
+ {{ 'service.overview.table.actions' | translate }} |
+
+
+
+
+ {{ ldnService.name }} |
+ {{ ldnService.description }} |
+
+
+ {{ ldnService.enabled ? ('ldn-service.overview.table.enabled' | translate) : ('ldn-service.overview.table.disabled' | translate) }}
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'service.overview.delete.body' | translate }}
+
+
+
+
+
+
+
+
+
diff --git a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.scss b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.scss
new file mode 100644
index 0000000000..07377d63d5
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.scss
@@ -0,0 +1,29 @@
+.status-indicator {
+ padding: 2.5px 25px 2.5px 25px;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.5s;
+}
+
+.status-enabled {
+ background-color: #daf7a6;
+ color: #4f5359;
+ font-size: 85%;
+ font-weight: bold;
+
+}
+
+.status-enabled:hover {
+ background-color: #faa0a0;
+}
+
+.status-disabled {
+ background-color: #faa0a0;
+ color: #4f5359;
+ font-size: 85%;
+ font-weight: bold;
+}
+
+.status-disabled:hover {
+ background-color: #daf7a6;
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.spec.ts b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.spec.ts
new file mode 100644
index 0000000000..bedcabb271
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ServicesDirectoryComponent } from './services-directory.component';
+
+describe('ServicesDirectoryComponent', () => {
+ let component: ServicesDirectoryComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ServicesDirectoryComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ServicesDirectoryComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.ts b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.ts
new file mode 100644
index 0000000000..9d19ad56c7
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.ts
@@ -0,0 +1,163 @@
+import { ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { LdnDirectoryService } from '../ldn-services-services/ldn-directory.service';
+import { Observable, Subscription } from 'rxjs';
+import { RemoteData } from '../../../core/data/remote-data';
+import { PaginatedList } from '../../../core/data/paginated-list.model';
+import { FindListOptions } from '../../../core/data/find-list-options.model';
+import { LdnService } from '../ldn-services-model/ldn-services.model';
+import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
+import { switchMap } from 'rxjs/operators';
+import { LdnServicesService } from 'src/app/admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
+import { PaginationService } from 'src/app/core/pagination/pagination.service';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { hasValue } from '../../../shared/empty.util';
+import { HttpClient } from '@angular/common/http';
+@Component({
+ selector: 'ds-ldn-services-directory',
+ templateUrl: './ldn-services-directory.component.html',
+ styleUrls: ['./ldn-services-directory.component.scss'],
+})
+export class LdnServicesOverviewComponent implements OnInit, OnDestroy {
+
+ selectedServiceId: number | null = null;
+ servicesData: any[] = [];
+ @ViewChild('deleteModal', {static: true}) deleteModal: TemplateRef;
+ ldnServicesRD$: Observable>>;
+ config: FindListOptions = Object.assign(new FindListOptions(), {
+ elementsPerPage: 20
+ });
+ pageConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
+ id: 'po',
+ pageSize: 20
+ });
+ isProcessingSub: Subscription;
+ private modalRef: any;
+
+ constructor(
+ protected processLdnService: LdnServicesService,
+ protected paginationService: PaginationService,
+ protected modalService: NgbModal,
+ public ldnDirectoryService: LdnDirectoryService,
+ private http: HttpClient,
+ private cdRef: ChangeDetectorRef
+ ) {
+ }
+
+ ngOnInit(): void {
+ /*this.ldnDirectoryService.listLdnServices();*/
+ this.findAllServices();
+ this.setLdnServices();
+ /*this.ldnServicesRD$.subscribe(data => {
+ console.log('searchByLdnUrl()', data);
+ });*/
+
+ /*this.ldnServicesRD$.pipe(
+ tap(data => {
+ console.log('ldnServicesRD$ data:', data);
+ })
+ ).subscribe(() => {
+ this.searchByLdnUrl();
+ });*/
+
+ }
+
+ setLdnServices() {
+ this.ldnServicesRD$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config).pipe(
+ switchMap((config) => this.processLdnService.findAll(config, true, false))
+ );
+ console.log();
+ }
+
+ ngOnDestroy(): void {
+ this.paginationService.clearPagination(this.pageConfig.id);
+ if (hasValue(this.isProcessingSub)) {
+ this.isProcessingSub.unsubscribe();
+ }
+ }
+
+ openDeleteModal(content) {
+ this.modalRef = this.modalService.open(content);
+ }
+
+ closeModal() {
+ this.modalRef.close();
+ this.cdRef.detectChanges();
+ }
+
+
+ findAllServices(): void {
+ this.retrieveAll().subscribe(
+ (response) => {
+ this.servicesData = response._embedded.ldnservices;
+ console.log('ServicesData =', this.servicesData);
+ this.cdRef.detectChanges();
+ },
+ (error) => {
+ console.error('Error:', error);
+ }
+ );
+ }
+
+ retrieveAll(): Observable {
+ const url = 'http://localhost:8080/server/api/ldn/ldnservices';
+ return this.http.get(url);
+ }
+
+
+
+
+ deleteSelected() {
+ if (this.selectedServiceId !== null) {
+ const deleteUrl = `http://localhost:8080/server/api/ldn/ldnservices/${this.selectedServiceId}`;
+ this.http.delete(deleteUrl).subscribe(
+ () => {
+ this.closeModal();
+ this.findAllServices();
+ },
+ (error) => {
+ console.error('Error deleting service:', error);
+ }
+ );
+ }
+ }
+
+ selectServiceToDelete(serviceId: number) {
+ this.selectedServiceId = serviceId;
+ this.openDeleteModal(this.deleteModal);
+ }
+
+ toggleStatus(ldnService: any): void {
+ const newStatus = !ldnService.enabled;
+
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${ldnService.id}`;
+ const patchOperation = {
+ op: 'replace',
+ path: '/enabled',
+ value: newStatus,
+ };
+
+ this.http.patch(apiUrl, [patchOperation]).subscribe(
+ () => {
+ console.log('Status updated successfully.');
+ ldnService.enabled = newStatus;
+ this.cdRef.detectChanges();
+ },
+ (error) => {
+ console.error('Error updating status:', error);
+ }
+ );
+ }
+
+ fetchServiceData(serviceId: string): void {
+ const apiUrl = `http://localhost:8080/server/api/ldn/ldnservices/${serviceId}`;
+
+ this.http.get(apiUrl).subscribe(
+ (data: any) => {
+ console.log(data);
+ },
+ (error) => {
+ console.error('Error fetching service data:', error);
+ }
+ );
+ }
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services-guard.service.ts b/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services-guard.service.ts
new file mode 100644
index 0000000000..85235b4370
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services-guard.service.ts
@@ -0,0 +1,29 @@
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LdnServicesGuard implements CanActivate {
+
+ constructor(
+ //private notifyInfoService: NotifyInfoService,
+ private router: Router
+ ) {}
+ canActivate(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree {
+ return true;
+ /*return this.notifyInfoService.isCoarConfigEnabled().pipe(
+ map(coarLdnEnabled => {
+ if (coarLdnEnabled) {
+ return true;
+ } else {
+ return this.router.parseUrl('/404');
+ }
+ })
+ );*/
+ }
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services.guard.spec.ts b/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services.guard.spec.ts
new file mode 100644
index 0000000000..30af31cab8
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-guard/ldn-services.guard.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LdnServicesGuard } from './ldn-services-guard.service';
+
+describe('LdnServicesGuard', () => {
+ let guard: LdnServicesGuard;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ guard = TestBed.inject(LdnServicesGuard);
+ });
+
+ it('should be created', () => {
+ // @ts-ignore
+ expect(guard).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-constraint.model.ts b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-constraint.model.ts
new file mode 100644
index 0000000000..704a3e7d8c
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-constraint.model.ts
@@ -0,0 +1,26 @@
+
+/**
+ * A cosntrain that can be used when running a service
+ */
+export class LdnServiceConstraint {
+ /**
+ * The name of the constrain
+ */
+ name: string;
+
+ /**
+ * The value of the constrain
+ */
+ value: string;
+}
+
+export const EndorsmentConstrain = [
+ {
+ name: 'Type 1 Item',
+ value: 'Type1'
+ },
+ {
+ name: 'Type 2 Item',
+ value: 'Type2'
+ },
+];
diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-status.model.ts b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-status.model.ts
new file mode 100644
index 0000000000..040e4d37b8
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service-status.model.ts
@@ -0,0 +1,8 @@
+/**
+ * List of services statuses
+ */
+export enum LdnServiceStatus {
+ UNKOWN,
+ DISABLED,
+ ENABLED,
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.resource-type.ts b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.resource-type.ts
new file mode 100644
index 0000000000..937fac255d
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-service.resource-type.ts
@@ -0,0 +1,9 @@
+/**
+ * The resource type for Ldn-Services
+ *
+ * Needs to be in a separate file to prevent circular
+ * dependencies in webpack.
+ */
+import { ResourceType } from '../../../core/shared/resource-type';
+
+export const LDN_SERVICE = new ResourceType('ldnservices');
diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/ldn-services.model.ts b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-services.model.ts
new file mode 100644
index 0000000000..bdb8bc5123
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-model/ldn-services.model.ts
@@ -0,0 +1,58 @@
+import { ResourceType } from '../../../core/shared/resource-type';
+import { CacheableObject } from '../../../core/cache/cacheable-object.model';
+import { autoserialize, deserialize } from 'cerialize';
+import { LDN_SERVICE } from './ldn-service.resource-type';
+import { excludeFromEquals } from '../../../core/utilities/equals.decorators';
+import { typedObject } from '../../../core/cache/builders/build-decorators';
+
+
+@typedObject
+export class LdnService extends CacheableObject {
+ static type = LDN_SERVICE;
+
+ @excludeFromEquals
+ @autoserialize
+ type: ResourceType;
+
+ @autoserialize
+ id?: number;
+
+ @autoserialize
+ name: string;
+
+ @autoserialize
+ description: string;
+
+ @autoserialize
+ url: string;
+
+ @autoserialize
+ ldnUrl: string;
+
+ @autoserialize
+ notifyServiceInboundPatterns?: NotifyServicePattern[];
+
+ @autoserialize
+ notifyServiceOutboundPatterns?: NotifyServicePattern[];
+
+ @deserialize
+ _links: {
+ self: {
+ href: string;
+ };
+ };
+
+ get self(): string {
+ return this._links.self.href;
+ }
+}
+
+
+class NotifyServicePattern {
+ @autoserialize
+ pattern: string;
+ @autoserialize
+ constraint?: string;
+ @autoserialize
+ automatic?: boolean;
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-model/service-constrain-type.model.ts b/src/app/admin/admin-ldn-services/ldn-services-model/service-constrain-type.model.ts
new file mode 100644
index 0000000000..d3f55c815e
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-model/service-constrain-type.model.ts
@@ -0,0 +1,10 @@
+/**
+ * List of parameter types used for scripts
+ */
+export enum LdnServiceConstrainType {
+ STRING = 'String',
+ DATE = 'date',
+ BOOLEAN = 'boolean',
+ FILE = 'InputStream',
+ OUTPUT = 'OutputStream'
+}
diff --git a/src/app/admin/admin-ldn-services/ldn-services-patterns/ldn-service-coar-patterns.ts b/src/app/admin/admin-ldn-services/ldn-services-patterns/ldn-service-coar-patterns.ts
new file mode 100644
index 0000000000..14d227c131
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-patterns/ldn-service-coar-patterns.ts
@@ -0,0 +1,73 @@
+export const notifyPatterns = [
+ {
+ name: 'Acknowledge and Accept',
+ description: 'This pattern is used to acknowledge and accept a request (offer). It implies an intention to act on the request.',
+ category: 'Acknowledgements'
+ },
+ {
+ name: 'Acknowledge and Reject',
+ description: 'This pattern is used to acknowledge and reject a request (offer). It signifies no further action regarding the request.',
+ category: 'Acknowledgements'
+ },
+ {
+ name: 'Acknowledge and Tentatively Accept',
+ description: 'This pattern is used to acknowledge and tentatively accept a request (offer). It implies an intention to act, which may change.',
+ category: 'Acknowledgements'
+ },
+ {
+ name: 'Acknowledge and Tentatively Reject',
+ description: 'This pattern is used to acknowledge and tentatively reject a request (offer). It signifies no further action, subject to change.',
+ category: 'Acknowledgements'
+ },
+ {
+ name: 'Announce Endorsement',
+ description: 'This pattern is used to announce the existence of an endorsement, referencing the endorsed resource.',
+ category: 'Announcements'
+ },
+ {
+ name: 'Announce Ingest',
+ description: 'This pattern is used to announce that a resource has been ingested.',
+ category: 'Announcements'
+ },
+ {
+ name: 'Announce Relationship',
+ description: 'This pattern is used to announce a relationship between two resources.',
+ category: 'Announcements'
+ },
+ {
+ name: 'Announce Review',
+ description: 'This pattern is used to announce the existence of a review, referencing the reviewed resource.',
+ category: 'Announcements'
+ },
+ {
+ name: 'Announce Service Result',
+ description: 'This pattern is used to announce the existence of a "service result", referencing the relevant resource.',
+ category: 'Announcements'
+ },
+ {
+ name: 'Request Endorsement',
+ description: 'This pattern is used to request endorsement of a resource owned by the origin system.',
+ category: 'Requests'
+ },
+ {
+ name: 'Request Ingest',
+ description: 'This pattern is used to request that the target system ingest a resource.',
+ category: 'Requests'
+ },
+ {
+ name: 'Request Review',
+ description: 'This pattern is used to request a review of a resource owned by the origin system.',
+ category: 'Requests'
+ },
+ {
+ name: 'Undo Offer',
+ description: 'This pattern is used to undo (retract) an offer previously made.',
+ category: 'Undo'
+ }
+];
+
+
+const pattern = notifyPatterns[0];
+console.log(`Pattern Name: ${pattern.name}`);
+console.log(`Pattern Description: ${pattern.description}`);
+console.log(`Pattern Category: ${pattern.category}`);
diff --git a/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.spec.ts b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.spec.ts
new file mode 100644
index 0000000000..e24508e942
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LdnDirectoryService } from './ldn-directory.service';
+
+describe('LdnDirectoryService', () => {
+ let service: LdnDirectoryService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(LdnDirectoryService);
+ });
+
+ it('should be created', () => {
+ // @ts-ignore
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.ts b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.ts
new file mode 100644
index 0000000000..5ed32f5bb7
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-directory.service.ts
@@ -0,0 +1,58 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, tap } from 'rxjs';
+import { LdnService } from '../ldn-services-model/ldn-services.model';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class LdnDirectoryService {
+ private baseUrl = 'http://localhost:8080/server/api/ldn/ldnservices';
+ private itemFilterEndpoint = 'http://localhost:8080/server/api/config/itemfilters';
+
+
+ constructor(private http: HttpClient) {}
+
+
+ public listLdnServices(): Observable {
+ const endpoint = `${this.baseUrl}`;
+ return this.http.get(endpoint).pipe(
+ tap(data => {
+ console.log('listLdnServices() Data:', data);
+ })
+ );
+ }
+
+ public getLdnServiceById(id: string): Observable {
+ const endpoint = `${this.baseUrl}/${id}`;
+ return this.http.get(endpoint);
+ }
+
+ public createLdnService(ldnService: LdnService): Observable {
+ return this.http.post(this.baseUrl, ldnService);
+ }
+
+ public updateLdnService(id: string, ldnService: LdnService): Observable {
+ const endpoint = `${this.baseUrl}/${id}`;
+ return this.http.put(endpoint, ldnService);
+ }
+
+ public deleteLdnService(id: string): Observable {
+ const endpoint = `${this.baseUrl}/${id}`;
+ return this.http.delete(endpoint);
+ }
+
+ public searchLdnServicesByLdnUrl(ldnUrl: string): Observable {
+ const endpoint = `${this.baseUrl}/search/byLdnUrl?ldnUrl=${ldnUrl}`;
+ return this.http.get(endpoint);
+ }
+
+ public getItemFilters(): Observable {
+ const itemFiltersEndpoint = `${this.itemFilterEndpoint}`;
+ return this.http.get(itemFiltersEndpoint);
+ }
+
+}
+
+
+
diff --git a/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.spec.ts b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.spec.ts
new file mode 100644
index 0000000000..38e1a4de2b
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LdnServicesBulkDeleteService } from './ldn-service-bulk-delete.service';
+
+describe('LdnServiceBulkDeleteService', () => {
+ let service: LdnServicesBulkDeleteService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(LdnServicesBulkDeleteService);
+ });
+
+ it('should be created', () => {
+ // @ts-ignore
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.ts b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.ts
new file mode 100644
index 0000000000..6805cd7f93
--- /dev/null
+++ b/src/app/admin/admin-ldn-services/ldn-services-services/ldn-service-bulk-delete.service.ts
@@ -0,0 +1,117 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, count, from } from 'rxjs';
+import { LdnServicesService } from '../ldn-services-data/ldn-services-data.service';
+import { NotificationsService } from '../../../shared/notifications/notifications.service';
+import { TranslateService } from '@ngx-translate/core';
+import { isNotEmpty } from '../../../shared/empty.util';
+import { concatMap, filter, tap } from 'rxjs/operators';
+import { getFirstCompletedRemoteData } from '../../../core/shared/operators';
+import { RemoteData } from '../../../core/data/remote-data';
+import { LdnService } from '../ldn-services-model/ldn-services.model';
+@Injectable({
+ providedIn: 'root'
+})
+/**
+ * Service to facilitate removing ldn services in bulk.
+ */
+export class LdnServicesBulkDeleteService {
+
+ /**
+ * Array to track the services to be deleted
+ */
+ ldnServicesToDelete: string[] = [];
+
+ /**
+ * Behavior subject to track whether the delete is processing
+ * @protected
+ */
+ protected isProcessingBehaviorSubject: BehaviorSubject = new BehaviorSubject(false);
+
+ constructor(
+ protected processLdnService: LdnServicesService,
+ protected notificationsService: NotificationsService,
+ protected translateService: TranslateService
+ ) {
+ }
+
+ /**
+ * Add or remove a process id to/from the list
+ * If the id is already present it will be removed, otherwise it will be added.
+ *
+ * @param notifyServiceName - The process id to add or remove
+ */
+ toggleDelete(notifyServiceName: string) {
+ if (this.isToBeDeleted(notifyServiceName)) {
+ this.ldnServicesToDelete.splice(this.ldnServicesToDelete.indexOf(notifyServiceName), 1);
+ } else {
+ this.ldnServicesToDelete.push(notifyServiceName);
+ }
+ }
+
+ /**
+ * Checks if the provided service id is present in the to be deleted list
+ * @param notifyServiceName
+ */
+ isToBeDeleted(notifyServiceName: string) {
+ return this.ldnServicesToDelete.includes(notifyServiceName);
+ }
+
+ /**
+ * Clear the list of services to be deleted
+ */
+ clearAllServices() {
+ this.ldnServicesToDelete.splice(0);
+ }
+
+ /**
+ * Get the amount of processes selected for deletion
+ */
+ getAmountOfSelectedServices() {
+ return this.ldnServicesToDelete.length;
+ }
+
+ /**
+ * Returns a behavior subject to indicate whether the bulk delete is processing
+ */
+ isProcessing$() {
+ return this.isProcessingBehaviorSubject;
+ }
+
+ /**
+ * Returns whether there currently are values selected for deletion
+ */
+ hasSelected(): boolean {
+ return isNotEmpty(this.ldnServicesToDelete);
+ }
+
+ /**
+ * Delete all selected processes one by one
+ * When the deletion for a process fails, an error notification will be shown with the process id,
+ * but it will continue deleting the other processes.
+ * At the end it will show a notification stating the amount of successful deletes
+ * The successfully deleted processes will be removed from the list of selected values, the failed ones will be retained.
+ */
+ deleteSelectedLdnServices() {
+ this.isProcessingBehaviorSubject.next(true);
+
+ from([...this.ldnServicesToDelete]).pipe(
+ concatMap((notifyServiceName) => {
+ return this.processLdnService.delete(notifyServiceName).pipe(
+ getFirstCompletedRemoteData(),
+ tap((rd: RemoteData) => {
+ if (rd.hasFailed) {
+ this.notificationsService.error(this.translateService.get('process.bulk.delete.error.head'), this.translateService.get('process.bulk.delete.error.body', {processId: notifyServiceName}));
+ } else {
+ this.toggleDelete(notifyServiceName);
+ }
+ })
+ );
+ }),
+ filter((rd: RemoteData) => rd.hasSucceeded),
+ count(),
+ ).subscribe((value) => {
+ this.notificationsService.success(this.translateService.get('process.bulk.delete.success', {count: value}));
+ this.isProcessingBehaviorSubject.next(false);
+ });
+ }
+}
diff --git a/src/app/admin/admin-routing-paths.ts b/src/app/admin/admin-routing-paths.ts
index 30f801cecb..df0459ff53 100644
--- a/src/app/admin/admin-routing-paths.ts
+++ b/src/app/admin/admin-routing-paths.ts
@@ -11,3 +11,5 @@ export function getRegistriesModuleRoute() {
export function getNotificationsModuleRoute() {
return new URLCombiner(getAdminModuleRoute(), NOTIFICATIONS_MODULE_PATH).toString();
}
+
+export const LDN_PATH = 'ldn';
diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts
index a7d19a6935..3acc219bce 100644
--- a/src/app/admin/admin-routing.module.ts
+++ b/src/app/admin/admin-routing.module.ts
@@ -6,7 +6,7 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
import { AdminWorkflowPageComponent } from './admin-workflow-page/admin-workflow-page.component';
import { I18nBreadcrumbsService } from '../core/breadcrumbs/i18n-breadcrumbs.service';
import { AdminCurationTasksComponent } from './admin-curation-tasks/admin-curation-tasks.component';
-import { REGISTRIES_MODULE_PATH, NOTIFICATIONS_MODULE_PATH } from './admin-routing-paths';
+import { LDN_PATH, REGISTRIES_MODULE_PATH, NOTIFICATIONS_MODULE_PATH } from './admin-routing-paths';
import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component';
@NgModule({
@@ -52,13 +52,24 @@ import { BatchImportPageComponent } from './admin-import-batch-page/batch-import
component: BatchImportPageComponent,
data: { title: 'admin.batch-import.title', breadcrumbKey: 'admin.batch-import' }
},
+ {
+ path: LDN_PATH,
+ children: [
+ { path: '', pathMatch: 'full', redirectTo: 'services' },
+ {
+ path: 'services',
+ loadChildren: () => import('./admin-ldn-services/admin-ldn-services.module')
+ .then((m) => m.AdminLdnServicesModule),
+ }
+ ]
+ },
{
path: 'system-wide-alert',
resolve: { breadcrumb: I18nBreadcrumbResolver },
loadChildren: () => import('../system-wide-alert/system-wide-alert.module').then((m) => m.SystemWideAlertModule),
data: {title: 'admin.system-wide-alert.title', breadcrumbKey: 'admin.system-wide-alert'}
},
- ])
+ ]),
],
providers: [
I18nBreadcrumbResolver,
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index 7acf132df9..e1b1fc8811 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -187,6 +187,7 @@ import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-brows
import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model';
import { SuggestionTarget } from './suggestion-notifications/reciter-suggestions/models/suggestion-target.model';
import { SuggestionSource } from './suggestion-notifications/reciter-suggestions/models/suggestion-source.model';
+import { LdnServicesService } from '../admin/admin-ldn-services/ldn-services-data/ldn-services-data.service';
/**
* When not in production, endpoint responses can be mocked for testing purposes
@@ -309,7 +310,8 @@ const PROVIDERS = [
OrcidAuthService,
OrcidQueueDataService,
OrcidHistoryDataService,
- SupervisionOrderDataService
+ SupervisionOrderDataService,
+ LdnServicesService,
];
/**
diff --git a/src/app/menu.resolver.ts b/src/app/menu.resolver.ts
index 70e2b6462f..23ba31b103 100644
--- a/src/app/menu.resolver.ts
+++ b/src/app/menu.resolver.ts
@@ -222,6 +222,18 @@ export class MenuResolver implements Resolve {
text: 'menu.section.new_process',
link: '/processes/new'
} as LinkMenuItemModel,
+ },/* ldn_services */
+ {
+ id: 'ldn_services_new',
+ parentID: 'new',
+ active: false,
+ visible: isSiteAdmin,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.services_new',
+ link: '/admin/ldn/services/new'
+ } as LinkMenuItemModel,
+ icon: '',
},
];
const editSubMenuList = [
@@ -350,6 +362,19 @@ export class MenuResolver implements Resolve {
icon: 'terminal',
index: 10
},
+ /* LDN Services */
+ {
+ id: 'ldn_services',
+ active: false,
+ visible: isSiteAdmin,
+ model: {
+ type: MenuItemType.LINK,
+ text: 'menu.section.services',
+ link: '/admin/ldn/services'
+ } as LinkMenuItemModel,
+ icon: 'inbox',
+ index: 14
+ },
{
id: 'health',
active: false,
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5
index 2a22e715cc..7a68fc60c8 100644
--- a/src/assets/i18n/en.json5
+++ b/src/assets/i18n/en.json5
@@ -902,7 +902,67 @@
"coar-notify-support.message-moderation.content": "To ensure a secure and productive environment, all incoming LDN messages are moderated. If you are planning to exchange information with us, kindly reach out via our dedicated Feedback form. You can access the Feedback form by clicking here.",
+ "service.overview.delete.header": "Delete Service",
+ "ldn-registered-services.title": "Registered Services",
+ "ldn-registered-services.table.name":"Name",
+ "ldn-registered-services.table.description": "Description",
+ "ldn-registered-services.table.status": "Status",
+ "ldn-registered-services.table.action": "Action",
+ "ldn-registered-services.new": "NEW",
+ "ldn-registered-services.new.breadcrumbs": "Registered Services",
+
+ "ldn-service.overview.table.enabled": "Enabled",
+ "ldn-service.overview.table.disabled": "Disabled",
+ "ldn-service.overview.table.clickToEnable": "Click to enable",
+ "ldn-service.overview.table.clickToDisable": "Click to disable",
+
+ "ldn-service-status": "Status",
+ "ldn-register-new-service.title": "Register a new service",
+ "ldn-new-service.form.label.submit": "Submit",
+ "ldn-new-service.form.label.name": "Name",
+ "ldn-new-service.form.label.description": "Description",
+ "ldn-new-service.form.label.url": "Service 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.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.ldnUrl": "Please specify the URL of the LDN Inbox",
+ "ldn-new-service.form.label.inboundPattern": "Inbound Patterns",
+ "ldn-new-service.form.label.placeholder.inboundPattern": "Select an Inbound Pattern",
+ "ldn-new-service.form.label.placeholder.selectedItemFilter": "No Item Filter Selected",
+ "ldn-new-service.form.label.ItemFilter": "Item Filter",
+ "ldn-new-service.form.label.automatic": "Automatic",
+ "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",
+ "service.overview.delete.body": "Are you sure you want to delete this service?",
+ "service.overview.delete": "Delete service",
+ "ldn-edit-service.title": "Edit service",
+ "ldn-edit-service.form.label.name": "Name",
+ "ldn-edit-service.form.label.description": "Description",
+ "ldn-edit-service.form.label.url": "Service URL",
+ "ldn-edit-service.form.label.ldnUrl": "LDN Inbox URL",
+ "ldn-edit-service.form.label.inboundPattern": "Inbound Pattern",
+ "ldn-edit-service.form.label.noInboundPatternSelected": "No Inbound Pattern",
+ "ldn-edit-service.form.label.selectedItemFilter": "Selected Item Filter",
+ "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": "Submit",
+ "ldn-edit-service.breadcrumbs": "Edit Service",
+
+
+
+ "ldn-register-new-service.notification.error.title": "Error",
+ "ldn-register-new-service.notification.error.content": "An error occurred while creating this process",
+ "ldn-register-new-service.notification.success.title": "Success",
+ "ldn-register-new-service.notification.success.content": "The process was successfully created",
"collection.create.head": "Create a Collection",
@@ -2920,6 +2980,8 @@
"menu.section.icon.notifications": "Notifications menu section",
+ "menu.section.icon.ldn_services": "LDN Services overview",
+
"menu.section.import": "Import",
"menu.section.import_batch": "Batch Import (ZIP)",
@@ -2958,6 +3020,10 @@
"menu.section.registries_metadata": "Metadata",
+ "menu.section.services": "LDN Services",
+
+ "menu.section.services_new": "LDN Service",
+
"menu.section.statistics": "Statistics",
"menu.section.statistics_task": "Statistics Task",
@@ -3346,6 +3412,10 @@
"process.new.breadcrumbs": "Create a new process",
+
+
+
+
"process.detail.arguments": "Arguments",
"process.detail.arguments.empty": "This process doesn't contain any arguments",
@@ -3428,6 +3498,39 @@
"process.bulk.delete.success": "{{count}} process(es) have been succesfully deleted",
+ "service.detail.delete.cancel": "Cancel",
+
+ "service.detail.delete.button": "Delete service",
+
+ "service.detail.delete.header": "Delete service",
+
+ "service.detail.delete.body": "Are you sure you want to delete the current service?",
+
+ "service.detail.delete.confirm": "Delete service",
+
+ "service.detail.delete.success": "The service was successfully deleted.",
+
+ "service.detail.delete.error": "Something went wrong when deleting the service",
+
+ "service.overview.table.id": "Services ID",
+
+ "service.overview.table.name": "Name",
+
+ "service.overview.table.start": "Start time (UTC)",
+
+ "service.overview.table.status": "Status",
+
+ "service.overview.table.user": "User",
+
+ "service.overview.title": "Services Overview",
+
+ "service.overview.breadcrumbs": "Services Overview",
+
+ "service.overview.table.actions": "Actions",
+
+ "service.overview.table.description": "Description",
+
+
"profile.breadcrumbs": "Update Profile",
"profile.card.identify": "Identify",