This commit is contained in:
Michele Artini 2023-03-23 12:24:56 +01:00
parent 4e92795b4a
commit ff908c7af3
19 changed files with 154 additions and 60 deletions

View File

@ -3,17 +3,18 @@ package eu.dnetlib.manager.wf;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.is.info.KeyValue;
import eu.dnetlib.manager.wf.model.WorkflowConfiguration;
import eu.dnetlib.manager.wf.model.WorkflowSection;
import eu.dnetlib.manager.wf.model.WorkflowSubscription;
import eu.dnetlib.manager.wf.workflows.procs.ExecutionStatus;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
@ -22,18 +23,18 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody;
@RequestMapping("/ajax/wfs")
public class WfConfigurationsController extends AbstractDnetController {
@Autowired
private WorkflowManagerService wfManagerService;
@GetMapping("/sections")
public List<KeyValue<Long>> listWfSections() throws Exception {
return wfManagerService.streamSections()
.map(x -> new KeyValue<>(x.getValue(), x.getCount()))
.collect(Collectors.toList());
public List<WorkflowSection> listWfSections() throws Exception {
return wfManagerService.listSsections();
}
@GetMapping("/search")
public List<KeyValue<String>> listWfConfigurations(@RequestParam final String section) throws Exception {
return wfManagerService.streamWfConfigurationsBySection(section)
@GetMapping("/sections/{section}")
public List<KeyValue<String>> listWfConfigurations(@PathVariable final String section) throws Exception {
return wfManagerService.listWfConfigurationsBySection(section)
.stream()
.map(x -> new KeyValue<>(x.getId(), x.getName()))
.collect(Collectors.toList());
}

View File

@ -10,7 +10,7 @@ import { DsmSearchComponent, DsmResultsComponent, DsmApiComponent } from './dsm/
import { MdstoreInspectorComponent, MdstoresComponent } from './mdstores/mdstores.component';
import { CleanerTesterComponent } from './cleaner-tester/cleaner-tester.component';
import { EmailsComponent } from './emails/emails.component';
import { WfInstancesComponent } from './wf-instances/wf-instances.component';
import { WfConfsComponent } from './wf-confs/wf-confs.component';
const routes: Routes = [
{ path: "", redirectTo: 'info', pathMatch: 'full' },
@ -20,7 +20,7 @@ const routes: Routes = [
{ path: "adv_resources/vocabulary", component: VocabulariesComponent },
{ path: "adv_resources/protocol", component: ProtocolsComponent },
{ path: "adv_resources/email", component: EmailsComponent },
{ path: "wfs", component: WfInstancesComponent },
{ path: "wfs/:section", component: WfConfsComponent },
{ path: "wf_history", component: WfHistoryComponent },
{ path: "ctx_viewer", component: ContextViewerComponent },
{ path: "voc_editor", component: VocabularyEditorComponent },

View File

@ -37,7 +37,7 @@ import { SpinnerHttpInterceptor } from './common/spinner.service';
import { MdstoresComponent, MdstoreInspectorComponent, MDStoreVersionsDialog, AddMDStoreDialog } from './mdstores/mdstores.component';
import { CleanerTesterComponent } from './cleaner-tester/cleaner-tester.component';
import { EmailDialog, EmailsComponent } from './emails/emails.component';
import { WfInstancesComponent } from './wf-instances/wf-instances.component';
import { WfConfsComponent } from './wf-confs/wf-confs.component';
import { MatTabsModule } from '@angular/material/tabs';
@NgModule({
@ -72,7 +72,7 @@ import { MatTabsModule } from '@angular/material/tabs';
CleanerTesterComponent,
EmailsComponent,
EmailDialog,
WfInstancesComponent
WfConfsComponent
],
imports: [
BrowserModule,

View File

@ -207,9 +207,15 @@ export interface EmailTemplate {
message: string
}
export interface WfSection {
id: string,
name: string
}
export interface WfConf {
id: string,
name: string,
section?: string,
details: Map<string, string>,
priority: number,
dsId?: string,

View File

@ -1,24 +1,33 @@
import { Injectable } from '@angular/core';
import { Injectable, OnInit } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Page, DsmConf, ResourceType, Protocol, WfHistoryEntry, SimpleResource, Context, ContextNode, Vocabulary, VocabularyTerm, KeyValue, BrowseTerm, Datasource, MDStore, MDStoreVersion, MDStoreRecord, EmailTemplate, WfConf, WfSubscription, WfProcessStatus } from './is.model';
import { Page, DsmConf, ResourceType, Protocol, WfHistoryEntry, SimpleResource, Context, ContextNode, Vocabulary, VocabularyTerm, KeyValue, BrowseTerm, Datasource, MDStore, MDStoreVersion, MDStoreRecord, EmailTemplate, WfConf, WfSubscription, WfProcessStatus, WfSection } from './is.model';
import { FormGroup } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { firstValueFrom, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ISService {
constructor(public client: HttpClient, public snackBar: MatSnackBar) { }
loadResourceTypes(onSuccess: Function): void {
this.httpGet<ResourceType[]>("/ajax/resourceTypes", onSuccess);
constructor(public client: HttpClient, public snackBar: MatSnackBar) {
}
loadResourceType(id: string, onSuccess: Function): void {
loadResourceTypes(onSuccess: Function) {
this.httpGet<ResourceType[]>("/ajax/resourceTypes", onSuccess)
}
loadResourceType(id: string, onSuccess: Function) {
this.httpGet<ResourceType>("/ajax/resourceTypes/" + encodeURIComponent(id), onSuccess);
}
loadWfSections(onSuccess: Function) {
this.httpGet<WfSection[]>("/ajax/wfs/sections", onSuccess)
}
loadInfo(onSuccess: Function): void {
this.httpGet<any[]>("/ajax/info/", onSuccess);
}
@ -220,12 +229,8 @@ export class ISService {
this.httpDelete('./ajax/templates/email/' + encodeURIComponent(id), onSuccess);
}
loadWfConfigurationSections(onSuccess: Function): void {
this.httpGet<KeyValue[]>('./ajax/wfs/sections', onSuccess);
}
loadWfConfigurations(section: string, onSuccess: Function): void {
this.httpGet<KeyValue[]>('./ajax/wfs/search?section=' + encodeURIComponent(section), onSuccess);
loadWfConfigurations(sectionId: string, onSuccess: Function): void {
this.httpGet<KeyValue[]>('./ajax/wfs/section/' + encodeURIComponent(sectionId), onSuccess);
}
loadWfConfiguration(id: string, onSuccess: Function): void {

View File

@ -56,7 +56,9 @@
<mat-panel-title>Workflows</mat-panel-title>
</mat-expansion-panel-header>
<div>
<a class="menu-item" routerLink="wfs">Workflows</a>
<div *ngFor="let s of wfSections">
<a class="menu-item" [routerLink]="['wfs', s.id]">{{s.name}}</a>
</div>
</div>
</mat-expansion-panel>

View File

@ -1,5 +1,5 @@
import { Component, ViewChild } from '@angular/core';
import { ResourceType } from '../common/is.model';
import { KeyValue, ResourceType, WfSection } from '../common/is.model';
import { ISService } from '../common/is.service';
import { MatAccordion } from '@angular/material/expansion';
@ -15,8 +15,10 @@ export class MainMenuPanelsComponent {
accordion!: MatAccordion;
resTypes: ResourceType[] = [];
wfSections: WfSection[] = [];
constructor(public service: ISService) {
this.service.loadResourceTypes((data: ResourceType[]) => this.resTypes = data);
service.loadResourceTypes((data: ResourceType[]) => this.resTypes = data);
service.loadWfSections((data: WfSection[]) => this.wfSections = data);
}
}

View File

@ -1,4 +1,4 @@
<h2>{{type.name}}</h2>
<h2>{{type?.name}}</h2>
<button mat-stroked-button color="primary" (click)="openNewDialog()">
<mat-icon fontIcon="add"></mat-icon>
@ -15,7 +15,7 @@
<mat-card-title title="{{r.id}}">
{{r.name}}
<span class="badge-label badge-warning" style="font-size: 0.7em;" *ngIf="r.subtype">{{r.subtype}}</span>
<span class="badge-label badge-info" style="font-size: 0.7em;">{{type.contentType}}</span>
<span class="badge-label badge-info" style="font-size: 0.7em;">{{type?.contentType}}</span>
</mat-card-title>
</mat-card-header>
<mat-card-content>

View File

@ -12,7 +12,7 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
})
export class ResourcesComponent implements OnInit {
typeId: string = '';
type: ResourceType = { id: '', name: '', contentType: '', count: 0, simple: true };
type?: ResourceType;
resources: SimpleResource[] = [];
searchText: string = '';
@ -60,7 +60,7 @@ export class ResourcesComponent implements OnInit {
const dialogRef = this.dialog.open(ResContentDialog, {
data: {
id: r.id,
contentType: this.type.contentType,
contentType: this.type?.contentType,
content: data
},
width: '80%'

View File

@ -1,4 +1,4 @@
<h2>Workflow Instances</h2>
<h2>{{section?.name}}: Configured Workflows</h2>
<mat-tab-group animationDuration="0ms">
<mat-tab label="First">Content 1</mat-tab>

View File

@ -0,0 +1,31 @@
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { KeyValue, WfSection } from '../common/is.model';
import { ISService } from '../common/is.service';
@Component({
selector: 'app-wf-confs',
templateUrl: './wf-confs.component.html',
styleUrls: ['./wf-confs.component.css']
})
export class WfConfsComponent implements OnInit {
section?: WfSection;
confs: KeyValue[] = [];
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
}
ngOnInit() {
this.route.params.subscribe(params => {
let sectionId = params['section'];
this.service.loadWfSections((data: WfSection[]) => this.section = data.find(s => s.id == sectionId));
this.reload();
});
}
reload() {
}
}

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-wf-instances',
templateUrl: './wf-instances.component.html',
styleUrls: ['./wf-instances.component.css']
})
export class WfInstancesComponent {
}

View File

@ -0,0 +1,39 @@
package eu.dnetlib.manager.wf.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "wf_sections")
public class WorkflowSection implements Serializable {
private static final long serialVersionUID = 1442544603323121950L;
@Id
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}

View File

@ -262,10 +262,25 @@ CREATE TABLE emails (
-- Workflows
CREATE TABLE wf_sections (
id text PRIMARY KEY,
name text NOT NULL
);
INSERT INTO wf_sections(id, name) VALUES
('GC', 'Garbage Collection'),
('CONSISTENCY', 'InfoSpace Consistency'),
('DEDUP', 'InfoSpace Deduplication'),
('INFERENCE', 'InfoSpace Inference'),
('MONITOR', 'InfoSpace Monitoring'),
('PROVISION', 'InfoSpace Provision'),
('IS', 'Information Service'),
('BROKER', 'Notification Broker');
CREATE TABLE wf_configurations (
id text PRIMARY KEY,
name text NOT NULL,
section text,
section text REFERENCES wf_sections(id),
details jsonb NOT NULL DEFAULT '{}',
priority int,
dsid text,
@ -276,8 +291,8 @@ CREATE TABLE wf_configurations (
scheduling_enabled boolean NOT NULL DEFAULT false,
scheduling_cron text,
scheduling_min_interval int,
workflow text REFERENCES resource(id),
destroy_wf text REFERENCES resource(id),
workflow text REFERENCES resources(id),
destroy_wf text REFERENCES resources(id),
system_params jsonb NOT NULL DEFAULT '{}',
user_params jsonb NOT NULL DEFAULT '{}'
);

View File

@ -1,21 +1,13 @@
package eu.dnetlib.manager.wf.repository;
import java.util.stream.Stream;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import eu.dnetlib.manager.wf.model.WorkflowConfiguration;
import eu.dnetlib.utils.CountedValue;
public interface WorkflowConfigurationRepository extends JpaRepository<WorkflowConfiguration, String> {
@Query(value = "select section as value, count(*) as count "
+ "from wf_configurations "
+ "group by section "
+ "order by count desc;", nativeQuery = true)
Stream<CountedValue> streamSections();
Stream<WorkflowConfiguration> findBySection(String section);
List<WorkflowConfiguration> findBySection(String section);
}

View File

@ -0,0 +1,9 @@
package eu.dnetlib.manager.wf.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import eu.dnetlib.manager.wf.model.WorkflowSection;
public interface WorkflowSectionRepository extends JpaRepository<WorkflowSection, String> {
}

View File

@ -6,7 +6,6 @@ import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
@ -24,9 +23,11 @@ import eu.dnetlib.errors.WorkflowManagerException;
import eu.dnetlib.is.model.resource.SimpleResource;
import eu.dnetlib.is.resource.repository.SimpleResourceRepository;
import eu.dnetlib.manager.wf.model.WorkflowConfiguration;
import eu.dnetlib.manager.wf.model.WorkflowSection;
import eu.dnetlib.manager.wf.model.WorkflowSubscription;
import eu.dnetlib.manager.wf.model.WorkflowTemplate;
import eu.dnetlib.manager.wf.repository.WorkflowConfigurationRepository;
import eu.dnetlib.manager.wf.repository.WorkflowSectionRepository;
import eu.dnetlib.manager.wf.repository.WorkflowSubscriptionRepository;
import eu.dnetlib.manager.wf.workflows.procs.ExecutionStatus;
import eu.dnetlib.manager.wf.workflows.procs.ProcessEngine;
@ -35,7 +36,6 @@ import eu.dnetlib.manager.wf.workflows.procs.ProcessRegistry;
import eu.dnetlib.manager.wf.workflows.procs.WorkflowProcess;
import eu.dnetlib.manager.wf.workflows.util.ExecutionCallback;
import eu.dnetlib.manager.wf.workflows.util.WorkflowsConstants;
import eu.dnetlib.utils.CountedValue;
import eu.dnetlib.utils.Stoppable;
import eu.dnetlib.utils.StoppableDetails;
@ -56,6 +56,8 @@ public class WorkflowManagerService implements Stoppable {
@Autowired
private SimpleResourceRepository simpleResourceRepository;
@Autowired
private WorkflowSectionRepository workflowSectionRepository;
@Autowired
private WorkflowConfigurationRepository workflowConfigurationRepository;
@Autowired
private WorkflowSubscriptionRepository workflowSubscriptionRepository;
@ -196,11 +198,11 @@ public class WorkflowManagerService implements Stoppable {
this.paused = paused;
}
public Stream<CountedValue> streamSections() {
return workflowConfigurationRepository.streamSections();
public List<WorkflowSection> listSsections() {
return workflowSectionRepository.findAll();
}
public Stream<WorkflowConfiguration> streamWfConfigurationsBySection(final String section) {
public List<WorkflowConfiguration> listWfConfigurationsBySection(final String section) {
return workflowConfigurationRepository.findBySection(section);
}