Compare commits

...

210 Commits

Author SHA1 Message Date
Michele Artini 4104cfd586 fix dependencies 2023-05-12 10:56:32 +02:00
Michele Artini 81424be9d1 wf templates 2023-05-05 11:31:01 +02:00
Michele Artini 6b7275e3bc change some packages 2023-05-05 10:33:48 +02:00
Michele Artini 889cc9da35 datasource and api objects in nodes 2023-05-05 08:56:28 +02:00
Michele Artini 4b95550a99 repohi and repobye nodes 2023-05-04 14:30:55 +02:00
Michele Artini 5d3f4ea58e repohi and repobye nodes 2023-05-04 14:25:04 +02:00
Michele Artini 0280e506c9 mdstore: add methods 2023-05-04 11:20:39 +02:00
Michele Artini 5d949e5aa0 start/end/abort reading streams 2023-05-04 10:13:37 +02:00
Michele Artini b73cda7ca1 fixed nodes 2023-05-03 10:41:04 +02:00
Michele Artini 607001d7da fix poms after a merge from master 2023-05-03 10:25:25 +02:00
Michele Artini df91e8c38e Merge branch 'master' into new-is-app 2023-05-03 09:51:52 +02:00
Michele Artini 37517dcfb9 some aggregation nodes 2023-04-28 11:41:22 +02:00
Michele Artini 8a242eb4b5 oai collector 2023-04-28 09:10:49 +02:00
Michele Artini b8e905dc64 collectors 2023-04-27 12:50:34 +02:00
Michele Artini 52cde91251 some refactoring 2023-04-27 11:08:47 +02:00
Michele Artini 3517cbde4d WfAsync annotation 2023-04-27 08:39:36 +02:00
Michele Artini 3737f5fa6c preparation of aggr nodes 2023-04-27 08:31:25 +02:00
Michele Artini 73bbb6d6b0 preliminary version of an aggregative wf 2023-04-26 15:55:02 +02:00
Michele Artini 66523a612b input params 2023-04-21 15:12:55 +02:00
Michele Artini 97d17eb379 wf annotations 2023-04-21 13:14:30 +02:00
Michele Artini f2eb88a3f8 WfNode annotation management 2023-04-20 13:57:27 +02:00
Michele Artini 5cd6efbba6 annotation params management 2023-04-20 11:53:35 +02:00
Michele Artini f348e5c1a0 xsl functions 2023-04-20 10:07:22 +02:00
Michele Artini 0beb8ea671 some wf nodes 2023-04-19 14:52:58 +02:00
Michele Artini c7eef9a4e9 mdstore streams 2023-04-18 16:13:42 +02:00
Michele Artini a855a6afff wf ui 2023-04-14 16:06:59 +02:00
Michele Artini f4399909a6 simple wf execution 2023-04-14 12:48:33 +02:00
Michele Artini c4608e68be css 2023-04-14 11:00:13 +02:00
Michele Artini 7ad5bc2395 Merge branch 'master' into new-is-app 2023-04-14 10:10:04 +02:00
Michele Artini b2881c6b53 test 2023-04-05 10:22:09 +02:00
Michele Artini 94a78beb16 ui to start wfs 2023-03-30 14:59:57 +02:00
Michele Artini cdb3381d3e ui 2023-03-30 12:34:36 +02:00
Michele Artini cb08aee137 wf ui 2023-03-30 12:00:37 +02:00
Michele Artini b7e7612a12 ui 2023-03-29 16:24:09 +02:00
Michele Artini bc9fe2c716 ui 2023-03-28 14:25:08 +02:00
Michele Artini 6676c7f26c ui 2023-03-28 11:12:10 +02:00
Michele Artini be65886fd3 ui 2023-03-27 14:16:53 +02:00
Michele Artini 1a0bfe69a6 ui 2023-03-23 15:05:39 +01:00
Michele Artini ff908c7af3 ui 2023-03-23 12:24:56 +01:00
Michele Artini 4e92795b4a ajax calls 2023-03-23 08:42:47 +01:00
Michele Artini bdeaef3b8e tests 2023-03-22 13:48:55 +01:00
Michele Artini 4079bee3f4 tests 2023-03-22 13:13:02 +01:00
Michele Artini 27aaab75b7 remaming classes 2023-03-22 11:38:31 +01:00
Michele Artini e40094a0b8 renaming classes 2023-03-22 10:42:33 +01:00
Michele Artini f168ca9a2b ajax methods for wfs 2023-03-21 15:28:18 +01:00
Michele Artini 8c3ae35dc4 Merge pull request 'new-is-app-with-new-msro' (#13) from new-is-app-with-new-msro into new-is-app
Reviewed-on: #13
2023-03-21 12:15:25 +01:00
Michele Artini 806f8e9227 ajax methods for wfs 2023-03-21 12:13:23 +01:00
Michele Artini 28598ddfea expression parser 2023-03-20 15:32:46 +01:00
Michele Artini ab3c0851ed refactoring 2023-03-20 12:26:40 +01:00
Michele Artini 836c31b6d6 refactoring 2023-03-20 11:07:54 +01:00
Michele Artini 630b4c282d minors 2023-03-10 15:20:49 +01:00
Michele Artini 34f6e2977b frontend: wf instances model 2023-03-10 12:15:54 +01:00
Michele Artini 0782c2f536 refactoring 2023-03-08 10:10:03 +01:00
Michele Artini cd5e1ab957 refactoring 2023-03-08 09:39:37 +01:00
Michele Artini 30991cabbf refactoring 2023-03-08 08:08:26 +01:00
Michele Artini e4443ef2c8 refactoring 2023-03-06 14:04:01 +01:00
Michele Artini 0bd5e32eed refactoring 2023-03-03 15:22:46 +01:00
Michele Artini 1a6cc4ae2a add subtype to resources 2023-03-03 13:53:21 +01:00
Michele Artini 1f801cb4bf Email templates UI 2023-03-03 11:13:24 +01:00
Michele Artini 0a70952cda new angular components 2023-03-02 12:24:38 +01:00
Michele Artini 39f8b20955 updated the version of some maven plugins 2023-03-01 14:35:54 +01:00
Michele Artini f0e780eabf fix a bug 2023-03-01 13:53:46 +01:00
Michele Artini a3d8b0de4d some fixes 2023-03-01 13:01:24 +01:00
Michele Artini 01b046cfb4 some fix 2023-02-27 13:09:58 +01:00
Michele Artini 2b2b0cad0a first import of msro 2023-02-27 11:43:28 +01:00
Michele Artini 3faf80e84d cleaner 2023-02-14 16:55:42 +01:00
Michele Artini c84ae70a12 new module for is services + cleaner 2023-02-14 15:00:44 +01:00
Michele Artini 7d36713db7 icons 2023-02-14 11:17:37 +01:00
Michele Artini 9e2be69d98 mock mdstore 2023-02-10 14:54:37 +01:00
Michele Artini 438a8a46bc show mdstore params 2023-02-10 12:48:16 +01:00
Michele Artini e52cdba8a3 mdstore type in ui 2023-02-10 12:18:06 +01:00
Michele Artini a22b725e3c multiple mdstore types 2023-02-10 10:27:09 +01:00
Michele Artini f8094c659c moved some mdstore componenets under dnet-data-services 2023-02-09 13:36:26 +01:00
Michele Artini 73ade91f43 indentation 2023-02-08 15:36:13 +01:00
Michele Artini 5d861c406a mdstore inspector 2023-02-08 15:12:39 +01:00
Michele Artini c9c0ac00b8 mstore list page 2023-02-08 12:23:53 +01:00
Michele Artini 1491e8a5d2 mdstore ui - partial implementation 2023-02-07 15:08:37 +01:00
Michele Artini 32c9766db7 mdstore ui - partial implementation 2023-02-07 11:22:20 +01:00
Michele Artini 87df817768 setup for mdstore inspector ui 2023-02-06 16:21:57 +01:00
Michele Artini 01635641f9 refactoring 2023-02-06 15:53:07 +01:00
Michele Artini 258366c4c6 refactoring 2023-02-06 15:31:21 +01:00
Michele Artini 5bdc530e91 first import of mdstore inspector (hdfs) 2023-02-06 14:50:10 +01:00
Michele Artini 721b55294d removed bootstrap UI 2023-02-06 12:20:37 +01:00
Michele Artini 2ae7ff4a5c Merge pull request 'new-is-app-angular2' (#12) from new-is-app-angular2 into new-is-app
Reviewed-on: #12
2023-02-06 11:30:13 +01:00
Michele Artini 286a9a189d spinner 2023-02-03 14:55:39 +01:00
Michele Artini a01a638661 Home redirects to Info 2023-02-03 12:48:12 +01:00
Michele Artini 7f71c5f3d0 baseUrl regex 2023-02-03 11:57:12 +01:00
Michele Artini 78ffdd538a typed params in new api form 2023-02-03 11:45:21 +01:00
Michele Artini 90d7b7626f add API model 2023-02-02 14:26:53 +01:00
Michele Artini f776c2ca68 dynamic form 2023-02-02 12:32:55 +01:00
Michele Artini e620975813 ui 2023-02-02 09:37:24 +01:00
Michele Artini 0e3a3f11c6 partial implementation of add Api Form 2023-02-01 15:45:33 +01:00
Michele Artini 416312a1e7 file positions 2023-02-01 11:51:23 +01:00
Michele Artini f429138a1f dsm results 2023-02-01 11:25:36 +01:00
Michele Artini 1c7869076f cleaning 2023-02-01 09:58:36 +01:00
Michele Artini 7baa6dcbde paginations 2023-02-01 09:33:22 +01:00
Michele Artini 6f291aa006 dsm results pagination 2023-01-31 15:02:51 +01:00
Michele Artini e3e597ee31 dsm search page 2023-01-31 12:36:51 +01:00
Michele Artini 23a3f2546e dsm setup 2023-01-31 11:05:11 +01:00
Michele Artini a5b318a065 vocabulary editor 2023-01-31 10:19:19 +01:00
Michele Artini 4cf13df0ab partial implementatiom of add/edit vocabulary term 2023-01-30 14:58:42 +01:00
Michele Artini e83bd36fc1 vocabulary terms 2023-01-30 12:37:01 +01:00
Michele Artini 527a80c931 Vocabularies 2023-01-30 10:58:34 +01:00
Michele Artini 78ca043213 proxy conf 2023-01-30 09:39:27 +01:00
Michele Artini d93c39275f minor fix 2023-01-27 15:04:51 +01:00
Michele Artini ea1b5a0b89 context viewer 2023-01-27 15:00:41 +01:00
Michele Artini f47cd2a8e7 contexts 2023-01-27 12:43:15 +01:00
Michele Artini 1cb603a83c contexts 2023-01-27 11:44:30 +01:00
Michele Artini 448bcb4e86 reimplemented ajax callback 2023-01-27 08:58:33 +01:00
Michele Artini e62329b5d9 snack errors 2023-01-26 14:56:39 +01:00
Michele Artini 7235e4ad4e error server side 2023-01-26 12:21:50 +01:00
Michele Artini 8ec682096a forms 2023-01-26 11:14:41 +01:00
Michele Artini 229e7798d0 form to add a resource 2023-01-26 10:55:02 +01:00
Michele Artini c8b8bf0dfd removed menu label 2023-01-26 08:39:21 +01:00
Michele Artini f4f076dc6a filter 2023-01-26 08:28:12 +01:00
Michele Artini 0a8a1ec118 edit metadata of simple resources 2023-01-25 16:08:32 +01:00
Michele Artini 085c3123de menu 2023-01-24 14:11:39 +01:00
Michele Artini cfcd1493a1 colored badge 2023-01-24 11:11:25 +01:00
Michele Artini f1183df749 sort 2023-01-24 10:45:59 +01:00
Michele Artini d11c51879e sort 2023-01-24 10:38:39 +01:00
Michele Artini 4a41d8cf84 ui 2023-01-24 10:08:55 +01:00
Michele Artini 9d8159b392 first dialog 2023-01-23 14:22:14 +01:00
Michele Artini 8785218ae9 ui 2023-01-23 11:08:43 +01:00
Michele Artini 118c3ac7d3 wf history 2023-01-20 16:57:03 +01:00
Michele Artini 8791d61303 menu with panels 2023-01-20 13:19:34 +01:00
Michele Artini 93560e881e info and protocols 2023-01-20 11:39:34 +01:00
Michele Artini 737a43ff82 first implementation 2023-01-19 14:47:52 +01:00
Michele Artini 2977d04205 new directives 2023-01-17 11:55:59 +01:00
Michele Artini 6889746c7e forms 2023-01-17 10:03:27 +01:00
Michele Artini 6ac09081b2 forms 2023-01-16 14:45:10 +01:00
Michele Artini 4a7085d7ec forms 2023-01-16 12:38:04 +01:00
Michele Artini 7b35bd753e forms 2023-01-16 11:42:45 +01:00
Michele Artini 0acad7e737 form 2023-01-13 15:03:12 +01:00
Michele Artini f5ff6f0601 moved code under new packages/folders 2023-01-13 11:39:49 +01:00
Michele Artini 5b681ed9e8 refactoring ui as single angularJs application 2023-01-12 15:09:14 +01:00
Michele Artini 2ed71532db protocols page 2023-01-12 09:37:18 +01:00
Michele Artini dbb304a46a protocols 2023-01-04 15:49:56 +01:00
Michele Artini d54ae4ac64 add api page (mock) 2022-12-20 13:37:40 +01:00
Michele Artini b44cb9e852 api page (mock) 2022-12-20 09:13:47 +01:00
Michele Artini 0e027eb037 fixed a bug with pagination 2022-12-19 09:26:46 +01:00
Michele Artini 04f0192a1b cache 2022-12-16 15:45:06 +01:00
Michele Artini ab1e85fa98 spinner 2022-12-16 15:31:49 +01:00
Michele Artini a5413b393a routes 2022-12-16 14:13:14 +01:00
Michele Artini f506ccaa4b routes 2022-12-16 13:49:45 +01:00
Michele Artini 56cf7ffcef dsm: routes and pagination 2022-12-16 11:39:32 +01:00
Michele Artini 209ed2166d pagination 2022-12-15 15:13:32 +01:00
Michele Artini 2c62f11b32 search 2022-12-15 14:40:50 +01:00
Michele Artini aca292308b refactoring of dsm queries 2022-12-15 14:19:30 +01:00
Michele Artini 48b16d8023 dsm results 2022-12-14 16:30:15 +01:00
Michele Artini 6f578df920 ui 2022-12-14 10:13:47 +01:00
Michele Artini 7907b61167 browse queries 2022-12-13 15:24:47 +01:00
Michele Artini 413d06ad7c search ds 2022-12-13 12:15:34 +01:00
Michele Artini 55113b3c60 updated db configurations 2022-12-13 08:51:08 +01:00
Michele Artini ee323bca6d new dsm 2022-12-12 15:55:35 +01:00
Michele Artini b37eb84e91 fixed import of legacy tr rules 2022-12-07 11:42:40 +01:00
Michele Artini 8933fa2273 xml validation (no schema) 2022-12-06 14:41:07 +01:00
Michele Artini c68ae255a3 json validation 2022-12-06 10:39:57 +01:00
Michele Artini a147c29c51 error messages 2022-12-06 10:24:53 +01:00
Michele Artini 0f687ae98f xml validation 2022-12-06 10:16:49 +01:00
Michele Artini 781254121d api docs 2022-12-05 12:23:02 +01:00
Michele Artini 67ec5bc368 separated ajax and api calls 2022-12-05 11:04:54 +01:00
Michele Artini 4335f420df xml indent 2022-12-02 14:14:50 +01:00
Michele Artini 5e86ab8723 Add Dedup Confs 2022-12-02 13:29:53 +01:00
Michele Artini 3aa5acd740 minors 2022-12-02 11:10:15 +01:00
Michele Artini 066e0ca73c css 2022-12-02 09:06:53 +01:00
Michele Artini 6e3700ba31 cs 2022-12-01 10:44:09 +01:00
Michele Artini 092bd008e3 props 2022-12-01 09:47:38 +01:00
Michele Artini dcdfbbf64e info ui 2022-11-29 14:14:59 +01:00
Michele Artini 437e835d42 filter 2022-11-29 12:22:59 +01:00
Michele Artini 05416186b1 Info page (modules) 2022-11-29 12:13:40 +01:00
Michele Artini 2cd18af72c Info page 2022-11-29 10:48:20 +01:00
Michele Artini 9a451d13f5 context property to count childs 2022-11-28 12:12:04 +01:00
Michele Artini a402e0c85d move to @Service 2022-11-28 11:11:13 +01:00
Michele Artini 9e05516295 ui 2022-11-24 14:17:50 +01:00
Michele Artini d5fb85d933 ui 2022-11-24 11:45:10 +01:00
Michele Artini 7e7f6ff746 contentType 2022-11-24 10:19:36 +01:00
Michele Artini 4ff77964ca importers 2022-11-23 15:32:36 +01:00
Michele Artini a49a9c9c01 upload file api 2022-11-23 14:45:56 +01:00
Michele Artini f3439a1d52 ui 2022-11-23 11:17:50 +01:00
Michele Artini 51a3eda4c2 ui 2022-11-21 14:26:22 +01:00
Michele Artini bf7962603f ui 2022-11-17 15:21:40 +01:00
Michele Artini 582c198cc0 ui 2022-11-17 11:19:07 +01:00
Michele Artini d81a72b896 ui 2022-11-17 09:53:09 +01:00
Michele Artini a4a2489643 prelimanary code from simple resources 2022-11-16 14:35:24 +01:00
Michele Artini 97eafc7ff0 fix after merge 2022-11-16 10:41:55 +01:00
Michele Artini 304baf9bf3 Merge remote-tracking branch 'origin/master' into new-is-app 2022-11-16 10:37:47 +01:00
Michele Artini 78bda245f2 wf history by date 2022-07-08 15:08:50 +02:00
Michele Artini 14d7110b1d ui 2022-07-08 14:29:51 +02:00
Michele Artini 761ad265fa wf history: details 2022-07-04 11:12:06 +02:00
Michele Artini 7db3d1b401 logger ui 2022-07-04 09:30:50 +02:00
Michele Artini daa8b1a14e first implementation of wf history 2022-06-30 14:43:26 +02:00
Michele Artini 2b10c4582a full context api 2022-06-28 08:43:39 +02:00
Michele Artini 2ee52eef18 ui 2022-06-27 14:54:22 +02:00
Michele Artini 9ff5476a70 concepts population with AJAX 2022-06-27 10:47:20 +02:00
Michele Artini 0855c17f4a context apis 2022-06-24 15:44:53 +02:00
Michele Artini a36745ab8b contexts: simple primary keys 2022-06-24 15:26:47 +02:00
Michele Artini 154b27b0bb contexts 2022-06-24 14:34:03 +02:00
Michele Artini d7779b364a menu 2022-06-23 14:20:57 +02:00
Michele Artini 945dc44dde ui 2022-06-23 12:13:45 +02:00
Michele Artini bbcd58a353 ui 2022-06-23 12:04:38 +02:00
Michele Artini 9c7656e618 add encoding property to terms and synonyms 2022-06-23 11:18:47 +02:00
Michele Artini d69bf62b5f ui 2022-06-23 08:40:01 +02:00
Michele Artini 6984a463cb only a name for terms 2022-06-23 08:32:34 +02:00
Michele Artini e4e3ea6110 ui 2022-06-22 15:47:19 +02:00
Michele Artini 4d6bc4b327 ui 2022-06-21 12:08:58 +02:00
Michele Artini 45673b832c ui 2022-06-21 09:55:39 +02:00
Michele Artini 3905cf3c3f terms 2022-06-16 11:15:05 +02:00
Michele Artini 6e42765602 first version of vocabularies UI 2022-06-16 09:57:30 +02:00
Michele Artini 0d722526c0 first methods 2022-06-15 14:39:36 +02:00
Michele Artini a17a7dc3a0 empty modules 2022-06-14 13:59:31 +02:00
911 changed files with 37305 additions and 22 deletions

2
.gitignore vendored
View File

@ -7,7 +7,7 @@
*.iws
*.java-version
*~
/**/*.sh
/**/ssh_tunnel_*.sh
/**/my_application.properties
.vscode
.classpath

View File

@ -4,7 +4,7 @@
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>apps</artifactId>
<version>3.4.3-SNAPSHOT</version>
<relativePath>../</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>apps</artifactId>
<version>3.4.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dnet-is-application</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Openaire IS common -->
<dependency>
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>dnet-is-services</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>dnet-wf-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>dnet-data-services</artifactId>
<version>${project.version}</version>
</dependency>
<!-- hot swapping, disable cache for template, enable live reload -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- Tests -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-help-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,30 @@
package eu.dnetlib;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.is.model.resource.ResourceType;
import eu.dnetlib.is.resource.repository.ResourceTypeRepository;
@RestController
public class MainAjaxController {
@Autowired
private ResourceTypeRepository resourceTypeRepository;
@GetMapping("/ajax/resourceTypes")
public Iterable<ResourceType> resourceTypes() {
return resourceTypeRepository.findAll();
}
@GetMapping("/ajax/resourceTypes/{id}")
public ResourceType resourceTypes(@PathVariable final String id) {
return resourceTypeRepository
.findById(id)
.orElse(new ResourceType("not_present", "???", MediaType.TEXT_PLAIN_VALUE, 0));
}
}

View File

@ -0,0 +1,36 @@
package eu.dnetlib;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import eu.dnetlib.common.app.AbstractDnetApp;
@SpringBootApplication
@EnableCaching
@EnableScheduling
@ComponentScan(basePackages = "eu.dnetlib")
public class MainApplication extends AbstractDnetApp {
public static void main(final String[] args) {
SpringApplication.run(MainApplication.class, args);
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("D-Net Information Service API")
.pathsToMatch("/api/**")
.build();
}
@Override
protected String swaggerTitle() {
return "D-Net Information Service APIs";
}
}

View File

@ -0,0 +1,63 @@
package eu.dnetlib;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "mainEntityManagerFactory", transactionManagerRef = "mainTransactionManager", basePackages = {
"eu.dnetlib.is",
"eu.dnetlib.common",
"eu.dnetlib.manager.history",
"eu.dnetlib.manager.wf",
"eu.dnetlib.data.mdstore"
})
public class MainDBConfig {
@Primary
@Bean(name = "mainDataSource")
@ConfigurationProperties(prefix = "is.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "mainEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final EntityManagerFactoryBuilder builder,
@Qualifier("mainDataSource") final DataSource ds) {
return builder
.dataSource(ds)
.packages("eu.dnetlib.is.model", "eu.dnetlib.manager.history.model", "eu.dnetlib.common.model", "eu.dnetlib.manager.wf.model", "eu.dnetlib.data.mdstore.model")
.persistenceUnit("is")
.build();
}
@Primary
@Bean(name = "mainTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("mainEntityManagerFactory") final EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Primary
@Bean(name = "mainJdbcTemplate")
public JdbcTemplate jdbcTemplate1(@Qualifier("mainDataSource") final DataSource ds) {
return new JdbcTemplate(ds);
}
}

View File

@ -0,0 +1,53 @@
package eu.dnetlib;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "openaireEntityManagerFactory", transactionManagerRef = "openaireTransactionManager", basePackages = {
"eu.dnetlib.dsm"
})
public class OpenaireDBConfig {
@Bean(name = "openaireDataSource")
@ConfigurationProperties(prefix = "openaire.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "openaireEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean openaireEntityManagerFactory(
final EntityManagerFactoryBuilder builder,
@Qualifier("openaireDataSource") final DataSource ds) {
return builder
.dataSource(ds)
.packages("eu.dnetlib.dsm.model")
.persistenceUnit("openaire")
.build();
}
@Bean(name = "openaireTransactionManager")
public PlatformTransactionManager opeanaireTransactionManager(
@Qualifier("openaireEntityManagerFactory") final EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean(name = "openaireJdbcTemplate")
public JdbcTemplate jdbcTemplate1(@Qualifier("openaireDataSource") final DataSource ds) {
return new JdbcTemplate(ds);
}
}

View File

@ -1,16 +1,16 @@
package eu.dnetlib.data.mdstore.manager.controller;
package eu.dnetlib;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class SwaggerController {
@RequestMapping(value = {
"/apidoc", "/api-doc", "/doc", "/swagger"
}, method = RequestMethod.GET)
"/", "/docs", "swagger-ui.html", "swagger-ui/"
})
public String apiDoc() {
return "redirect:swagger-ui/index.html";
}
}

View File

@ -0,0 +1,44 @@
package eu.dnetlib.data.mapping;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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.data.mapping.cleaner.CleanerFactory;
@RestController
@RequestMapping("/ajax/mapping")
public class MappingAjaxController {
@Autowired
private CleanerFactory cleanerFactory;
@PostMapping(value = "/clean", consumes = {
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE,
}, produces = MediaType.APPLICATION_XML_VALUE)
public void importResource(@RequestParam final String rule, final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final String xmlIn = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
final String xmlOut = cleanerFactory.obtainCleaningRule(rule).transform(xmlIn);
res.setCharacterEncoding(StandardCharsets.UTF_8.name());
res.setContentType(MediaType.APPLICATION_XML_VALUE);
if (StringUtils.isNotBlank(xmlOut)) {
IOUtils.write(xmlOut, res.getOutputStream(), StandardCharsets.UTF_8.name());
} else {
res.sendError(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invalid record");
}
}
}

View File

@ -0,0 +1,124 @@
package eu.dnetlib.data.mdstore;
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.RequestParam;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.data.mdstore.model.MDStoreType;
import eu.dnetlib.data.mdstore.model.MDStoreVersion;
import eu.dnetlib.data.mdstore.model.MDStoreWithInfo;
import eu.dnetlib.errors.MDStoreManagerException;
import io.swagger.v3.oas.annotations.Operation;
public class AbstractMDStoreController extends AbstractDnetController {
@Autowired
protected MDStoreService service;
@Operation(summary = "Return all the mdstores")
@GetMapping("/")
public Iterable<MDStoreWithInfo> find() {
return service.listMdStores();
}
@Operation(summary = "Return a mdstores by id")
@GetMapping("/mdstore/{mdId}")
public MDStoreWithInfo getMdStore(@PathVariable final String mdId) throws MDStoreManagerException {
return service.findMdStore(mdId);
}
@Operation(summary = "Create a new mdstore")
@PostMapping("/new/{format}/{layout}/{interpretation}/{type}")
public MDStoreWithInfo createMDStore(
@PathVariable final String format,
@PathVariable final String layout,
@PathVariable final String interpretation,
@PathVariable final String type,
@RequestParam(required = true) final String dsName,
@RequestParam(required = true) final String dsId,
@RequestParam(required = true) final String apiId) throws MDStoreManagerException {
final String id = service.createMDStore(format, layout, interpretation, MDStoreType.valueOf(type), dsName, dsId, apiId);
return service.findMdStore(id);
}
@Operation(summary = "Delete a mdstore by id")
@DeleteMapping("/mdstore/{mdId}")
public StatusResponse delete(@PathVariable final String mdId)
throws MDStoreManagerException {
service.deleteMdStore(mdId);
return StatusResponse.DELETED;
}
@Operation(summary = "Return all the versions of a mdstore")
@GetMapping("/mdstore/{mdId}/versions")
public Iterable<MDStoreVersion> listVersions(@PathVariable final String mdId) throws MDStoreManagerException {
return service.listVersions(mdId);
}
@Operation(summary = "Create a new preliminary version of a mdstore")
@GetMapping("/mdstore/{mdId}/newVersion")
public MDStoreVersion prepareNewVersion(
@PathVariable final String mdId) throws MDStoreManagerException {
return service.prepareMdStoreVersion(mdId);
}
@Operation(summary = "Promote a preliminary version to current")
@GetMapping("/version/{versionId}/commit/{size}")
public MDStoreVersion commitVersion(
@PathVariable final String versionId,
@PathVariable final long size) throws MDStoreManagerException {
try {
return service.commitMdStoreVersion(versionId, size);
} finally {
service.deleteExpiredVersions();
}
}
@Operation(summary = "Abort a preliminary version")
@GetMapping("/version/{versionId}/abort")
public StatusResponse commitVersion(@PathVariable final String versionId)
throws MDStoreManagerException {
service.deleteMdStoreVersion(versionId, true);
return StatusResponse.ABORTED;
}
@Operation(summary = "Return an existing mdstore version")
@GetMapping("/version/{versionId}")
public MDStoreVersion getVersion(@PathVariable final String versionId)
throws MDStoreManagerException {
return service.findVersion(versionId);
}
@Operation(summary = "Delete a mdstore version")
@DeleteMapping("/version/{versionId}")
public StatusResponse deleteVersion(@PathVariable final String versionId,
@RequestParam(required = false, defaultValue = "false") final boolean force)
throws MDStoreManagerException {
service.deleteMdStoreVersion(versionId, force);
return StatusResponse.DELETED;
}
@Operation(summary = "Decrease the read count of a mdstore version")
@GetMapping("/version/{versionId}/endReading")
public MDStoreVersion endReading(@PathVariable final String versionId) throws MDStoreManagerException {
return service.endReading(versionId);
}
@Operation(summary = "Reset the read count of a mdstore version")
@GetMapping("/version/{versionId}/resetReading")
public MDStoreVersion resetReading(@PathVariable final String versionId) throws MDStoreManagerException {
return service.resetReading(versionId);
}
}

View File

@ -0,0 +1,56 @@
package eu.dnetlib.data.mdstore;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.data.mdstore.model.records.MetadataRecord;
import eu.dnetlib.errors.MDStoreManagerException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/ajax/mdstores")
@Tag(name = "Metadata Stores")
public class MDStoreAjaxController extends AbstractMDStoreController {
@Value("${dhp.mdstore-manager.hadoop.cluster}")
private String hadoopCluster;
@Value("${dhp.mdstore-manager.hadoop.user}")
private String hadoopUser;
@Value("${dhp.mdstore-manager.hdfs.base-path}")
private String hdfsBasePath;
@Operation(summary = "Show informations")
@GetMapping("/info")
public Map<String, Object> info() {
final Map<String, Object> info = new LinkedHashMap<>();
info.put("number_of_mdstores", service.countMdStores());
info.put("hadoop_user", hadoopUser);
info.put("hadoop_cluster", hadoopCluster);
info.put("hdfs_base_path", hdfsBasePath);
info.put("expired_versions", service.listExpiredVersions());
return info;
}
@Operation(summary = "read the parquet file of a mdstore version")
@GetMapping("/version/{versionId}/content/{limit}")
public List<? extends MetadataRecord> listVersionRecords(@PathVariable final String versionId, @PathVariable final long limit)
throws MDStoreManagerException {
return service.listVersionRecords(versionId, limit);
}
@Operation(summary = "read the parquet file of a mdstore (current version)")
@GetMapping("/mdstore/{mdId}/content/{limit}")
public List<? extends MetadataRecord> listMdstoreRecords(@PathVariable final String mdId, @PathVariable final long limit) throws MDStoreManagerException {
return service.listVersionRecords(service.findMdStore(mdId).getCurrentVersion(), limit);
}
}

View File

@ -0,0 +1,59 @@
package eu.dnetlib.data.mdstore;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.data.mdstore.model.MDStoreType;
import eu.dnetlib.data.mdstore.model.MDStoreVersion;
import eu.dnetlib.errors.MDStoreManagerException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/api/mdstores")
@Tag(name = "Metadata Stores")
public class MDStoreApiController extends AbstractMDStoreController {
@Operation(summary = "Return all the mdstore identifiers")
@GetMapping("/ids")
public List<String> findIdentifiers() {
return service.listMdStoreIDs();
}
@Operation(summary = "Increase the read count of the current mdstore")
@GetMapping("/mdstore/{mdId}/startReading")
public MDStoreVersion startReading(@PathVariable final String mdId) throws MDStoreManagerException {
return service.startReading(mdId);
}
@Operation(summary = "Fix the inconsistencies on HDFS")
@GetMapping("/hdfs/inconsistencies")
public Map<MDStoreType, Set<String>> fixHdfsInconsistencies(
@RequestParam(required = false, defaultValue = "false") final boolean forceDelete)
throws MDStoreManagerException {
return service.fixInconsistencies(forceDelete);
}
@Operation(summary = "Delete expired versions")
@DeleteMapping("/versions/expired")
public StatusResponse deleteExpiredVersions() {
new Thread(service::deleteExpiredVersions).start();
return StatusResponse.DELETING;
}
@Operation(summary = "list the file inside the path of a mdstore version")
@GetMapping("/version/{versionId}/files")
public Set<String> listVersionFiles(@PathVariable final String versionId) throws MDStoreManagerException {
return service.listVersionInternalFiles(versionId);
}
}

View File

@ -0,0 +1,25 @@
package eu.dnetlib.data.mdstore;
public class StatusResponse {
public static final StatusResponse DELETED = new StatusResponse("DELETED");
public static final StatusResponse DELETING = new StatusResponse("DELETING...");
public static final StatusResponse ABORTED = new StatusResponse("ABORTED");;
private String status;
public StatusResponse() {}
public StatusResponse(final String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
}

View File

@ -0,0 +1,30 @@
package eu.dnetlib.data.mdstore;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.data.mdstore.hadoop.ZeppelinClient;
import eu.dnetlib.errors.MDStoreManagerException;
@RestController
@RequestMapping("/zeppelin")
public class ZeppelinAjaxController extends AbstractDnetController {
@Autowired
private ZeppelinClient zeppelinClient;
@GetMapping("/templates")
public List<String> getTemplates() throws MDStoreManagerException {
try {
return zeppelinClient.listTemplates();
} catch (final Throwable e) {
throw new MDStoreManagerException("Zeppelin is unreachable", e);
}
}
}

View File

@ -0,0 +1,37 @@
package eu.dnetlib.data.mdstore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import eu.dnetlib.data.mdstore.hadoop.ZeppelinClient;
import eu.dnetlib.data.mdstore.model.MDStoreType;
import eu.dnetlib.data.mdstore.model.MDStoreVersion;
import eu.dnetlib.data.mdstore.model.MDStoreWithInfo;
import eu.dnetlib.errors.MDStoreManagerException;
@Controller
public class ZeppelinController {
@Autowired
private ZeppelinClient zeppelinClient;
@Autowired
private MDStoreService service;
@RequestMapping("/zeppelin/{mdId}/{note}")
public String goToZeppelin(@PathVariable final String mdId, final @PathVariable String note) throws MDStoreManagerException {
final MDStoreWithInfo mdstore = service.findMdStore(mdId);
if (mdstore.getType() == MDStoreType.HDFS) {
final String currentVersion = mdstore.getCurrentVersion();
final MDStoreVersion version = service.findVersion(currentVersion);
if (version.getParams().containsKey("hdfs_path")) {
final String path = version.getParams().get("hdfs_path") + "/store";
return "redirect:" + zeppelinClient.zeppelinNote(note, mdstore, path);
}
}
throw new MDStoreManagerException();
}
}

View File

@ -0,0 +1,119 @@
package eu.dnetlib.dsm;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.dsm.domain.Response;
import eu.dnetlib.errors.DsmException;
import eu.dnetlib.errors.DsmForbiddenException;
import eu.dnetlib.errors.DsmNotFoundException;
/**
* Created by claudio on 18/07/2017.
*/
public abstract class AbstractDsmController extends AbstractDnetController {
private static final Log log = LogFactory.getLog(DsmException.class); // NOPMD by marko on 11/24/08 5:02 PM
@ResponseBody
@ExceptionHandler({
DsmException.class
})
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorMessage handleDSMException(final Exception e) {
return _handleError(e);
}
@ResponseBody
@ExceptionHandler(DsmForbiddenException.class)
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public ErrorMessage handleForbiddenException(final Exception e) {
return _handleError(e);
}
@ResponseBody
@ExceptionHandler({
DsmNotFoundException.class
})
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public ErrorMessage handleNotFoundException(final Exception e) {
return _handleError(e);
}
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public List<ErrorMessage> processValidationError(final MethodArgumentNotValidException e) {
return e.getBindingResult()
.getFieldErrors()
.stream()
.map(fe -> new ErrorMessage(
String.format("field '%s'", fe.getField()),
String.format("rejected value '%s'", fe.getRejectedValue()),
fe.getDefaultMessage()))
.collect(Collectors.toList());
}
private ErrorMessage _handleError(final Exception e) {
log.error(e);
if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(e), "Broken pipe")) {
return null; // socket is closed, cannot return any response
} else {
return new ErrorMessage(e);
}
}
// HELPERS
protected <T extends Response> T prepareResponse(final int page, final int size, final StopWatch stopWatch, final T rsp) {
rsp.getHeader()
.setTime(stopWatch.getTime())
.setPage(page)
.setSize(size);
return rsp;
}
@JsonAutoDetect
public class ErrorMessage {
private final String message;
private final String details;
private final String stacktrace;
public ErrorMessage(final Exception e) {
this(e.getMessage(), "", ExceptionUtils.getStackTrace(e));
}
public ErrorMessage(final String message, final String details, final String stacktrace) {
this.message = message;
this.details = details;
this.stacktrace = stacktrace;
}
public String getMessage() {
return this.message;
}
public String getStacktrace() {
return this.stacktrace;
}
public String getDetails() {
return details;
}
}
}

View File

@ -0,0 +1,86 @@
package eu.dnetlib.dsm;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
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.RequestBody;
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.dsm.model.Api;
import eu.dnetlib.dsm.model.BrowseTerm;
import eu.dnetlib.dsm.model.readonly.SimpleDsWithApis;
import eu.dnetlib.dsm.utils.DsmBrowsableFields;
import eu.dnetlib.errors.DsmException;
import eu.dnetlib.is.info.KeyValue;
import eu.dnetlib.is.protocol.ProtocolService;
import eu.dnetlib.is.vocabulary.repository.VocabularyTermRepository;
@RestController
@RequestMapping("/ajax/dsm")
public class DsmAjaxController extends AbstractDnetController {
@Autowired
private DsmService dsmService;
@Autowired
private VocabularyTermRepository vocabularyTermRepository;
@Autowired
private ProtocolService protocolService;
@GetMapping("/browsableFields")
public List<KeyValue<String>> browsableFields() {
return Arrays.stream(DsmBrowsableFields.values())
.map(f -> new KeyValue<>(f.name(), f.desc))
.collect(Collectors.toList());
}
@GetMapping("/conf")
public Map<String, Iterable<?>> configuration() {
final Map<String, Iterable<?>> map = new LinkedHashMap<>();
map.put("protocols", protocolService.listProtocols());
map.put("compatibilityLevels", vocabularyTermRepository.findTermsByVocabulary("dnet:compatibilityLevel"));
map.put("contentDescTypes", vocabularyTermRepository.findTermsByVocabulary("dnet:content_description_typologies"));
return map;
}
@GetMapping("/browse/{field}")
public List<BrowseTerm> browse(@PathVariable final String field) {
return dsmService.browseTerm(DsmBrowsableFields.valueOf(field));
}
@GetMapping("/searchByField/{field}/{page}/{size}")
public Page<SimpleDsWithApis> searchByField(@PathVariable final String field,
@PathVariable final int page,
@PathVariable final int size,
@RequestParam final String value) {
return dsmService.searchByField(DsmBrowsableFields.valueOf(field), value, page, size);
}
@GetMapping("/search/{page}/{size}")
public Page<SimpleDsWithApis> search(@RequestParam final String value, @PathVariable final int page, @PathVariable final int size) {
return dsmService.search(value, page, size);
}
@GetMapping("/api")
public Api findApi(@RequestParam final String id) throws DsmException {
return dsmService.findApi(id);
}
@PostMapping("/api")
public Api saveApi(@RequestBody final Api api) throws DsmException {
throw new DsmException("TO BE IMPLEMENTED");
}
}

View File

@ -0,0 +1,281 @@
package eu.dnetlib.dsm;
import static eu.dnetlib.dsm.utils.DsmMappingUtils.asDbEntry;
import static eu.dnetlib.dsm.utils.DsmMappingUtils.copyNonNullProperties;
import static eu.dnetlib.dsm.utils.DsmMappingUtils.createId;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.domain.Page;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.dsm.domain.AggregationHistoryResponse;
import eu.dnetlib.dsm.domain.AggregationInfo;
import eu.dnetlib.dsm.domain.ApiDetails;
import eu.dnetlib.dsm.domain.ApiDetailsResponse;
import eu.dnetlib.dsm.domain.Country;
import eu.dnetlib.dsm.domain.DatasourceDetailResponse;
import eu.dnetlib.dsm.domain.DatasourceDetails;
import eu.dnetlib.dsm.domain.DatasourceDetailsUpdate;
import eu.dnetlib.dsm.domain.DatasourceDetailsWithApis;
import eu.dnetlib.dsm.domain.DatasourceSnippetResponse;
import eu.dnetlib.dsm.domain.RegisteredDatasourceInfo;
import eu.dnetlib.dsm.domain.RequestFilter;
import eu.dnetlib.dsm.domain.RequestSort;
import eu.dnetlib.dsm.domain.RequestSortOrder;
import eu.dnetlib.dsm.domain.SimpleResponse;
import eu.dnetlib.dsm.model.Api;
import eu.dnetlib.dsm.model.Datasource;
import eu.dnetlib.dsm.model.Identity;
import eu.dnetlib.dsm.utils.DsmMappingUtils;
import eu.dnetlib.dsm.utils.ResponseUtils;
import eu.dnetlib.dsm.utils.WfLoggerClient;
import eu.dnetlib.errors.DsmException;
import eu.dnetlib.errors.DsmForbiddenException;
import eu.dnetlib.errors.DsmNotFoundException;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/api/dsm/1.0")
@ConditionalOnProperty(value = "openaire.api.enable.dsm", havingValue = "true")
@Tag(name = "OpenAIRE DSM API", description = "the OpenAIRE Datasource Manager API")
public class DsmApiControllerV1 extends AbstractDsmController {
private static final Log log = LogFactory.getLog(DsmApiControllerV1.class);
@Autowired
private WfLoggerClient wfLoggerClient;
@Autowired
private DsmService dsmService;
@GetMapping("/ds/countries")
public List<Country> listCountries() throws DsmException {
return dsmService.listCountries();
}
@PostMapping("/ds/searchdetails/{page}/{size}")
public DatasourceDetailResponse searchDsDetails(
@RequestParam final RequestSort requestSortBy,
@RequestParam final RequestSortOrder order,
@RequestBody final RequestFilter requestFilter,
@PathVariable final int page,
@PathVariable final int size) throws DsmException {
final StopWatch stop = StopWatch.createStarted();
final DatasourceDetailResponse rsp = dsmService.searchDetails(requestSortBy, order, requestFilter, page, size);
return prepareResponse(page, size, stop, rsp);
}
@GetMapping("/ds/aggregationhistory/{dsId}")
public AggregationHistoryResponse aggregationHistory(@PathVariable final String dsId) throws DsmException {
final StopWatch stop = StopWatch.createStarted();
final List<AggregationInfo> history = wfLoggerClient.getAggregationHistory(dsId);
final AggregationHistoryResponse rsp = new AggregationHistoryResponse(history);
rsp.setHeader(ResponseUtils.header(history.size()));
return prepareResponse(0, rsp.getAggregationInfo().size(), stop, rsp);
}
@PostMapping("/ds/searchsnippet/{page}/{size}")
public DatasourceSnippetResponse searchSnippet(
@RequestParam final RequestSort requestSortBy,
@RequestParam final RequestSortOrder order,
@RequestBody final RequestFilter requestFilter,
@PathVariable final int page,
@PathVariable final int size) throws DsmException {
final StopWatch stop = StopWatch.createStarted();
final DatasourceSnippetResponse rsp = dsmService.searchSnippet(requestSortBy, order, requestFilter, page, size);
return prepareResponse(page, size, stop, rsp);
}
@PostMapping("/ds/searchregistered/{page}/{size}")
public DatasourceSnippetResponse searchRegistered(
@RequestParam final RequestSort requestSortBy,
@RequestParam final RequestSortOrder order,
@RequestBody final RequestFilter requestFilter,
@PathVariable final int page,
@PathVariable final int size) throws DsmException {
final StopWatch stop = StopWatch.createStarted();
final Page<Datasource> dsPage = dsmService.searchRegistered(requestSortBy, order, requestFilter, page, size);
final DatasourceSnippetResponse rsp =
ResponseUtils.snippetResponse(dsPage.map(DsmMappingUtils::asSnippetExtended).getContent(), dsPage.getTotalElements());
return prepareResponse(page, size, stop, rsp);
}
@GetMapping("/ds/recentregistered/{size}")
public SimpleResponse<RegisteredDatasourceInfo> recentRegistered(@PathVariable final int size) throws Throwable {
final StopWatch stop = StopWatch.createStarted();
final SimpleResponse<RegisteredDatasourceInfo> rsp = dsmService.searchRecentRegistered(size);
return prepareResponse(1, size, stop, rsp);
}
@GetMapping("/ds/countregistered")
public Long countRegistered(@RequestParam final String fromDate,
@RequestParam(required = false) final String typologyFilter) throws Throwable {
return dsmService.countRegisteredAfter(fromDate, typologyFilter);
}
@GetMapping("/ds/api/{dsId}")
public ApiDetailsResponse getApi(
@PathVariable final String dsId) throws DsmException {
final StopWatch stop = StopWatch.createStarted();
final Datasource ds = dsmService.findDs(dsId);
final List<Api> apis = dsmService.findApis(dsId);
final List<ApiDetails> api = apis.stream()
.map(DsmMappingUtils::asDetails)
.map(a -> a.setEoscDatasourceType(ds.getEoscDatasourceType()))
.map(a -> a.setTypology(ds.getTypology()))
.collect(Collectors.toList());
final ApiDetailsResponse rsp = ResponseUtils.apiResponse(api, api.size());
return prepareResponse(0, rsp.getApi().size(), stop, rsp);
}
@PostMapping("/api/baseurl/{page}/{size}")
public List<String> searchBaseUrls(
@RequestBody final RequestFilter requestFilter,
@PathVariable final int page,
@PathVariable final int size) throws DsmException {
return dsmService.findApiBaseURLs(requestFilter, page, size);
}
@DeleteMapping("/ds/api/{apiId}")
public void deleteApi(@PathVariable final String apiId) throws DsmForbiddenException, DsmNotFoundException {
dsmService.deleteApi(null, apiId);
}
@PostMapping("/ds/manage")
public void setManaged(
@RequestParam final String id,
@RequestParam final boolean managed) throws DsmException {
log.info(String.format("updated ds '%s' managed with '%s'", id, managed));
dsmService.setManaged(id, managed);
}
@GetMapping("/ds/managed/{id}")
public boolean isManaged(@PathVariable final String id) throws DsmException {
return dsmService.isManaged(id);
}
@PostMapping("/ds/add")
public void saveDs(@Valid @RequestBody final DatasourceDetails datasource) throws DsmException {
if (dsmService.existDs(datasource.getId())) { // TODO further check that the DS doesn't have any API
throw new DsmException(String.format("cannot register, datasource already defined '%s'", datasource.getId()));
}
dsmService.saveDs(asDbEntry(datasource));
log.info("DS saved, " + datasource.getId());
}
@PostMapping("/ds/addWithApis")
public void saveDsWithApis(@Valid @RequestBody final DatasourceDetailsWithApis d) throws DsmException {
if (d.getDatasource() == null) { throw new DsmException("Datasource field is null"); }
if (dsmService.existDs(d.getDatasource().getId())) { // TODO further check that the DS doesn't have any API
throw new DsmException(String.format("cannot register, datasource already defined '%s'", d.getDatasource().getId()));
}
dsmService.addDsAndApis(asDbEntry(d.getDatasource()), d.getApis());
}
@PostMapping("/ds/update")
public void updateDatasource(@RequestBody final DatasourceDetailsUpdate d) throws DsmException, DsmNotFoundException {
// initialize with current values from DB
final Datasource ds = dsmService.findDs(d.getId());
if (ds == null) { throw new DsmNotFoundException(String.format("ds '%s' does not exist", d.getId())); }
final Datasource update = asDbEntry(d);
if (ds.getIdentities() != null) {
final Set<Identity> identities = new HashSet<>(
Stream.of(update.getIdentities(), ds.getIdentities())
.flatMap(Collection::stream)
.collect(Collectors.toMap(i -> i.getIssuertype() + i.getPid(), Function.identity(), (i1, i2) -> i1))
.values());
copyNonNullProperties(update, ds);
ds.setIdentities(identities);
} else {
copyNonNullProperties(update, ds);
}
dsmService.saveDs(ds);
}
@PostMapping("/ds/api/baseurl")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "500", description = "unexpected error")
})
public void updateBaseUrl(
@RequestParam final String dsId,
@RequestParam final String apiId,
@RequestParam final String baseUrl) throws DsmException {
log.info(String.format("updated api '%s' baseurl with '%s'", apiId, baseUrl));
dsmService.updateApiBaseUrl(apiId, baseUrl);
}
@PostMapping("/ds/api/compliance")
public void updateCompliance(
@RequestParam final String dsId,
@RequestParam final String apiId,
@RequestParam final String compliance,
@RequestParam(required = false, defaultValue = "false") final boolean override) throws DsmException {
log.info(String.format("updated api '%s' compliance with '%s'", apiId, compliance));
dsmService.updateCompliance(null, apiId, compliance, override);
}
@PostMapping("/ds/api/oaiset")
public void updateOaiSetl(
@RequestParam final String dsId,
@RequestParam final String apiId,
@RequestParam final String oaiSet) throws DsmException, DsmNotFoundException {
dsmService.upsertApiOaiSet(apiId, oaiSet);
}
@PostMapping("/ds/api/add")
public void addApi(@RequestBody final ApiDetails api) throws DsmException {
if (StringUtils.isBlank(api.getDatasource())) { throw new DsmException("missing datasource id"); }
if (StringUtils.isBlank(api.getId())) {
api.setId(createId(api));
log.info(String.format("missing api id, created '%s'", api.getId()));
}
dsmService.addApi(asDbEntry(api));
log.info("API saved, id: " + api.getId());
}
}

View File

@ -0,0 +1,51 @@
package eu.dnetlib.dsm;
import java.util.List;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.dsm.domain.SimpleDatasourceInfo;
import eu.dnetlib.dsm.domain.SimpleResponse;
import eu.dnetlib.dsm.utils.ResponseUtils;
import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/api/dsm/2.0")
@ConditionalOnProperty(value = "openaire.api.enable.dsm", havingValue = "true")
@Tag(name = "OpenAIRE DSM API (version 2.0)", description = "the OpenAIRE Datasource Manager API 2.0")
public class DsmApiControllerV2 extends AbstractDsmController {
@Autowired
private DsmService dsmService;
@GetMapping("/recentregistered/{size}")
public SimpleResponse<SimpleDatasourceInfo> recentRegisteredV2(@PathVariable final int size) throws Throwable {
final StopWatch stop = StopWatch.createStarted();
final SimpleResponse<SimpleDatasourceInfo> rsp = dsmService.searchRecentRegisteredV2(size);
return prepareResponse(1, size, stop, rsp);
}
@GetMapping("/countfirstcollect")
public Long countFirstCollectAfter(@RequestParam final String fromDate,
@RequestParam(required = false) final String typologyFilter) throws Throwable {
return dsmService.countFirstCollect(fromDate, typologyFilter);
}
@GetMapping("/firstCollected")
public SimpleResponse<SimpleDatasourceInfo> firstCollectedAfter(@RequestParam final String fromDate,
@RequestParam(required = false) final String typologyFilter) throws Throwable {
final StopWatch stop = StopWatch.createStarted();
final List<SimpleDatasourceInfo> list = dsmService.findFirstCollectedAfter(fromDate, typologyFilter);
final SimpleResponse<SimpleDatasourceInfo> rsp = ResponseUtils.simpleResponse(list);
return prepareResponse(1, list.size(), stop, rsp);
}
}

View File

@ -0,0 +1,21 @@
package eu.dnetlib.is.context;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.is.model.context.Context;
public class AbstractContextController extends AbstractDnetController {
@Autowired
protected ContextService contextService;
@GetMapping("/")
public List<Context> listContexts() {
return contextService.listContexts();
}
}

View File

@ -0,0 +1,33 @@
package eu.dnetlib.is.context;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.errors.InformationServiceException;
import eu.dnetlib.is.model.context.Category;
import eu.dnetlib.is.model.context.Context;
import eu.dnetlib.is.model.context.CtxChildInfo;
@RestController
@RequestMapping("/ajax/contexts")
public class ContextAjaxController extends AbstractContextController {
@GetMapping("/{ctxId}")
public Context getContext(@PathVariable final String ctxId) throws InformationServiceException {
return contextService.getContext(ctxId);
}
@GetMapping("/{parent}/categories")
public Iterable<Category> listCategories(@PathVariable final String parent) {
return contextService.listCategories(parent);
}
@GetMapping("/{level}/{parent}/concepts")
public Iterable<? extends CtxChildInfo> listCategories(@PathVariable final int level, @PathVariable final String parent)
throws InformationServiceException {
return contextService.listConcepts(level, parent);
}
}

View File

@ -0,0 +1,21 @@
package eu.dnetlib.is.context;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.dnetlib.errors.InformationServiceException;
@RestController
@RequestMapping("/api/contexts")
public class ContextApiController extends AbstractContextController {
@GetMapping("/{ctxId}")
public ObjectNode getContextFull(@PathVariable final String ctxId) throws InformationServiceException {
return contextService.getContextFull(ctxId);
}
}

View File

@ -0,0 +1,41 @@
package eu.dnetlib.is.email;
import java.util.List;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.common.model.EmailTemplate;
import eu.dnetlib.notifications.mail.EmailService;
@RestController
@RequestMapping("/ajax/templates/email")
public class EmailTemplateController extends AbstractDnetController {
@Autowired
protected EmailService emailService;
@GetMapping("/")
public List<EmailTemplate> listEmailTemplates() {
return emailService.listEmailTemplates();
}
@PostMapping("/")
public List<EmailTemplate> saveEmailTemplate(@RequestBody final EmailTemplate email) {
emailService.saveEmailTemplate(email);
return emailService.listEmailTemplates();
}
@DeleteMapping("/{id}")
public List<EmailTemplate> deleteEmailTemplate(@PathVariable final String id) {
emailService.deleteEmailTemplate(id);
return emailService.listEmailTemplates();
}
}

View File

@ -0,0 +1,113 @@
package eu.dnetlib.is.importer;
import java.io.IOException;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import eu.dnetlib.is.context.repository.CategoryRepository;
import eu.dnetlib.is.context.repository.ConceptLevel0Repository;
import eu.dnetlib.is.context.repository.ConceptLevel1Repository;
import eu.dnetlib.is.context.repository.ConceptLevel2Repository;
import eu.dnetlib.is.context.repository.ContextRepository;
import eu.dnetlib.is.model.context.Category;
import eu.dnetlib.is.model.context.ConceptLevel0;
import eu.dnetlib.is.model.context.ConceptLevel1;
import eu.dnetlib.is.model.context.ConceptLevel2;
import eu.dnetlib.is.model.context.Context;
import eu.dnetlib.is.model.context.CtxChildInfo;
import eu.dnetlib.is.model.context.CtxInfo;
import eu.dnetlib.is.model.context.Parameter;
@Service
public class ContextImporter {
@Autowired
private ContextRepository contextRepository;
@Autowired
private CategoryRepository categoryRepository;
@Autowired
private ConceptLevel0Repository conceptLevel0Repository;
@Autowired
private ConceptLevel1Repository conceptLevel1Repository;
@Autowired
private ConceptLevel2Repository conceptLevel2Repository;
private static final Log log = LogFactory.getLog(ContextImporter.class);
public void loadFromOldProfile(final String xml) throws DocumentException, IOException {
DocumentHelper.parseText(xml)
.selectNodes("//context")
.forEach(this::saveContext);
}
private void saveContext(final Node node) {
final Context ctx = new Context();
setCommonInfo(ctx, null, node);
ctx.setType(node.valueOf("@type"));
contextRepository.save(ctx);
log.info("Saved context: " + ctx.getId());
node.selectNodes("./category").forEach(n -> saveCategory(n, ctx.getId()));
}
private void saveCategory(final Node node, final String ctxId) {
final Category cat = new Category();
setCommonInfo(cat, ctxId, node);
categoryRepository.save(cat);
log.info("- Saved category: " + cat.getId());
node.selectNodes("./concept").forEach(n -> saveConceptLvl0(n, cat.getId()));
}
private void saveConceptLvl0(final Node node, final String parent) {
final ConceptLevel0 c0 = new ConceptLevel0();
setCommonInfo(c0, parent, node);
conceptLevel0Repository.save(c0);
log.info("-- Saved concept 0: " + c0.getId());
node.selectNodes("./concept").forEach(n -> saveConceptLvl1(n, c0.getId()));
}
private void saveConceptLvl1(final Node node, final String parent) {
final ConceptLevel1 c1 = new ConceptLevel1();
setCommonInfo(c1, parent, node);
conceptLevel1Repository.save(c1);
log.info("--- Saved concept 1: " + c1.getId());
node.selectNodes("./concept").forEach(n -> saveConceptLvl2(n, c1.getId()));
}
private void saveConceptLvl2(final Node node, final String parent) {
final ConceptLevel2 c2 = new ConceptLevel2();
setCommonInfo(c2, parent, node);
conceptLevel2Repository.save(c2);
log.info("---- Saved concept 2: " + c2.getId());
}
private void setCommonInfo(final CtxInfo o, final String parent, final Node n) {
o.setId(n.valueOf("@id"));
o.setLabel(n.valueOf("@label"));
if (o instanceof CtxChildInfo) {
((CtxChildInfo) o).setParent(parent);
((CtxChildInfo) o).setClaim(BooleanUtils.toBoolean(n.valueOf("@claim")));
}
o.setParameters(n.selectNodes("./param")
.stream()
.map(np -> new Parameter(np.valueOf("@name"), np.getText()))
.sorted()
.toArray(Parameter[]::new));
}
}

View File

@ -0,0 +1,80 @@
package eu.dnetlib.is.importer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
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.model.vocabulary.Vocabulary;
@RestController
@RequestMapping("/api/import")
public class ImporterController extends AbstractDnetController {
// EXAMPLE:
// find ./VocabularyDSResourceType/ -name "*.xml" -exec curl -X POST "http://localhost:8280/api/import/vocabulary" -H "accept: */*" -H
// "Content-Type: text/plain" --data-binary @{} \;
// find ./DedupConfigurationDSResources/ -name "*.xml" -exec curl -X POST "http://localhost:8280/api/import/resource" -H "accept: */*"
// -H "Content-Type: text/plain" --data-binary @{} \;
@Autowired
private ContextImporter contextImporter;
@Autowired
private OldProfilesImporter oldProfilesImporter;
@Autowired
private WfHistoryImporter wfHistoryImporter;
@PostMapping(value = "/context", consumes = {
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE
})
public List<String> importContext(final HttpServletRequest request) throws DocumentException, IOException {
final String xml = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
contextImporter.loadFromOldProfile(xml);
return Arrays.asList("Done.");
}
@PostMapping(value = "/resource", consumes = {
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE
})
public String importResource(final HttpServletRequest request) throws Exception {
final String xml = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
return oldProfilesImporter.importSimpleResource(xml);
}
@PostMapping(value = "/vocabulary", consumes = {
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE
})
public Vocabulary importVocabulary(final HttpServletRequest request) throws Exception, IOException {
final String xml = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
return oldProfilesImporter.importVocabulary(xml);
}
@GetMapping(value = "/wf_logs")
public List<String> importWfLogs(@RequestParam final String path) throws Exception {
// mongoexport -d dnet_logs -c wf_logs --jsonArray -o /tmp/mongodump.json
wfHistoryImporter.load(path);
return Arrays.asList("Done.");
}
}

View File

@ -0,0 +1,156 @@
package eu.dnetlib.is.importer;
import java.time.LocalDateTime;
import javax.transaction.Transactional;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import eu.dnetlib.errors.InformationServiceException;
import eu.dnetlib.is.model.resource.SimpleResource;
import eu.dnetlib.is.model.vocabulary.Synonym;
import eu.dnetlib.is.model.vocabulary.Vocabulary;
import eu.dnetlib.is.model.vocabulary.VocabularyTerm;
import eu.dnetlib.is.resource.ResourceValidator;
import eu.dnetlib.is.resource.repository.SimpleResourceRepository;
import eu.dnetlib.is.vocabulary.repository.VocabularyRepository;
import eu.dnetlib.is.vocabulary.repository.VocabularyTermRepository;
import eu.dnetlib.utils.XmlIndenter;
@Service
public class OldProfilesImporter {
@Autowired
private SimpleResourceRepository simpleResourceRepository;
@Autowired
private VocabularyRepository vocabularyRepository;
@Autowired
private VocabularyTermRepository vocabularyTermRepository;
@Autowired
private ResourceValidator resourceValidator;
@Transactional
public String importSimpleResource(final String xml) throws InformationServiceException {
try {
final Document doc = DocumentHelper.parseText(xml);
final String id = StringUtils.substringBefore(doc.valueOf("//RESOURCE_IDENTIFIER/@value"), "_");
final LocalDateTime now = LocalDateTime.now();
final SimpleResource res = new SimpleResource();
res.setId(id);
res.setCreationDate(now);
res.setModificationDate(now);
String resContent;
switch (doc.valueOf("//RESOURCE_TYPE/@value")) {
case "CleanerDSResourceType":
res.setType("cleaning_rule");
res.setName(doc.valueOf("//CLEANER_NAME"));
res.setDescription(doc.valueOf("//CLEANER_DESCRIPTION"));
for (final Node n : doc.selectNodes("//RULE")) {
final Element e = (Element) n;
e.addAttribute("vocabulary", e.valueOf("@vocabularies"));
for (final Attribute attr : e.attributes()) {
if (attr.getName() == "vocabularies" || attr.getName() == "groovy") {
attr.detach();
}
}
}
resContent = XmlIndenter.indent(doc.selectSingleNode("//CLEANER_RULES"));
break;
case "TransformationRuleDSResourceType":
res.setName(doc.valueOf("//SCRIPT/TITLE"));
res.setDescription("");
if (doc.selectNodes("//*[local-name() = 'stylesheet']").size() > 0) {
res.setType("transformation_rule_xslt");
resContent = XmlIndenter.indent(doc.selectSingleNode("//*[local-name() = 'stylesheet']"));
} else {
final String code = doc.valueOf("//SCRIPT/CODE").trim();
try {
final Document xsltDoc = DocumentHelper.parseText(code);
res.setType("transformation_rule_xslt");
resContent = XmlIndenter.indent(xsltDoc);
} catch (final DocumentException e) {
res.setType("transformation_rule_legacy");
resContent = code;
}
}
break;
case "HadoopJobConfigurationDSResourceType":
res.setType("hadoop_job_configuration");
res.setName(doc.valueOf("//HADOOP_JOB/@name"));
res.setDescription(doc.valueOf("//HADOOP_JOB/DESCRIPTION"));
resContent = XmlIndenter.indent(doc.selectSingleNode("//HADOOP_JOB"));
break;
case "DedupConfigurationDSResourceType":
res.setType("dedup_configuration");
res.setName(doc.valueOf("//DESCRIPTION"));
res.setDescription(doc.valueOf("//DESCRIPTION"));
resContent = doc.valueOf("//DEDUPLICATION");
break;
default:
throw new InformationServiceException("Invalid resource type: " + doc.valueOf("//RESOURCE_TYPE/@value"));
}
resourceValidator.validate(res.getType(), resContent.trim());
simpleResourceRepository.save(res);
simpleResourceRepository.setContentById(id, resContent.trim());
return res.getId();
} catch (final Exception e) {
throw new InformationServiceException("Error parsing file", e);
}
}
@Transactional
public Vocabulary importVocabulary(final String xml) throws Exception {
final Document doc = DocumentHelper.parseText(xml);
final Vocabulary voc = new Vocabulary();
final String vocId = doc.valueOf("//VOCABULARY_NAME/@code");
final String vocName = doc.valueOf("//VOCABULARY_NAME");
final String vocDesc = doc.valueOf("//VOCABULARY_DESCRIPTION");
voc.setId(vocId);
voc.setName(vocName);
voc.setDescription(vocDesc);
vocabularyRepository.save(voc);
for (final Node n : doc.selectNodes("//TERM")) {
final VocabularyTerm term = new VocabularyTerm();
term.setVocabulary(vocId);
term.setCode(n.valueOf("@code"));
term.setName(n.valueOf("@english_name"));
term.setEncoding(n.valueOf("@encoding"));
term.setSynonyms(n.selectNodes(".//SYNONYM")
.stream()
.map(ns -> new Synonym(ns.valueOf("@term"), ns.valueOf("@encoding")))
.sorted()
.distinct()
.toArray(Synonym[]::new));
vocabularyTermRepository.save(term);
}
return voc;
}
}

View File

@ -0,0 +1,85 @@
package eu.dnetlib.is.importer;
import java.io.File;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.dnetlib.manager.history.model.WfHistoryEntry;
import eu.dnetlib.manager.history.repository.WfHistoryEntryRepository;
@Service
public class WfHistoryImporter {
private static final Log log = LogFactory.getLog(WfHistoryImporter.class);
@Autowired
private WfHistoryEntryRepository wfHistoryEntryRepository;
public void load(final String path) throws Exception {
final ObjectMapper mapper = new ObjectMapper();
final JsonNode rootNode = mapper.readTree(new File(path));
rootNode.forEach(wf -> saveWf(wf));
}
private void saveWf(final JsonNode node) {
final WfHistoryEntry wf = new WfHistoryEntry();
wf.setProcessId(node.get("system:processId").asText());
wf.setName(node.get("system:wfName").asText());
wf.setFamily(node.get("system:profileFamily").asText());
if (node.has("dataprovider:id")) {
wf.setDsId(node.get("dataprovider:id").asText());
}
if (node.has("dataprovider:name")) {
wf.setDsName(node.get("dataprovider:name").asText());
}
if (node.has("dataprovider:interface")) {
wf.setDsApi(node.get("dataprovider:interface").asText());
}
wf.setStartDate(LocalDateTime
.ofInstant(Instant.ofEpochMilli(NumberUtils.toLong(node.get("system:startDate").asText())), TimeZone.getDefault().toZoneId()));
wf.setEndDate(LocalDateTime.ofInstant(Instant.ofEpochMilli(NumberUtils.toLong(node.get("system:endDate").asText())), TimeZone.getDefault().toZoneId()));
if (BooleanUtils.toBoolean(node.get("system:isCompletedSuccessfully").asText())) {
wf.setStatus("success");
} else {
wf.setStatus("failure");
}
final Map<String, String> details = new LinkedHashMap<>();
final Iterator<Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
final Entry<String, JsonNode> f = fields.next();
if (f.getValue().isValueNode()) {
details.put(f.getKey(), f.getValue().asText());
}
}
wf.setDetails(details);
wfHistoryEntryRepository.save(wf);
log.info("Wf saved with id: " + wf.getProcessId());
}
}

View File

@ -0,0 +1,146 @@
package eu.dnetlib.is.info;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.common.controller.AbstractDnetController;
@RestController
@RequestMapping("/ajax/info")
public class InfoAjaxController extends AbstractDnetController {
@Autowired
private ConfigurableEnvironment configurableEnvironment;
@Autowired
private ResourceLoader resourceLoader;
private final RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
private static final Log log = LogFactory.getLog(InfoAjaxController.class);
@GetMapping("/")
public List<InfoSection<?>> info() throws Exception {
final List<InfoSection<?>> res = new ArrayList<>();
res.add(jvm());
res.add(args());
res.addAll(props());
res.add(modules());
return res;
}
private InfoSection<KeyValue<String>> jvm() {
final InfoSection<KeyValue<String>> jvm = new InfoSection<>("JVM");
jvm.getData().add(new KeyValue<>("JVM Name", mxbean.getVmName()));
jvm.getData().add(new KeyValue<>("JVM Vendor", mxbean.getVmVendor()));
jvm.getData().add(new KeyValue<>("JVM Version", mxbean.getVmVersion()));
jvm.getData().add(new KeyValue<>("JVM Spec Name", mxbean.getSpecName()));
jvm.getData().add(new KeyValue<>("JVM Spec Vendor", mxbean.getSpecVendor()));
jvm.getData().add(new KeyValue<>("JVM Spec Version", mxbean.getSpecVersion()));
jvm.getData().add(new KeyValue<>("Running JVM Name", mxbean.getName()));
jvm.getData().add(new KeyValue<>("Management Spec Version", mxbean.getManagementSpecVersion()));
return jvm;
}
private InfoSection<KeyValue<String>> args() {
final InfoSection<KeyValue<String>> libs = new InfoSection<>("Arguments");
libs.getData().add(new KeyValue<>("Input arguments", StringUtils.join(mxbean.getInputArguments(), " ")));
return libs;
}
private List<InfoSection<KeyValue<?>>> props() {
final List<InfoSection<KeyValue<?>>> res = new ArrayList<>();
configurableEnvironment.getPropertySources().forEach(ps -> {
final InfoSection<KeyValue<?>> section = new InfoSection<>("Properties: " + ps.getName());
addAllProperties(section, ps);
res.add(section);
});
return res;
}
private void addAllProperties(final InfoSection<KeyValue<?>> res, final PropertySource<?> ps) {
if (ps instanceof CompositePropertySource) {
final CompositePropertySource cps = (CompositePropertySource) ps;
cps.getPropertySources().forEach(x -> addAllProperties(res, x));
} else if (ps instanceof EnumerablePropertySource<?>) {
final EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) ps;
Arrays.asList(eps.getPropertyNames()).forEach(k -> res.getData().add(new KeyValue<>(k, eps.getProperty(k))));
} else {}
}
public InfoSection<JavaModule> modules() throws IOException {
final Map<String, Map<String, JavaModule>> modules = new LinkedHashMap<>();
final MavenXpp3Reader reader = new MavenXpp3Reader();
for (final Resource res : ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath*:/META-INF/**/pom.xml")) {
try {
final Model model = reader.read(res.getInputStream());
final String name = model.getArtifactId();
String groupId = model.getGroupId();
for (Parent parent = model.getParent(); groupId == null && model.getParent() != null; parent = model.getParent()) {
groupId = parent.getGroupId();
}
String version = model.getVersion();
for (Parent parent = model.getParent(); version == null && model.getParent() != null; parent = model.getParent()) {
version = parent.getVersion();
}
if (!modules.containsKey(groupId)) {
modules.put(groupId, new HashMap<String, JavaModule>());
}
if (!modules.get(groupId).containsKey(name)) {
modules.get(groupId).put(name, new JavaModule(groupId, name));
}
modules.get(groupId).get(name).addFileAndVersion(res.getURI().toString(), version);
} catch (final Exception e) {
log.error("Error evaluating pom: " + res.getURI());
log.debug("-- ERROR --", e);
}
}
final InfoSection<JavaModule> res = new InfoSection<>("Modules");
for (final Entry<String, Map<String, JavaModule>> e : modules.entrySet()) {
for (final Entry<String, JavaModule> e1 : e.getValue().entrySet()) {
res.getData().add(e1.getValue());
}
}
Collections.sort(res.getData());
return res;
}
}

View File

@ -0,0 +1,24 @@
package eu.dnetlib.is.info;
import java.util.ArrayList;
import java.util.List;
public class InfoSection<T> {
private final String name;
private final List<T> data;
public InfoSection(final String name) {
this.name = name;
this.data = new ArrayList<>();
}
public String getName() {
return name;
}
public List<T> getData() {
return data;
}
}

View File

@ -0,0 +1,50 @@
package eu.dnetlib.is.info;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
public class JavaModule implements Comparable<JavaModule> {
private final String group;
private final String name;
private final Set<String> versions = new LinkedHashSet<>();
private final Set<String> files = new LinkedHashSet<>();
public JavaModule(final String group, final String name) {
this.group = group;
this.name = name;
}
public String getGroup() {
return group;
}
public String getName() {
return name;
}
public Set<String> getVersions() {
return versions;
}
public Set<String> getFiles() {
return files;
}
public void addFileAndVersion(final String file, final String version) {
files.add(file);
versions.add(version);
}
@Override
public int compareTo(final JavaModule o) {
if (getGroup().equals(o.getGroup())) {
return StringUtils.compare(getName(), o.getName());
} else {
return StringUtils.compare(getGroup(), o.getGroup());
}
}
}

View File

@ -0,0 +1,21 @@
package eu.dnetlib.is.info;
public class KeyValue<T> {
private final String k;
private final T v;
public KeyValue(final String k, final T v) {
this.k = k;
this.v = v;
}
public String getK() {
return k;
}
public T getV() {
return v;
}
}

View File

@ -0,0 +1,20 @@
package eu.dnetlib.is.protocol;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import eu.dnetlib.common.controller.AbstractDnetController;
public class AbstractProtocolController extends AbstractDnetController {
@Autowired
protected ProtocolService protocolService;
@GetMapping("/")
public List<ProtocolDesc> listProtocols() {
return protocolService.listProtocols();
}
}

View File

@ -0,0 +1,27 @@
package eu.dnetlib.is.protocol;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RestController;
@RestController
@RequestMapping("/ajax/protocols")
public class ProtocolAjaxController extends AbstractProtocolController {
@DeleteMapping("/{protocol}")
public List<ProtocolDesc> deleteProtocol(@PathVariable final String protocol) {
protocolService.deleteProtocols(protocol);
return protocolService.listProtocols();
}
@PostMapping("/{protocol}/terms")
public List<ProtocolDesc> saveTerm(@PathVariable final ProtocolDesc protocol) {
protocolService.saveProtocol(protocol);
return protocolService.listProtocols();
}
}

View File

@ -0,0 +1,10 @@
package eu.dnetlib.is.protocol;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/protocols")
public class ProtocolApiController extends AbstractProtocolController {
}

View File

@ -0,0 +1,52 @@
package eu.dnetlib.is.resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.errors.InformationServiceException;
import eu.dnetlib.is.model.resource.SimpleResource;
import eu.dnetlib.utils.XmlIndenter;
public class AbstractResourceController extends AbstractDnetController {
@Autowired
protected SimpleResourceService service;
@GetMapping("/{typeId}")
public List<SimpleResource> listResources(@PathVariable final String typeId) {
return service.listResources(typeId);
}
@GetMapping("/{resId}/metadata")
public SimpleResource getMetadata(@PathVariable final String resId) throws InformationServiceException {
return service.getMetadata(resId);
}
@GetMapping("/{resId}/content")
public void getContent(@PathVariable final String resId, final HttpServletResponse res) throws InformationServiceException {
final String ctype = service.getContentType(resId);
res.setCharacterEncoding(StandardCharsets.UTF_8.name());
res.setContentType(ctype);
final String content =
ctype.equals(MediaType.APPLICATION_XML_VALUE) ? XmlIndenter.indent(service.getContent(resId)) : service.getContent(resId);
try {
IOUtils.write(content, res.getOutputStream(), StandardCharsets.UTF_8.name());
} catch (final IOException e) {
throw new InformationServiceException("Error retrieving content", e);
}
}
}

View File

@ -0,0 +1,63 @@
package eu.dnetlib.is.resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.errors.InformationServiceException;
import eu.dnetlib.is.model.resource.SimpleResource;
@RestController
@RequestMapping("/ajax/resources")
public class ResourceAjaxController extends AbstractResourceController {
@PostMapping("/")
public SimpleResource newResource(@RequestParam final String name,
@RequestParam final String type,
@RequestParam(required = false, defaultValue = "") final String subtype,
@RequestParam(required = false, defaultValue = "") final String description,
@RequestParam final String content)
throws InformationServiceException {
return service.saveNewResource(name, type, subtype, description, content);
}
@DeleteMapping("/{resId}")
public void deleteResource(@PathVariable final String resId) {
service.deleteResource(resId);
}
@PostMapping("/{resId}/metadata")
public void saveMetadata(@PathVariable final String resId, @RequestBody final SimpleResource r) throws InformationServiceException {
service.saveMetadata(resId, r);
}
@PostMapping("/{resId}/content")
public void saveContent(@PathVariable final String resId, @RequestParam final String content) throws InformationServiceException {
service.saveContent(resId, content);
}
@PostMapping(value = "/{resId}/file", consumes = {
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE
})
public void uploadContent(@PathVariable final String resId, final HttpServletRequest request) throws InformationServiceException {
try {
final String content = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
service.saveContent(resId, content);
} catch (final IOException e) {
throw new InformationServiceException("Error processing input file", e);
}
}
}

View File

@ -0,0 +1,65 @@
package eu.dnetlib.is.resource;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.is.model.resource.ResourceType;
import eu.dnetlib.is.resource.repository.ResourceTypeRepository;
@RestController
@RequestMapping("/api/resources")
public class ResourceApiController extends AbstractResourceController {
@Autowired
private ResourceTypeRepository resourceTypeRepository;
@GetMapping("/")
private List<SimpleResourceType> types() {
return StreamSupport.stream(resourceTypeRepository.findAll().spliterator(), false)
.filter(rt -> rt.isSimple())
.map(rt -> new SimpleResourceType(rt))
.collect(Collectors.toList());
}
class SimpleResourceType {
private final String typeId;
private final String typeName;
private final String contentType;
private final long count;
public SimpleResourceType(final ResourceType rt) {
this.typeId = rt.getId();
this.typeName = rt.getName();
this.contentType = rt.getContentType();
this.count = rt.getCount();
}
public String getTypeId() {
return typeId;
}
public String getTypeName() {
return typeName;
}
public String getContentType() {
return contentType;
}
public long getCount() {
return count;
}
}
}

View File

@ -0,0 +1,27 @@
package eu.dnetlib.is.vocabulary;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import eu.dnetlib.common.controller.AbstractDnetController;
import eu.dnetlib.is.model.vocabulary.Vocabulary;
import eu.dnetlib.is.model.vocabulary.VocabularyTerm;
public class AbstractVocabularyController extends AbstractDnetController {
@Autowired
protected VocabularyService vocabularyService;
@GetMapping("/")
public List<Vocabulary> listVocs() {
return vocabularyService.listVocs();
}
@GetMapping("/{vocabulary}/terms")
public Iterable<VocabularyTerm> listTerms(@PathVariable final String vocabulary) {
return vocabularyService.listTerms(vocabulary);
}
}

View File

@ -0,0 +1,50 @@
package eu.dnetlib.is.vocabulary;
import java.util.List;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.errors.InformationServiceException;
import eu.dnetlib.is.model.vocabulary.Vocabulary;
import eu.dnetlib.is.model.vocabulary.VocabularyTerm;
@RestController
@RequestMapping("/ajax/vocs")
public class VocabularyAjaxController extends AbstractVocabularyController {
@GetMapping("/{vocabulary}")
public Vocabulary getVoc(@PathVariable final String vocabulary) throws InformationServiceException {
return vocabularyService.getVoc(vocabulary);
}
@DeleteMapping("/{vocabulary}")
public List<Vocabulary> deleteVocs(@PathVariable final String vocabulary) {
vocabularyService.deleteVocs(vocabulary);
return vocabularyService.listVocs();
}
@PostMapping("/")
public List<Vocabulary> saveVoc(@RequestBody final Vocabulary voc) {
vocabularyService.saveVoc(voc);
return vocabularyService.listVocs();
}
@PostMapping("/{vocabulary}/terms")
public Iterable<VocabularyTerm> saveTerm(@PathVariable final String vocabulary, @RequestBody final VocabularyTerm term) {
vocabularyService.saveTerms(vocabulary, term);
return vocabularyService.listTerms(vocabulary);
}
@DeleteMapping("/{vocabulary}/terms/{term}")
public Iterable<VocabularyTerm> deleteTerms(@PathVariable final String vocabulary, @PathVariable final String term) {
vocabularyService.deleteTerms(vocabulary, term);
return vocabularyService.listTerms(vocabulary);
}
}

View File

@ -0,0 +1,10 @@
package eu.dnetlib.is.vocabulary;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/vocs")
public class VocabularyApiController extends AbstractVocabularyController {
}

View File

@ -0,0 +1,86 @@
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
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;
@RestController
@RequestMapping("/ajax/wfs")
public class WfConfigurationsController extends AbstractDnetController {
@Autowired
private WorkflowManagerService wfManagerService;
@GetMapping("/sections")
public List<WorkflowSection> listWfSections() throws Exception {
return wfManagerService.listSections();
}
@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());
}
@GetMapping("/conf/{id}")
public WorkflowConfiguration getWfConfiguration(@PathVariable final String id) throws Exception {
return wfManagerService.findWorkflowConfiguration(id);
}
@PostMapping("/conf")
public WorkflowConfiguration saveWfConfiguration(@RequestBody final WorkflowConfiguration conf) throws Exception {
return wfManagerService.saveWfConfiguration(conf);
}
@DeleteMapping("/conf/{id}")
public void deleteWfConfiguration(@PathVariable final String id) throws Exception {
wfManagerService.deleteWfConfiguration(id);
}
@GetMapping("/conf/{id}/start")
public ExecutionStatus startWorkflowConfiguration(@PathVariable final String id) throws Exception {
return wfManagerService.startWorkflowConfiguration(id);
}
@GetMapping("/conf/{id}/destroy")
public ExecutionStatus destroyWorkflowConfiguration(@PathVariable final String id) throws Exception {
return wfManagerService.destroyWorkflowConfiguration(id);
}
@GetMapping("/process/{id}")
public ExecutionStatus findProcess(@PathVariable final String id) throws Exception {
return wfManagerService.findProcess(id);
}
@DeleteMapping("/process/{id}")
public void killProcess(@PathVariable final String id) throws Exception {
wfManagerService.killProcess(id);
}
@GetMapping("/conf/{id}/subscriptions")
public List<WorkflowSubscription> listWorkflowSubscriptions(@PathVariable final String id) throws Exception {
return wfManagerService.listSubscriptions(id);
}
@PostMapping("/conf/{id}/subscriptions")
public void saveWorkflowSubscriptions(@PathVariable final String id, @RequestBody final List<WorkflowSubscription> subscriptions) throws Exception {
wfManagerService.saveSubscriptions(id, subscriptions);
}
}

View File

@ -0,0 +1,41 @@
package eu.dnetlib.manager.wf;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.manager.history.WorkflowLogger;
import eu.dnetlib.manager.history.model.WfHistoryEntry;
@RestController
@RequestMapping("/ajax/wf_history")
public class WfHistoryAjaxController extends AbstractDnetController {
@Autowired
private WorkflowLogger logger;
@GetMapping("/")
public List<WfHistoryEntry> history(
@RequestParam(required = true) final int total,
@RequestParam(required = false) final Long from,
@RequestParam(required = false) final Long to) {
return logger.history(total, from, to);
}
@GetMapping("/byConf/{wfConfId}")
public List<WfHistoryEntry> history(@PathVariable final String wfConfId) {
return logger.history(wfConfId);
}
@GetMapping("/proc/{processId}")
public WfHistoryEntry getProcessExecution(@PathVariable final String processId) {
return logger.getLog(processId);
}
}

View File

@ -0,0 +1,71 @@
server.port=8280
server.public_url =
server.public_desc = API Base URL
spring.thymeleaf.check-template-location=false
dnet.configuration.infrastructure = LOCAL DEV
spring.profiles.active=dev
maven.pom.path = /META-INF/maven/eu.dnetlib.dhp/dnet-is-application/effective-pom.xml
spring.main.banner-mode = off
logging.level.root = INFO
#logging.level.org.springframework = DEBUG
management.endpoints.web.exposure.include = prometheus,health
management.endpoints.web.base-path = /
management.endpoints.web.path-mapping.prometheus = metrics
management.endpoints.web.path-mapping.health = health
# Main Database for IS
is.datasource.jdbc-url=jdbc:postgresql://localhost:5432/new_is
is.datasource.username=dnet
is.datasource.password=dnetPwd
is.datasource.driver-class-name=org.postgresql.Driver
# Database for Main Entities
openaire.datasource.jdbc-url=jdbc:postgresql://localhost:5432/dnet_openaireplus
openaire.datasource.username=dnet
openaire.datasource.password=dnetPwd
openaire.datasource.driver-class-name=org.postgresql.Driver
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = validate
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.hbm2dll.extra_physical_table_types = MATERIALIZED VIEW
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.use_sql_comments=false
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.database=default
openaire.api.enable.dsm = true
# Hadoop
dhp.mdstore-manager.hadoop.cluster = GARR
dhp.mdstore-manager.hdfs.base-path = /data/dnet.dev/mdstore
dhp.mdstore-manager.hadoop.user = dnet.dev
#dhp.mdstore-manager.hadoop.zeppelin.base-url = https://iis-cdh5-test-gw.ocean.icm.edu.pl/zeppelin
#dhp.mdstore-manager.hadoop.zeppelin.login =
#dhp.mdstore-manager.hadoop.zeppelin.password =
dhp.mdstore-manager.hadoop.zeppelin.base-url = https://hadoop-zeppelin.garr-pa1.d4science.org
dhp.mdstore-manager.hadoop.zeppelin.login =
dhp.mdstore-manager.hadoop.zeppelin.password =
dhp.mdstore-manager.hadoop.zeppelin.name-prefix = mdstoreManager
# Email Configuration
dnet.configuration.mail.sender.email =
dnet.configuration.mail.sender.name =
dnet.configuration.mail.cc =
dnet.configuration.mail.smtp.host =
dnet.configuration.mail.smtp.port = 25
dnet.configuration.mail.smtp.user =
dnet.configuration.mail.smtp.password =

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Autogenerated by Cloudera Manager-->
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop-rm1.garr-pa1.d4science.org:8020</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>
<property>
<name>fs.trash.checkpoint.interval</name>
<value>60</value>
</property>
<property>
<name>net.topology.script.file.name</name>
<value>{{CMF_CONF_DIR}}/topology.py</value>
</property>
<property>
<name>hadoop.security.authentication</name>
<value>simple</value>
</property>
<property>
<name>hadoop.security.authorization</name>
<value>false</value>
</property>
<property>
<name>hadoop.rpc.protection</name>
<value>authentication</value>
</property>
<property>
<name>hadoop.ssl.enabled</name>
<value>false</value>
</property>
<property>
<name>hadoop.ssl.require.client.cert</name>
<value>false</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.keystores.factory.class</name>
<value>org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.server.conf</name>
<value>ssl-server.xml</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.client.conf</name>
<value>ssl-client.xml</value>
<final>true</final>
</property>
<property>
<name>hadoop.security.auth_to_local</name>
<value>DEFAULT</value>
</property>
<property>
<name>hadoop.proxyuser.oozie.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.oozie.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.flume.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.flume.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.HTTP.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.HTTP.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hive.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hive.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hue.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hue.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.httpfs.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.httpfs.groups</name>
<value>*</value>
</property>
<property>
<name>hue.kerberos.principal.shortname</name>
<value>hue</value>
</property>
<property>
<name>hadoop.proxyuser.hdfs.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hdfs.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.yarn.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.yarn.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.security.group.mapping</name>
<value>org.apache.hadoop.security.ShellBasedUnixGroupsMapping</value>
</property>
<property>
<name>hadoop.security.instrumentation.requires.admin</name>
<value>false</value>
</property>
<property>
<name>hadoop.http.logs.enabled</name>
<value>true</value>
</property>
</configuration>

View File

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Autogenerated by Cloudera Manager-->
<configuration>
<property>
<name>dfs.hosts</name>
<value>{{CMF_CONF_DIR}}/dfs_all_hosts.txt</value>
</property>
<property>
<name>dfs.namenode.hosts.provider.classname</name>
<value>org.apache.hadoop.hdfs.server.blockmanagement.CombinedHostFileManager</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///dfs/nn</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address</name>
<value>hadoop-rm1.garr-pa1.d4science.org:8022</value>
</property>
<property>
<name>dfs.namenode.rpc-address</name>
<value>hadoop-rm1.garr-pa1.d4science.org:8020</value>
</property>
<property>
<name>dfs.https.address</name>
<value>hadoop-rm1.garr-pa1.d4science.org:50470</value>
</property>
<property>
<name>dfs.https.port</name>
<value>50470</value>
</property>
<property>
<name>dfs.namenode.http-address</name>
<value>hadoop-rm1.garr-pa1.d4science.org:50070</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>hadoop-rm2.garr-pa1.d4science.org:50090</value>
</property>
<property>
<name>dfs.permissions.superusergroup</name>
<value>supergroup</value>
</property>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.replication.min</name>
<value>1</value>
</property>
<property>
<name>dfs.replication.max</name>
<value>512</value>
</property>
<property>
<name>dfs.namenode.maintenance.replication.min</name>
<value>1</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>134217728</value>
</property>
<property>
<name>dfs.image.transfer.timeout</name>
<value>60000</value>
</property>
<property>
<name>dfs.image.transfer.bandwidthPerSec</name>
<value>0</value>
</property>
<property>
<name>dfs.namenode.plugins</name>
<value></value>
</property>
<property>
<name>dfs.namenode.handler.count</name>
<value>59</value>
</property>
<property>
<name>dfs.namenode.service.handler.count</name>
<value>59</value>
</property>
<property>
<name>dfs.namenode.name.dir.restore</name>
<value>false</value>
</property>
<property>
<name>dfs.thrift.threads.max</name>
<value>20</value>
</property>
<property>
<name>dfs.thrift.threads.min</name>
<value>10</value>
</property>
<property>
<name>dfs.thrift.timeout</name>
<value>60</value>
</property>
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.permissions</name>
<value>true</value>
</property>
<property>
<name>dfs.namenode.safemode.threshold-pct</name>
<value>0.999</value>
</property>
<property>
<name>dfs.namenode.invalidate.work.pct.per.iteration</name>
<value>0.32</value>
</property>
<property>
<name>dfs.namenode.replication.work.multiplier.per.iteration</name>
<value>10</value>
</property>
<property>
<name>dfs.namenode.replication.max-streams</name>
<value>20</value>
</property>
<property>
<name>dfs.namenode.replication.max-streams-hard-limit</name>
<value>40</value>
</property>
<property>
<name>dfs.namenode.avoid.read.stale.datanode</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.snapshot.capture.openfiles</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.avoid.write.stale.datanode</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.stale.datanode.interval</name>
<value>30000</value>
</property>
<property>
<name>dfs.namenode.write.stale.datanode.ratio</name>
<value>0.5</value>
</property>
<property>
<name>dfs.namenode.safemode.min.datanodes</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.safemode.extension</name>
<value>30000</value>
</property>
<property>
<name>dfs.client.use.datanode.hostname</name>
<value>false</value>
</property>
<property>
<name>fs.permissions.umask-mode</name>
<value>022</value>
</property>
<property>
<name>dfs.encrypt.data.transfer</name>
<value>false</value>
</property>
<property>
<name>dfs.encrypt.data.transfer.algorithm</name>
<value>rc4</value>
</property>
<property>
<name>dfs.namenode.acls.enabled</name>
<value>false</value>
</property>
<property>
<name>dfs.access.time.precision</name>
<value>3600000</value>
</property>
<property>
<name>dfs.qjournal.write-txns.timeout.ms</name>
<value>20000</value>
</property>
<property>
<name>dfs.qjournal.start-segment.timeout.ms</name>
<value>20000</value>
</property>
<property>
<name>dfs.qjournal.prepare-recovery.timeout.ms</name>
<value>120000</value>
</property>
<property>
<name>dfs.qjournal.accept-recovery.timeout.ms</name>
<value>120000</value>
</property>
<property>
<name>dfs.qjournal.finalize-segment.timeout.ms</name>
<value>120000</value>
</property>
<property>
<name>dfs.qjournal.select-input-streams.timeout.ms</name>
<value>20000</value>
</property>
<property>
<name>dfs.qjournal.get-journal-state.timeout.ms</name>
<value>120000</value>
</property>
<property>
<name>dfs.qjournal.new-epoch.timeout.ms</name>
<value>120000</value>
</property>
<property>
<name>dfs.datanode.hdfs-blocks-metadata.enabled</name>
<value>true</value>
</property>
</configuration>

View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Autogenerated by Cloudera Manager-->
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://nameservice1</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>1</value>
</property>
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.BZip2Codec,org.apache.hadoop.io.compress.DeflateCodec,org.apache.hadoop.io.compress.SnappyCodec,org.apache.hadoop.io.compress.Lz4Codec</value>
</property>
<property>
<name>hadoop.security.authentication</name>
<value>simple</value>
</property>
<property>
<name>hadoop.security.authorization</name>
<value>false</value>
</property>
<property>
<name>hadoop.rpc.protection</name>
<value>authentication</value>
</property>
<property>
<name>hadoop.security.auth_to_local</name>
<value>DEFAULT</value>
</property>
<property>
<name>hadoop.proxyuser.oozie.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.oozie.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.flume.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.flume.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.HTTP.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.HTTP.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hive.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hive.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hue.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hue.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.httpfs.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.httpfs.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hdfs.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hdfs.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.yarn.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.yarn.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.security.group.mapping</name>
<value>org.apache.hadoop.security.ShellBasedUnixGroupsMapping</value>
</property>
<property>
<name>hadoop.security.instrumentation.requires.admin</name>
<value>false</value>
</property>
<property>
<name>net.topology.script.file.name</name>
<value>/etc/hadoop/conf.cloudera.yarn2/topology.py</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>65536</value>
</property>
<property>
<name>hadoop.ssl.enabled</name>
<value>false</value>
</property>
<property>
<name>hadoop.ssl.require.client.cert</name>
<value>false</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.keystores.factory.class</name>
<value>org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.server.conf</name>
<value>ssl-server.xml</value>
<final>true</final>
</property>
<property>
<name>hadoop.ssl.client.conf</name>
<value>ssl-client.xml</value>
<final>true</final>
</property>
</configuration>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Autogenerated by Cloudera Manager-->
<configuration>
<property>
<name>dfs.nameservices</name>
<value>nameservice1</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.nameservice1</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled.nameservice1</name>
<value>true</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>iis-cdh5-test-m1.ocean.icm.edu.pl:2181,iis-cdh5-test-m2.ocean.icm.edu.pl:2181,iis-cdh5-test-m3.ocean.icm.edu.pl:2181</value>
</property>
<property>
<name>dfs.ha.namenodes.nameservice1</name>
<value>namenode528,namenode434</value>
</property>
<property>
<name>dfs.namenode.rpc-address.nameservice1.namenode528</name>
<value>iis-cdh5-test-m1.ocean.icm.edu.pl:8020</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.nameservice1.namenode528</name>
<value>iis-cdh5-test-m1.ocean.icm.edu.pl:8022</value>
</property>
<property>
<name>dfs.namenode.http-address.nameservice1.namenode528</name>
<value>iis-cdh5-test-m1.ocean.icm.edu.pl:50070</value>
</property>
<property>
<name>dfs.namenode.https-address.nameservice1.namenode528</name>
<value>iis-cdh5-test-m1.ocean.icm.edu.pl:50470</value>
</property>
<property>
<name>dfs.namenode.rpc-address.nameservice1.namenode434</name>
<value>iis-cdh5-test-m2.ocean.icm.edu.pl:8020</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.nameservice1.namenode434</name>
<value>iis-cdh5-test-m2.ocean.icm.edu.pl:8022</value>
</property>
<property>
<name>dfs.namenode.http-address.nameservice1.namenode434</name>
<value>iis-cdh5-test-m2.ocean.icm.edu.pl:50070</value>
</property>
<property>
<name>dfs.namenode.https-address.nameservice1.namenode434</name>
<value>iis-cdh5-test-m2.ocean.icm.edu.pl:50470</value>
</property>
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>134217728</value>
</property>
<property>
<name>dfs.client.use.datanode.hostname</name>
<value>false</value>
</property>
<property>
<name>fs.permissions.umask-mode</name>
<value>022</value>
</property>
<property>
<name>dfs.namenode.acls.enabled</name>
<value>false</value>
</property>
<property>
<name>dfs.client.use.legacy.blockreader</name>
<value>false</value>
</property>
<property>
<name>dfs.client.read.shortcircuit</name>
<value>false</value>
</property>
<property>
<name>dfs.domain.socket.path</name>
<value>/var/run/hdfs-sockets/dn</value>
</property>
<property>
<name>dfs.client.read.shortcircuit.skip.checksum</name>
<value>false</value>
</property>
<property>
<name>dfs.client.domain.socket.data.traffic</name>
<value>false</value>
</property>
<property>
<name>dfs.datanode.hdfs-blocks-metadata.enabled</name>
<value>true</value>
</property>
</configuration>

View File

@ -0,0 +1,8 @@
%spark
val dsName = "__DS_NAME__"
val dsId = "__DS_ID__"
val apiId = "__API_ID__"
val mdId = "__MDSTORE_ID__"
val mdVersion = "__VERSION__"
val path = "__PATH__"

View File

@ -5,7 +5,7 @@
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>apps</artifactId>
<version>3.4.3-SNAPSHOT</version>
<relativePath>../</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -19,6 +19,7 @@
<module>dnet-exporter-api</module>
<module>scholexplorer-api</module>
<module>bioschemas-api</module>
<module>dnet-is-application</module>
</modules>
<dependencies>

View File

@ -5,7 +5,7 @@
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>cmd-line-apps</artifactId>
<version>3.4.3-SNAPSHOT</version>
<relativePath>../</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<groupId>eu.dnetlib.dhp</groupId>
<artifactId>dnet-applications</artifactId>
<version>3.4.3-SNAPSHOT</version>
<relativePath>../</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

View File

@ -0,0 +1,27 @@
# DnetIsApplication
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.1.1.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

View File

@ -0,0 +1,102 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"dnet-is-application": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/dnet-is-application",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.css"
],
"scripts": [
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "dnet-is-application:build:production"
},
"development": {
"browserTarget": "dnet-is-application:build:development",
"proxyConfig": "src/proxy.conf.json"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "dnet-is-application:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.css"
],
"scripts": []
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
{
"name": "dnet-is-application",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.1.0",
"@angular/cdk": "^15.1.1",
"@angular/common": "^15.1.0",
"@angular/compiler": "^15.1.0",
"@angular/core": "^15.1.0",
"@angular/forms": "^15.1.0",
"@angular/material": "^15.1.1",
"@angular/platform-browser": "^15.1.0",
"@angular/platform-browser-dynamic": "^15.1.0",
"@angular/router": "^15.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.1.1",
"@angular/cli": "~15.1.1",
"@angular/compiler-cli": "^15.1.0",
"@types/jasmine": "~4.3.0",
"jasmine-core": "~4.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"typescript": "~4.9.4"
}
}

View File

@ -0,0 +1,39 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { InfoComponent } from './info/info.component';
import { ProtocolsComponent } from './protocols/protocols.component';
import { WfHistoryComponent } from './wf-history/wf-history.component';
import { ResourcesComponent } from './resources/resources.component';
import { VocabulariesComponent, VocabularyEditorComponent } from './vocabularies/vocabularies.component';
import { ContextViewerComponent, ContextsComponent } from './contexts/contexts.component';
import { DsmSearchComponent, DsmResultsComponent, DsmApiComponent } from './dsm/dsm.component';
import { MdstoreInspectorComponent, MdstoresComponent } from './mdstores/mdstores.component';
import { CleanerTesterComponent } from './cleaner-tester/cleaner-tester.component';
import { EmailsComponent } from './emails/emails.component';
import { WfConfsComponent } from './wf-confs/wf-confs.component';
const routes: Routes = [
{ path: "", redirectTo: 'info', pathMatch: 'full' },
{ path: "info", component: InfoComponent },
{ path: "resources/:type", component: ResourcesComponent },
{ path: "adv_resources/context", component: ContextsComponent },
{ path: "adv_resources/vocabulary", component: VocabulariesComponent },
{ path: "adv_resources/protocol", component: ProtocolsComponent },
{ path: "adv_resources/email", component: EmailsComponent },
{ path: "wfs/:section", component: WfConfsComponent },
{ path: "wfs/conf/:conf", component: WfConfsComponent },
{ path: "wf_history", component: WfHistoryComponent },
{ path: "ctx_viewer", component: ContextViewerComponent },
{ path: "voc_editor", component: VocabularyEditorComponent },
{ path: "dsm/search", component: DsmSearchComponent },
{ path: "dsm/results/:page/:size", component: DsmResultsComponent },
{ path: "mdstores", component: MdstoresComponent },
{ path: "mdrecords/:versionId/:limit", component: MdstoreInspectorComponent },
{ path: "cleaner", component: CleanerTesterComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,17 @@
.sidenav-container {
height: 100%;
}
.sidenav {
width: 350px;
}
.sidenav .mat-toolbar {
background: inherit;
}
.mat-toolbar.mat-primary {
position: sticky;
top: 0;
z-index: 1;
}

View File

@ -0,0 +1,23 @@
<div *ngIf="spinnerService.visibility | async" class="spinner">
<mat-progress-spinner mode="indeterminate" [strokeWidth]="8" [diameter]="80" color="primary"></mat-progress-spinner>
</div>
<mat-sidenav-container class="sidenav-container">
<mat-sidenav #drawer class="sidenav mat-elevation-z8" fixedInViewport
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'"
[opened]="(isHandset$ | async) === false">
<app-main-menu-panels></app-main-menu-panels>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()">
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
</button>
<span>{{title}}</span>
</mat-toolbar>
<div style="padding: 20px;">
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@ -0,0 +1,23 @@
import { Component, inject } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { SpinnerService } from './common/spinner.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'D-NET Information Service Application';
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches),
shareReplay()
);
constructor(private breakpointObserver: BreakpointObserver, public spinnerService: SpinnerService) { }
}

View File

@ -0,0 +1,120 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FilterPipe } from './common/filter.pipe';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { InfoComponent } from './info/info.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { LayoutModule } from '@angular/cdk/layout';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatTreeModule } from '@angular/material/tree';
import { MatBadgeModule } from '@angular/material/badge';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'
import { MatSelectModule } from '@angular/material/select'
import { MatTableModule } from '@angular/material/table';
import { ProtocolsComponent } from './protocols/protocols.component';
import { MainMenuPanelsComponent } from './main-menu-panels/main-menu-panels.component';
import { MatExpansionModule } from '@angular/material/expansion';
import { WfHistoryDialog, WfHistoryComponent } from './wf-history/wf-history.component';
import { MatDialogModule } from '@angular/material/dialog';
import { MatSortModule } from '@angular/material/sort';
import { ResourcesComponent, ResContentDialog, ResCreateNewDialog, ResMetadataDialog } from './resources/resources.component'
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { ContextsComponent, ContextViewerComponent, ContextParamsDialog } from './contexts/contexts.component';
import { VocabulariesComponent, VocabularyEditorComponent, VocDialog, VocTermDialog } from './vocabularies/vocabularies.component';
import { DsmSearchComponent, DsmResultsComponent, DsmApiComponent, DsmAddApiDialog, DsmBrowseDialog } from './dsm/dsm.component';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
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 { WfConfsComponent, WfConfDialog, WfConfSingle } from './wf-confs/wf-confs.component';
import { MatTabsModule } from '@angular/material/tabs';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatStepperModule } from '@angular/material/stepper';
@NgModule({
declarations: [
AppComponent,
FilterPipe,
MainMenuPanelsComponent,
InfoComponent,
ProtocolsComponent,
WfHistoryComponent,
WfHistoryDialog,
ResourcesComponent,
ResContentDialog,
ResCreateNewDialog,
ResMetadataDialog,
ContextsComponent,
ContextViewerComponent,
ContextParamsDialog,
VocabulariesComponent,
VocabularyEditorComponent,
VocDialog,
VocTermDialog,
DsmSearchComponent,
DsmResultsComponent,
DsmApiComponent,
DsmAddApiDialog,
DsmBrowseDialog,
MdstoresComponent,
MdstoreInspectorComponent,
MDStoreVersionsDialog,
AddMDStoreDialog,
CleanerTesterComponent,
EmailsComponent,
EmailDialog,
WfConfsComponent,
WfConfDialog,
WfConfSingle
],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
LayoutModule,
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatIconModule,
MatListModule,
MatTreeModule,
MatBadgeModule,
MatCardModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatCheckboxModule,
MatTableModule,
MatExpansionModule,
MatDialogModule,
MatSortModule,
ReactiveFormsModule,
MatSnackBarModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatTabsModule,
MatStepperModule
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: SpinnerHttpInterceptor,
multi: true
}],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,25 @@
<h2>Cleaner Tester</h2>
<form [formGroup]="cleanForm" (ngSubmit)="clean()">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Cleaning rule</mat-label>
<mat-select matInput formControlName="rule">
<mat-option *ngFor="let i of rules" [value]="i.name">{{i.name}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 50%;">
<mat-label>Input Record</mat-label>
<textarea matInput rows="10" formControlName="xmlIn"></textarea>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 50%;">
<mat-label>Output Record</mat-label>
<textarea matInput rows="10" formControlName="xmlOut" readonly></textarea>
</mat-form-field>
<button mat-stroked-button color="primary" type="submit" [disabled]="!cleanForm.valid">Submit</button>
<mat-error *ngIf="cleanForm.errors?.['serverError']">
{{ cleanForm.errors?.['serverError'] }}
</mat-error>
</form>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CleanerTesterComponent } from './cleaner-tester.component';
describe('CleanerTesterComponent', () => {
let component: CleanerTesterComponent;
let fixture: ComponentFixture<CleanerTesterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CleanerTesterComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(CleanerTesterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,32 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { ResourceType, SimpleResource } from '../common/is.model';
import { ISService } from '../common/is.service';
@Component({
selector: 'app-cleaner-tester',
templateUrl: './cleaner-tester.component.html',
styleUrls: ['./cleaner-tester.component.css']
})
export class CleanerTesterComponent {
rules: SimpleResource[] = [];
xmlOutput: string = ''
cleanForm = new FormGroup({
xmlIn: new FormControl('', [Validators.required]),
rule: new FormControl('', [Validators.required]),
xmlOut: new FormControl('')
});
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
this.service.loadSimpleResources("cleaning_rule", (data: SimpleResource[]) => this.rules = data);
}
clean() {
this.service.testCleaning(this.cleanForm.get("rule")?.value!, this.cleanForm.get("xmlIn")?.value!, (data: string) => this.cleanForm.get("xmlOut")?.setValue(data), this.cleanForm);
}
}

View File

@ -0,0 +1,25 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'searchFilter' })
export class FilterPipe implements PipeTransform {
/**
* Pipe filters the list of elements based on the search text provided
*
* @param items list of elements to search in
* @param searchText search string
* @returns list of elements filtered by search text or []
*/
transform(items: any[], searchText: string): any[] {
if (!items) return [];
if (!searchText) return items;
searchText = searchText.trim().toLocaleLowerCase();
return items.filter(obj => {
return Object.keys(obj).reduce((acc, curr) => {
return acc || JSON.stringify(obj[curr]).toLowerCase().includes(searchText);
}, false);
});
}
}

View File

@ -0,0 +1,249 @@
export interface ResourceType {
id: string
name: string
contentType: string
count: number
simple: boolean
}
export interface KeyValue {
k: string;
v: any;
}
export interface BrowseTerm {
term: string,
name: string,
total: number
}
export interface Module {
group: string;
name: string;
versions: string[];
files: string[];
}
export interface ProtocolParam {
name: string
label: string
type: string
optional: boolean
hasSelFunction: boolean
}
export interface Protocol {
id: string
params: ProtocolParam[]
}
export interface WfHistoryEntry {
processId: string,
name: string,
family: string,
status: string,
startDate: string,
endDate: string,
dsId?: string,
dsName?: string,
dsApi?: string,
details: Map<string, string>
}
export interface SimpleResource {
id: string,
name: string,
type: string,
subtype?: string,
description?: string,
creationDate?: string,
modificationDate?: string
}
export interface ContextParam {
name: string,
value: string
}
export interface Context {
id: string,
label: string,
parameters: ContextParam[],
nChilds: number,
type: string
}
export interface ContextNode {
id: string,
label: string,
parameters: ContextParam[],
nChilds: number,
claim: boolean,
parent: string,
populated?: boolean,
childs?: ContextNode[]
}
export interface Vocabulary {
id: string,
name: string,
description?: string
}
export interface VocabularyTermSynonym {
term: string,
encoding: string
}
export interface VocabularyTerm {
code: string,
vocabulary: string,
name: string,
encoding: string,
synonyms: VocabularyTermSynonym[]
}
export interface Api {
id: string,
protocol: string,
compliance: string,
active: boolean,
aggrDate: string,
aggrTotal: number
}
export interface ApiParam {
param: string,
value: string
}
export interface ApiInsert {
id: string,
protocol: string,
datasource: string,
contentdescription: string,
removable: boolean,
compatibility: string,
metadataIdentifierPath: string,
baseurl: string,
apiParams: ApiParam[]
};
export interface Organization {
name: string,
country: string
}
export interface Datasource {
id: string,
name: string,
otherName?: string,
nsprefix: string,
websiteUrl?: string,
type: string,
consenttermsofuse?: boolean,
fulltextdownload?: boolean,
collectedFrom: string,
organizations: Organization[],
apis: Api[]
}
export interface Page<T> {
content: T[],
totalPages: number,
totalElements: number,
size: number,
number: number
}
export interface DsmConf {
compatibilityLevels: string[],
contentDescTypes: string[],
protocols: Protocol[]
}
export interface MDStore {
id: string,
format: string,
layout: string,
type: string,
interpretation: string,
datasourceName: string,
datasourceId: string,
apiId: string,
currentVersion: string,
creationDate: string,
lastUpdate: string,
size: number,
numberOfVersions: number,
params: any
}
export interface MDStoreVersion {
id: string,
mdstore: string,
writing: boolean,
readCount: number,
lastUpdate: string,
size: number,
params: any
}
export interface MDStoreRecord {
id: string,
originalId: string,
encoding: string,
body: string,
dateOfCollection: string,
dateOfTransformation: string,
provenance: any
}
export interface EmailTemplate {
id: string,
description: string,
subject: string,
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,
dsName?: string,
apiId?: string,
enabled: boolean,
configured: boolean,
schedulingEnabled: boolean,
cronExpression?: string,
cronMinInterval?: number,
workflow: string,
destroyWf?: string,
systemParams: Map<string, string>,
userParams: Map<string, string>
}
export interface WfParam {
name: string,
description?: string,
type?: string,
defaultValue?: any,
required: boolean
}
export interface WfSubscription {
// TODO
}
export interface WfProcessStatus {
// TODO
}

View File

@ -0,0 +1,333 @@
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, 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) {
this.httpGet<ResourceType[]>("/ajax/resourceTypes", onSuccess)
}
loadResourceType(id: string, onSuccess: Function) {
this.httpGet<ResourceType>("/ajax/resourceTypes/" + encodeURIComponent(id), onSuccess);
}
loadInfo(onSuccess: Function): void {
this.httpGet<any[]>("/ajax/info/", onSuccess);
}
loadProtocols(onSuccess: Function): void {
this.httpGet<Protocol[]>("/ajax/protocols/", onSuccess);
}
loadSimpleResources(type: string, onSuccess: Function): void {
this.httpGet<SimpleResource[]>("/ajax/resources/" + encodeURIComponent(type), onSuccess);
}
loadSimpleResourceContent(id: any, onSuccess: Function): void {
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
this.httpGetWithOptions<string>("/ajax/resources/" + encodeURIComponent(id) + '/content', {
headers, responseType: 'text' as 'json'
}, onSuccess);
}
saveSimpleResourceMedatata(res: SimpleResource, onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('/ajax/resources/' + encodeURIComponent(res.id) + '/metadata', res, onSuccess, relatedForm);
}
saveSimpleResourceContent(id: string, content: string, onSuccess: Function, relatedForm?: FormGroup): void {
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
let body = new HttpParams().set('content', content);
this.httpPostWithOptions('/ajax/resources/' + encodeURIComponent(id) + '/content', body, { headers: headers }, onSuccess, relatedForm);
}
addSimpleResource(name: string, type: string, subtype: string, description: string, content: string, onSuccess: Function, relatedForm?: FormGroup): void {
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
let body = new HttpParams()
.set('name', name)
.set('type', type)
.set('subtype', subtype)
.set('description', description)
.set('content', content);
this.httpPostWithOptions('/ajax/resources/', body, { headers: headers }, onSuccess, relatedForm);
}
deleteSimpleResource(resourceId: string, onSuccess: Function): void {
this.httpDelete('/ajax/resources/' + encodeURIComponent(resourceId), onSuccess);
}
loadWfHistory(total: number, from: number, to: number, onSuccess: Function): void {
let params = new HttpParams();
if (total && total > 0) { params = params.append('total', total); }
if (from && from > 0) { params = params.append('from', from); }
if (to && to > 0) { params = params.append('to', to); }
this.httpGetWithOptions<WfHistoryEntry[]>('/ajax/wf_history/', { params: params }, onSuccess);
}
loadWfHistoryForConf(wfConfId: string, onSuccess: Function): void {
this.httpGet<WfHistoryEntry[]>('/ajax/wf_history/byConf/' + encodeURIComponent(wfConfId), onSuccess);
}
loadContexts(onSuccess: Function): void {
this.httpGet<Context[]>('./ajax/contexts/', onSuccess);
}
loadContext(ctxId: string, onSuccess: Function): void {
this.httpGet<Context>('./ajax/contexts/' + encodeURIComponent(ctxId), onSuccess);
}
loadContextCategories(ctxId: string, onSuccess: Function): void {
this.httpGet<ContextNode[]>('./ajax/contexts/' + encodeURIComponent(ctxId) + '/categories', onSuccess);
}
loadContextConcepts(level: number, nodeId: string, onSuccess: Function): void {
this.httpGet<ContextNode[]>('./ajax/contexts/' + encodeURIComponent(level) + '/' + encodeURIComponent(nodeId) + '/concepts', onSuccess);
}
loadVocabularies(onSuccess: Function): void {
this.httpGet<Vocabulary[]>('./ajax/vocs/', onSuccess);
}
loadVocabulary(vocId: string, onSuccess: Function): void {
this.httpGet<Vocabulary>('./ajax/vocs/' + encodeURIComponent(vocId), onSuccess);
}
loadVocabularyTerms(vocId: string, onSuccess: Function): void {
this.httpGet<VocabularyTerm[]>('./ajax/vocs/' + encodeURIComponent(vocId) + '/terms', onSuccess);
}
saveVocabulary(voc: Vocabulary, onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('./ajax/vocs/', voc, onSuccess, relatedForm);
}
saveVocabularyTerm(vocId: string, term: VocabularyTerm, onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('./ajax/vocs/' + encodeURIComponent(vocId) + '/terms', term, onSuccess, relatedForm);
}
deleteVocabulary(vocId: string, onSuccess: Function): void {
this.httpDelete('./ajax/vocs/' + encodeURIComponent(vocId), onSuccess);
}
deleteVocabularyTerm(vocId: string, termCode: string, onSuccess: Function): void {
this.httpDelete('./ajax/vocs/'
+ encodeURIComponent(vocId)
+ '/terms/'
+ encodeURIComponent(termCode)
, onSuccess);
}
dsmConf(onSuccess: Function) {
this.httpGet<DsmConf>('./ajax/dsm/conf', onSuccess);
}
dsmBrowsableFields(onSuccess: Function) {
this.httpGet<KeyValue[]>('./ajax/dsm/browsableFields', onSuccess);
}
dsmBrowse(field: string, onSuccess: Function) {
this.httpGet<BrowseTerm[]>('./ajax/dsm/browse/' + encodeURIComponent(field), onSuccess);
}
dsmSearchByField(field: string, value: string, page: number, pageSize: number, onSuccess: Function) {
this.httpGet<Page<Datasource>>('./ajax/dsm/searchByField/' + encodeURIComponent(field) + '/' + page + '/' + pageSize + '?value=' + encodeURIComponent(value), onSuccess);
}
dsmSearch(value: string, page: number, pageSize: number, onSuccess: Function) {
this.httpGet<Page<Datasource>>('./ajax/dsm/search/' + page + '/' + pageSize + '?value=' + encodeURIComponent(value), onSuccess);
}
loadMDStores(onSuccess: Function): void {
this.httpGet<MDStore[]>("/ajax/mdstores/", onSuccess);
}
loadMDStore(mdId: string, onSuccess: Function): void {
this.httpGet<MDStore>('./ajax/mdstores/mdstore/' + encodeURIComponent(mdId), onSuccess);
}
addMDStore(format: string, layout: string, interpretation: string, type: string, dsName: string, dsId: string, apiId: string, onSuccess: Function, relatedForm?: FormGroup) {
const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')
let body = new HttpParams()
.set('dsName', dsName)
.set('dsId', dsId)
.set('apiId', apiId);
this.httpPostWithOptions('/ajax/mdstores/new/'
+ encodeURIComponent(format)
+ '/'
+ encodeURIComponent(layout)
+ '/'
+ encodeURIComponent(interpretation)
+ '/'
+ encodeURIComponent(type),
body, { headers: headers }, onSuccess, relatedForm);
}
deleteMDStore(mdId: string, onSuccess: Function): void {
this.httpDelete('./ajax/mdstores/mdstore/' + encodeURIComponent(mdId), onSuccess);
}
prepareNewMDStoreVersion(mdId: string, onSuccess: Function): void {
this.httpGet<MDStoreVersion>('./ajax/mdstores/mdstore/' + encodeURIComponent(mdId) + '/newVersion', onSuccess);
}
commitMDStoreVersion(versionId: string, size: number, onSuccess: Function) {
this.httpGet<any>('./ajax/mdstores/version/' + encodeURIComponent(versionId) + '/commit/' + size, onSuccess);
}
abortMDStoreVersion(versionId: string, onSuccess: Function) {
this.httpGet<any>('./ajax/mdstores/version/' + encodeURIComponent(versionId) + '/abort', onSuccess);
}
deleteMDStoreVersion(versionId: string, onSuccess: Function) {
this.httpDelete('./ajax/mdstores/version/' + encodeURIComponent(versionId), onSuccess);
}
resetReadingMDStoreVersion(versionId: string, onSuccess: Function) {
this.httpGet<any>('./ajax/mdstores/version/' + encodeURIComponent(versionId) + '/resetReading', onSuccess);
}
loadMDStoreVersions(mdId: string, onSuccess: Function): void {
this.httpGet<MDStoreVersion[]>('./ajax/mdstores/mdstore/' + encodeURIComponent(mdId) + '/versions', onSuccess);
}
loadMDStoreVersion(versionId: string, onSuccess: Function): void {
this.httpGet<MDStoreVersion>('./ajax/mdstores/version/' + encodeURIComponent(versionId), onSuccess);
}
loadMDStoreVersionRecords(versionId: string, limit: number, onSuccess: Function): void {
this.httpGet<MDStoreRecord[]>('./ajax/mdstores/version/' + encodeURIComponent(versionId) + '/content/' + limit, onSuccess);
}
testCleaning(rule: string, xml: string, onSuccess: Function, relatedForm?: FormGroup): void {
var headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
this.httpPostWithOptions('./ajax/mapping/clean?rule=' + encodeURIComponent(rule), xml, { headers, responseType: 'text' as 'json' }, onSuccess, relatedForm);
}
loadEmailTemplates(onSuccess: Function): void {
this.httpGet<EmailTemplate[]>('./ajax/templates/email/', onSuccess);
}
saveEmailTemplate(email: EmailTemplate, onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('./ajax/templates/email/', email, onSuccess, relatedForm);
}
deleteEmailTemplate(id: string, onSuccess: Function): void {
this.httpDelete('./ajax/templates/email/' + encodeURIComponent(id), onSuccess);
}
loadWfSections(onSuccess: Function) {
this.httpGet<WfSection[]>("/ajax/wfs/sections", onSuccess)
}
loadWfConfigurations(sectionId: string, onSuccess: Function): void {
this.httpGet<KeyValue[]>('./ajax/wfs/sections/' + encodeURIComponent(sectionId), onSuccess);
}
loadWfConfiguration(id: string, onSuccess: Function): void {
this.httpGet<WfConf>('./ajax/wfs/conf/' + encodeURIComponent(id), onSuccess);
}
saveWfConfiguration(conf: WfConf, onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('./ajax/wfs/conf', conf, onSuccess, relatedForm);
}
deleteWfConfiguration(id: string, onSuccess: Function): void {
this.httpDelete('./ajax/wfs/conf/' + encodeURIComponent(id), onSuccess);
}
startWfConfiguration(id: string, onSuccess: Function): void {
this.httpGet<WfProcessStatus>('./ajax/wfs/conf/' + encodeURIComponent(id) + '/start', onSuccess);
}
startDestroyWfConfiguration(id: string, onSuccess: Function): void {
this.httpGet<WfProcessStatus>('./ajax/wfs/conf/' + encodeURIComponent(id) + '/destroy', onSuccess);
}
findProcess(id: string, onSuccess: Function): void {
this.httpGet<WfProcessStatus>('./ajax/wfs/process/' + encodeURIComponent(id), onSuccess);
}
killProcess(id: string, onSuccess: Function): void {
this.httpDelete('./ajax/wfs/process/' + encodeURIComponent(id), onSuccess);
}
findWfSubscriptions(id: string, onSuccess: Function): void {
this.httpGet<WfSubscription[]>('./ajax/wfs/conf/' + encodeURIComponent(id) + '/subscriptions', onSuccess);
}
saveWfSubscriptions(id: string, subscriptions: WfSubscription[], onSuccess: Function, relatedForm?: FormGroup): void {
this.httpPost('./ajax/wfs/conf/' + encodeURIComponent(id) + '/subscriptions', subscriptions, onSuccess, relatedForm);
}
private httpGet<T>(url: string, onSuccess: Function) {
this.client.get<T>(url).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
private httpGetWithOptions<T>(url: string, options: any, onSuccess: Function) {
this.client.get<T>(url, options).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
private httpDelete(url: string, onSuccess: Function) {
this.client.delete<void>(url).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
private httpPost(url: string, body: any, onSuccess: Function, relatedForm?: FormGroup) {
this.client.post<void>(url, body).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error, relatedForm)
});
}
private httpPostWithOptions(url: string, body: any, options: any, onSuccess: Function, relatedForm?: FormGroup) {
this.client.post<void>(url, body, options).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error, relatedForm)
});
}
private showError(error: any, form?: FormGroup) {
console.log(error);
const msg = this.errorMessage(error);
if (form) {
form.setErrors({ serverError: msg })
} else if (this.snackBar) {
this.snackBar.open(msg, 'ERROR', {
duration: 5000,
});
} else {
alert(msg);
}
}
private errorMessage(error: any) {
if (error.error && error.error.message) {
return error.error.message;
} else if (error.message) {
return error.message;
} else {
return 'Generic server side error';
}
}
}

View File

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Observable } from 'rxjs';
import { HttpInterceptor, HttpResponse } from '@angular/common/http';
import { HttpRequest } from '@angular/common/http';
import { HttpHandler } from '@angular/common/http';
import { HttpEvent } from '@angular/common/http';
import { tap } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class SpinnerService {
visibility: BehaviorSubject<boolean>;
constructor() { this.visibility = new BehaviorSubject(false); }
show() { this.visibility.next(true); }
hide() { this.visibility.next(false); }
}
@Injectable()
export class SpinnerHttpInterceptor implements HttpInterceptor {
constructor(private spinnerService: SpinnerService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.spinnerService.show();
return next.handle(req)
.pipe(tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
this.spinnerService.hide();
}
}, (error) => {
this.spinnerService.hide();
}));
}
}

View File

@ -0,0 +1,16 @@
<h1 mat-dialog-title>Parameters</h1>
<div mat-dialog-content>
<p *ngIf="!data || data.length == 0">No parameters</p>
<div *ngFor="let p of data">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; font-size: 0.8em;">
<mat-label>{{p.name}}</mat-label>
<input matInput required readonly value="{{p.value}}" />
</mat-form-field>
</div>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
</div>

View File

@ -0,0 +1,57 @@
<h2>Context Viewer</h2>
<p>
<b>Context ID: </b>{{context?.id}}<br />
<b>Label: </b>{{context?.label}}<br />
<b>Type: </b>{{context?.type}}<br />
</p>
<p>
<a mat-stroked-button color="primary" [routerLink]="['/adv_resources/context']">
<mat-icon fontIcon="home"></mat-icon>
Return to contexts list
</a>
<button mat-stroked-button color="primary" (click)="showParamsDialog(context?.parameters)">
<mat-icon fontIcon="info"></mat-icon>
Global parameters
</button>
<a mat-stroked-button color="link" href="/api/contexts/{{context?.id}}" target="_blank">
<mat-icon fontIcon="download"></mat-icon>
Download
</a>
</p>
<ul>
<li *ngFor="let cat of categories" class="context-node">
<a (click)="populateNode(0, cat)" *ngIf="!(cat.populated || cat.nChilds == 0)"
style="margin-right: 0.5em;">[+{{cat.nChilds}}]</a>
<a title="{{cat.label}}" (click)="showParamsDialog(cat.parameters)" style="margin-right: 0.5em;">{{cat.id}}</a>
<span class="badge-label badge-info" *ngIf="cat.claim">claim</span>
<ul *ngIf="cat.childs && cat.childs.length > 0">
<li *ngFor="let c0 of cat.childs" class="context-node">
<a (click)="populateNode(1, c0)" *ngIf="!(c0.populated || c0.nChilds == 0)"
style="margin-right: 0.5em;">[+{{c0.nChilds}}]</a>
<a title="{{c0.label}" (click)="showParamsDialog(c0.parameters)" style="margin-right: 0.5em;">{{c0.id}}</a>
<span class="badge-label badge-info" *ngIf="c0.claim">claim</span>
<ul *ngIf="c0.childs && c0.childs.length > 0">
<li *ngFor="let c1 of c0.childs" class="context-node">
<a (click)="populateNode(2, c1)" *ngIf="!(c1.populated || c1.nChilds == 0)"
style="margin-right: 0.5em;">[+{{c1.nChilds}}]</a>
<a title="{{c1.label}}" (click)="showParamsDialog(c1.parameters)" style="margin-right: 0.5em;">{{c1.id}}</a>
<span class="badge-label badge-info" *ngIf="c1.claim">claim</span>
<ul *ngIf="c1.childs && c1.childs.length > 0">
<li *ngFor="let c2 of c1.childs" class="context-node">
<a title="{{c2.label}}" (click)="showParamsDialog(c2.parameters)"
style="margin-right: 0.5em;">{{c2.id}}</a>
<span class="badge-label badge-info" *ngIf="c2.claim">claim</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

View File

@ -0,0 +1,11 @@
.context-node {
padding-top: 1em;
}
.context-node a {
text-decoration: none;
}
.context-node a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,44 @@
<h2>Contexts</h2>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; margin-top: 10px;">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter..." #input />
</mat-form-field>
<table mat-table [dataSource]="contextsDatasource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef style="width: 20%;" mat-sort-header
sortActionDescription="Sort by Context ID"> Id </th>
<td mat-cell *matCellDef="let element">
<a [routerLink]="['/ctx_viewer']" [queryParams]="{id: element.id}">{{element.id}}</a>
</td>
</ng-container>
<ng-container matColumnDef="label">
<th mat-header-cell *matHeaderCellDef style="width: 40%;" mat-sort-header
sortActionDescription="Sort by Context Label"> Label </th>
<td mat-cell *matCellDef="let element"> {{element.label}} </td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef style="width: 10%;" mat-sort-header
sortActionDescription="Sort by Context Type"> Type </th>
<td mat-cell *matCellDef="let element"> {{element.type}} </td>
</ng-container>
<ng-container matColumnDef="buttons">
<th mat-header-cell *matHeaderCellDef style="text-align: right;">Parameters</th>
<td mat-cell *matCellDef="let element" class="table-buttons">
<button mat-stroked-button color="primary" (click)="showParamsDialog(element)">show</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="colums"></tr>
<tr mat-row *matRowDef="let row; columns: colums;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>

View File

@ -0,0 +1,101 @@
import { Component, Inject, AfterViewInit, OnInit, ViewChild } from '@angular/core';
import { ISService } from '../common/is.service';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { Context, ContextParam, ContextNode } from '../common/is.model';
import { ActivatedRoute, Params } from '@angular/router';
import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'app-contexts',
templateUrl: './contexts.component.html',
styleUrls: ['./contexts.component.css']
})
export class ContextsComponent implements AfterViewInit, OnInit {
contextsDatasource: MatTableDataSource<Context> = new MatTableDataSource<Context>([]);
colums: string[] = ['id', 'label', 'type', 'buttons'];
@ViewChild(MatSort) sort: MatSort | undefined
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
}
ngOnInit() {
this.reload();
}
ngAfterViewInit() {
if (this.sort) this.contextsDatasource.sort = this.sort;
}
reload() {
this.service.loadContexts((data: Context[]) => this.contextsDatasource.data = data);
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
this.contextsDatasource.filter = filterValue;
}
showParamsDialog(context: Context): void {
const dialogRef = this.dialog.open(ContextParamsDialog, {
data: context.parameters,
width: '80%'
});
}
}
@Component({
selector: 'context-dialog',
templateUrl: 'context-params-dialog.html',
styleUrls: ['./contexts.component.css']
})
export class ContextParamsDialog {
constructor(public dialogRef: MatDialogRef<ContextParamsDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
}
onNoClick(): void {
this.dialogRef.close();
}
}
@Component({
selector: 'app-context-editor',
templateUrl: './context-viewer.component.html',
styleUrls: ['./contexts.component.css']
})
export class ContextViewerComponent implements OnInit {
context?: Context
categories: ContextNode[] = [];
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
}
ngOnInit() {
this.route.queryParams.subscribe((params) => {
this.service.loadContext(params['id'], (data: Context) => this.context = data);
this.service.loadContextCategories(params['id'], (data: ContextNode[]) => this.categories = data);
});
}
populateNode(level: number, node: ContextNode): void {
this.service.loadContextConcepts(level, node.id, (data: ContextNode[]) => {
node.populated = true;
node.childs = data
});
}
showParamsDialog(params: ContextParam[] | undefined): void {
if (params) {
this.dialog.open(ContextParamsDialog, {
data: params,
width: '80%'
});
}
}
}

View File

@ -0,0 +1,142 @@
<form [formGroup]="addApiForm" (ngSubmit)="onSubmit()">
<h1 mat-dialog-title>Add a new API</h1>
<div mat-dialog-content>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Protocol</mat-label>
<mat-select matInput formControlName="protocol" (selectionChange)="onChangeProtocol($event)">
<mat-option *ngFor="let i of protocols" [value]="i">{{i}}</mat-option>
</mat-select>
<mat-error *ngIf="addApiForm.get('protocol')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
<mat-card style="margin-top: 10px;" *ngIf="selectedProtocol">
<mat-card-header>
<mat-card-title>API Description</mat-card-title>
</mat-card-header>
<mat-card-content style="padding-top: 1em;">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Datasource (ID: {{data.dsId}})</mat-label>
<input matInput readonly value="{{data.dsName}}" />
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 30%;">
<mat-label>Api ID (prefix)</mat-label>
<input matInput readonly value="{{apiPrefix}}" />
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 70%;">
<mat-label>Api ID (suffix)</mat-label>
<input matInput formControlName="apiIdSuffix" placeholder="example: {{selectedProtocol}}_01" />
<mat-error *ngIf="addApiForm.get('apiIdSuffix')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 50%;">
<mat-label>Compatibility level</mat-label>
<mat-select matInput formControlName="compatibility">
<mat-option *ngFor="let i of compatibilityLevels" [value]="i">{{i}}</mat-option>
</mat-select>
<mat-error *ngIf="addApiForm.get('compatibility')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 50%;">
<mat-label>Content description</mat-label>
<mat-select matInput formControlName="contentdescription">
<mat-option *ngFor="let i of contentDescTypes" [value]="i">{{i}}</mat-option>
</mat-select>
<mat-error *ngIf="addApiForm.get('contentdescription')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
</mat-card-content>
</mat-card>
<mat-card style="margin-top: 10px;" *ngIf="selectedProtocol">
<mat-card-header>
<mat-card-title>Configuration Parameters</mat-card-title>
</mat-card-header>
<mat-card-content style="padding-top: 1em;">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Base URL</mat-label>
<input matInput formControlName="baseurl" />
<mat-error *ngIf="addApiForm.get('baseurl')?.invalid">
Invalid URL
<span *ngIf="addApiForm.get('baseurl')?.hasError('required')">: This field is
<strong>required</strong></span>
</mat-error>
</mat-form-field>
<ng-container *ngFor="let p of selProtParams">
<ng-container *ngIf="!p.hasSelFunction">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;" *ngIf="p.type != 'BOOLEAN'">
<mat-label>
<b>{{selectedProtocol}}</b> - {{p.label}}
<b *ngIf="p.type == 'LIST'">(Comma separeted values)</b>
<b *ngIf="p.type == 'INT'">(Numeric)</b>
</mat-label>
<input matInput [formControlName]="paramPrefix + p.name" />
<mat-error *ngIf="addApiForm.get(paramPrefix + p.name)?.invalid">Invalid value</mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;" *ngIf="p.type == 'BOOLEAN'">
<mat-label><b>{{selectedProtocol}}</b> - {{p.label}} <b>(true/false)</b></mat-label>
<mat-select matInput [formControlName]="paramPrefix + p.name">
<mat-option></mat-option>
<mat-option value="true">true</mat-option>
<mat-option value="false">false</mat-option>
</mat-select>
<mat-error *ngIf="addApiForm.get(paramPrefix + p.name)?.invalid">Invalid value</mat-error>
</mat-form-field>
</ng-container>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;" *ngIf="p.hasSelFunction">
<mat-label><b>{{selectedProtocol}}</b> - {{p.label}} <b *ngIf="p.type == 'LIST'">(multiple
choice)</b></mat-label>
<mat-select matInput [formControlName]="paramPrefix + p.name" [multiple]="p.type == 'LIST'">
<mat-option *ngIf="p.type != 'LIST'"></mat-option>
<mat-option value="0">0 - TODO</mat-option>
<mat-option value="1">1 - TODO</mat-option>
<mat-option value="2">2 - TODO</mat-option>
<mat-option value="3">3 - TODO</mat-option>
</mat-select>
<mat-error *ngIf="addApiForm.get(paramPrefix + p.name)?.invalid">Invalid value</mat-error>
</mat-form-field>
</ng-container>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>XPath for Metatadata Identifier</mat-label>
<input matInput formControlName="metadataIdentifierPath" />
<mat-error *ngIf="addApiForm.get('metadataIdentifierPath')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
</mat-card-content>
</mat-card>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" type="submit" [disabled]="!addApiForm.valid">Submit</button>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
<mat-error *ngIf="addApiForm.errors?.['serverError']">
{{ addApiForm.errors?.['serverError'] }}
</mat-error>
</div>
</form>
<!--
<form-textfield-static label="Datasource" value="dsName"></form-textfield-static>
<form-textfield-with-prefix label="API Id" prefix="{{prefix}}" value="api.id"></form-textfield-with-prefix>
<form-select label="Compatibility level" terms="compatibilityLevels" value="api.compliance"></form-select>
<form-select label="Content description" terms="contentDescTypes" value="api.contentdescription"></form-select>
<form-select label="Protocol" terms="protocols" value="api.protocol"></form-select>
<form-textfield label="BaseURL" value="api.baseUrl" regex="^(http|https|ftp|file|sftp|jar|mongodb):\/\/"></form-textfield>
<div ng-show="api.protocol" ng-repeat="p in selProtParams">
<form-textfield label="Parameter: {{p.label}}" value="api.apiParams[p.name]" type="{{p.type}}" optional="{{p.optional}}"></form-textfield>
</div>
{{api}}
<br />
<button type="submit" class="btn btn-sm btn-primary">Add API</button>
-->

View File

@ -0,0 +1 @@
<p>dsm api page</p>

View File

@ -0,0 +1,39 @@
<h1 mat-dialog-title>{{data.label}}</h1>
<div mat-dialog-content>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; margin-top: 10px;">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter..." #input />
</mat-form-field>
<table mat-table [dataSource]="datasource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef style="width: 70%;" mat-sort-header sortActionDescription="Sort by Term">
Name</th>
<td mat-cell *matCellDef="let element">
<a [routerLink]="['/dsm/results/0/50']" [queryParams]="{field: data.field, value: element.term}"
mat-dialog-close>{{element.name}}</a>
</td>
</ng-container>
<ng-container matColumnDef="total">
<th mat-header-cell *matHeaderCellDef style="width: 30%;" mat-sort-header sortActionDescription="Sort by Total"> #
datasources </th>
<td mat-cell *matCellDef="let element"> {{element.total}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="colums"></tr>
<tr mat-row *matRowDef="let row; columns: colums;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="2" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
</div>

View File

@ -0,0 +1,99 @@
<h2>Datasource Manager: Results</h2>
<p>
<span *ngIf="field">Searching for <b>{{field}}</b> = <i>"{{value}}"</i></span>
<span *ngIf="!field && value">Searching for <i>"{{value}}"</i></span>
<span *ngIf="!field && !value">Returning all the datasources</span>
</p>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; margin-top: 10px;">
<mat-label><b>Filter in current page</b> (Total: {{(results | searchFilter: filterText).length}})</mat-label>
<input matInput [(ngModel)]="filterText" placeholder="Filter..." autofocus />
</mat-form-field>
<mat-paginator (page)="changePage($event)" [pageIndex]="currPage" [pageSize]="pageSize" [length]="nResults"
[pageSize]="pageSize" [pageSizeOptions]="[10, 25, 50, 100]" aria-label="Select page">
</mat-paginator>
<mat-card *ngFor="let r of results | searchFilter: filterText" style="margin-top: 10px;">
<mat-card-header>
<mat-card-title>{{r.name}}</mat-card-title>
<mat-card-subtitle *ngIf="r.otherName && r.name != r.otherName">{{r.otherName}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<table class="dsm-result-table">
<tr>
<th>Id</th>
<td>{{r.id}}</td>
</tr>
<tr>
<th>Type</th>
<td>{{r.type}}</td>
</tr>
<tr>
<th>Namespace Prefix</th>
<td>{{r.nsprefix}}</td>
</tr>
<tr>
<th>Collected From</th>
<td>{{r.collectedFrom}}</td>
</tr>
<tr *ngIf="r.websiteUrl">
<th>URL</th>
<td><a href="{{r.websiteUrl}}" target="_blank">{{r.websiteUrl}}</a></td>
</tr>
<tr *ngIf="r.organizations && r.organizations.length > 0">
<th>Organization(s)</th>
<td>
<span *ngFor="let o of r.organizations">
{{o.name}}
<img src="assets/images/flags/{{o.country}}.gif" title="{{o.country}}" alt="{{o.country}}"
*ngIf="o.country" />
<br />
</span>
</td>
</tr>
<tr>
<th>APIs</th>
<td>
<ng-container *ngFor="let a of r.apis">
<p *ngIf="a.id" style="border-bottom: 1px solid lightgrey;">
<a [routerLink]="['/dsm/api']" [queryParams]="{id: a.id}" style="margin-right: 0.5em;">{{a.id}}</a>
<span class="badge-label badge-default" title="protocol">{{a.protocol}}</span>
<span class="badge-label badge-info" title="compliance">{{a.compliance}}</span>
<span class="badge-label badge-success" *ngIf="a.active">active</span>
<span class="badge-label badge-failure" *ngIf="!a.active">not active</span>
<span *ngIf="a.aggrDate">
<br />
<b>Last aggregation:</b>
{{a.aggrDate}}
<b>(total: {{a.aggrTotal}})</b>
</span>
</p>
</ng-container>
<button mat-raised-button color="primary" (click)="openAddApiDialog(r.id, r.name)">add api</button>
</td>
</tr>
<tr *ngIf="r.consenttermsofuse">
<th>Consent Terms of Use</th>
<td>YES</td>
</tr>
<tr *ngIf="r.fulltextdownload">
<th>Fulltext Download</th>
<td>YES</td>
</tr>
</table>
</mat-card-content>
</mat-card>
<mat-paginator (page)="changePage($event)" [pageIndex]="currPage" [pageSize]="pageSize" [length]="nResults"
[pageSize]="pageSize" [pageSizeOptions]="[10, 25, 50, 100]" aria-label="Select page">
</mat-paginator>

View File

@ -0,0 +1,17 @@
<h2>Datasource Manager: Search</h2>
<form (ngSubmit)="search()">
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Search...</mat-label>
<input matInput [(ngModel)]="searchText" name="searchText" autofocus />
</mat-form-field>
</form>
<div>
<h4>Or browse using:</h4>
<ul>
<li *ngFor="let f of browsableFields">
<a (click)="browseField(f.k, f.v)">{{f.v}}</a>
</li>
</ul>
</div>

View File

@ -0,0 +1,21 @@
.dsm-result-table {
margin-top: 1em;
border-collapse: collapse;
}
.dsm-result-table tr:not(:last-child) {
border-bottom: 1pt solid lightgrey;
}
.dsm-result-table th,
.dsm-result-table td {
text-align: left;
font-size: 0.9em;
vertical-align: top;
}
.dsm-result-table td button {
font-size: 0.8em !important;
padding: 0 !important;
height: 2.5em !important;
}

View File

@ -0,0 +1,251 @@
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { Page, BrowseTerm, Datasource, KeyValue, DsmConf, ProtocolParam, Api, ApiInsert } from '../common/is.model';
import { ISService } from '../common/is.service';
import { ActivatedRoute, Params } from '@angular/router';
import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { combineLatest } from 'rxjs';
import { Router } from '@angular/router';
import { PageEvent } from '@angular/material/paginator';
import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { MatSelectChange } from '@angular/material/select';
@Component({
selector: 'app-dsm-search',
templateUrl: './dsm-search.component.html',
styleUrls: ['./dsm.component.css']
})
export class DsmSearchComponent implements OnInit {
searchText: string = '';
browsableFields: KeyValue[] = [];
constructor(public service: ISService, public route: ActivatedRoute, public router: Router, public dialog: MatDialog) {
}
ngOnInit() {
this.service.dsmBrowsableFields((data: KeyValue[]) => this.browsableFields = data);
}
search() {
this.router.navigate(['/dsm/results/0/50'], {
queryParams: { value: this.searchText }
});
}
browseField(field: string, label: string) {
const dialogRef = this.dialog.open(DsmBrowseDialog, {
data: { field: field, label: label },
width: '80%'
});
}
}
@Component({
selector: 'app-dsm-results',
templateUrl: './dsm-results.component.html',
styleUrls: ['./dsm.component.css']
})
export class DsmResultsComponent implements OnInit {
filterText: string = '';
results: Datasource[] = [];
nResults: number = 0;
currPage: number = 0;
nPages: number = 0;
pageSize: number = 0;
field?: string;
value: string = '';
constructor(public service: ISService, public route: ActivatedRoute, public router: Router, public dialog: MatDialog) {
}
ngOnInit(): void {
combineLatest([this.route.params, this.route.queryParams],
(params: Params, queryParams: Params) => ({ params, queryParams })
).subscribe((res: { params: Params; queryParams: Params }) => {
const { params, queryParams } = res;
this.currPage = parseInt(params['page']);
this.pageSize = parseInt(params['size']);
this.field = queryParams['field'];
this.value = queryParams['value'];
this.reload();
});
}
reload() {
if (this.field) {
this.service.dsmSearchByField(this.field, this.value, this.currPage, this.pageSize, (page: Page<Datasource>) => {
this.results = page.content;
this.nResults = page.totalElements;
this.nPages = page.totalPages;
});
} else {
this.service.dsmSearch(this.value, this.currPage, this.pageSize, (page: Page<Datasource>) => {
this.results = page.content;
this.nResults = page.totalElements;
this.nPages = page.totalPages;
});
}
}
changePage(event: PageEvent) {
let path = '/dsm/results/' + event.pageIndex + '/' + event.pageSize;
let qp = this.field ?
{ field: this.field, value: this.value } :
{ value: this.value };
this.router.navigate([path], {
queryParams: qp
});
}
openAddApiDialog(dsId: string, dsName: string) {
const dialogRef = this.dialog.open(DsmAddApiDialog, {
data: { dsId: dsId, dsName: dsName },
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
}
@Component({
selector: 'app-dsm-api',
templateUrl: './dsm-api.component.html',
styleUrls: ['./dsm.component.css']
})
export class DsmApiComponent {
}
@Component({
selector: 'dsm-browse-dialog',
templateUrl: 'dsm-browse-dialog.html',
styleUrls: ['./dsm.component.css']
})
export class DsmBrowseDialog implements OnInit {
datasource: MatTableDataSource<BrowseTerm> = new MatTableDataSource<BrowseTerm>([]);
colums: string[] = ['name', 'total'];
@ViewChild(MatSort) sort: MatSort | undefined
constructor(public dialogRef: MatDialogRef<DsmBrowseDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
}
ngOnInit(): void {
this.service.dsmBrowse(this.data.field, (res: BrowseTerm[]) => this.datasource.data = res);
}
ngAfterViewInit() {
if (this.sort) this.datasource.sort = this.sort;
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
this.datasource.filter = filterValue;
}
onNoClick(): void {
this.dialogRef.close();
}
}
@Component({
selector: 'dsm-add-api-dialog',
templateUrl: './dsm-add-api.dialog.html',
styleUrls: ['./dsm.component.css']
})
export class DsmAddApiDialog {
selectedProtocol: string = '';
apiPrefix: string = '';
paramPrefix: string = '__PARAM_';
protocols: string[] = [];
compatibilityLevels: string[] = [];
contentDescTypes: string[] = [];
protocolsMap: any = {};
selProtParams: ProtocolParam[] = [];
addApiForm: FormGroup = new FormGroup({
apiIdSuffix: new FormControl('', [Validators.required]),
compatibility: new FormControl('', [Validators.required]),
contentdescription: new FormControl('', [Validators.required]),
protocol: new FormControl('', [Validators.required]),
baseurl: new FormControl('', [Validators.required, Validators.pattern('^(http|https|ftp|file|sftp|jar|mongodb):\/\/')]),
metadataIdentifierPath: new FormControl('', [Validators.required])
});
constructor(public dialogRef: MatDialogRef<DsmAddApiDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
this.apiPrefix = 'api_________::' + data.dsId + '::';
this.service.dsmConf((conf: DsmConf) => {
this.compatibilityLevels = conf.compatibilityLevels;
this.contentDescTypes = conf.contentDescTypes;
conf.protocols.forEach((p) => {
this.protocols.push(p.id);
this.protocolsMap[p.id] = p.params;
});
});
}
onChangeProtocol(e: MatSelectChange): void {
this.selectedProtocol = e.value;
Object.keys(this.addApiForm.controls).forEach(k => {
if (k.startsWith(this.paramPrefix)) {
this.addApiForm.removeControl(k);
}
});
if (this.protocolsMap[e.value]) {
this.selProtParams = this.protocolsMap[e.value];
this.selProtParams.forEach(p => {
let validations: ValidatorFn[] = [];
if (!p.optional) { validations.push(Validators.required); }
if (p.type == 'INT') { validations.push(Validators.pattern('^[0-9]*$')); }
this.addApiForm.addControl(this.paramPrefix + p.name, new FormControl('', validations));
});
} else {
this.selProtParams = [];
}
}
onSubmit(): void {
let api: ApiInsert = {
id: this.apiPrefix + this.addApiForm.get('apiIdSuffix')?.value,
protocol: this.selectedProtocol,
datasource: this.data.dsId,
contentdescription: this.addApiForm.get('contentdescription')?.value,
removable: true,
compatibility: this.addApiForm.get('compatibility')?.value,
metadataIdentifierPath: this.addApiForm.get('metadataIdentifierPath')?.value,
baseurl: this.addApiForm.get('baseurl')?.value,
apiParams: []
};
Object.keys(this.addApiForm.controls).forEach(k => {
if (k.startsWith(this.paramPrefix)) {
let val = this.addApiForm.get(k)?.value;
if (val) {
api.apiParams.push({
param: k.substring(this.paramPrefix.length),
value: (Array.isArray(val)) ? val.join() : val
});
}
}
});
console.log(api);
//this.service.dsmAddApi(api, (data: void) => this.dialogRef.close(1), this.metadataForm);
}
onNoClick(): void {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,41 @@
<form [formGroup]="emailForm" (ngSubmit)="onSubmit()">
<h1 mat-dialog-title *ngIf="emailForm.get('id')?.value">Edit Email Template</h1>
<h1 mat-dialog-title *ngIf="!emailForm.get('id')?.value">New Email Template</h1>
<div mat-dialog-content>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;" *ngIf="emailForm.get('id')?.value">
<mat-label>ID</mat-label>
<input matInput formControlName="id" readonly="readonly" />
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Description</mat-label>
<input matInput formControlName="description" />
<mat-error *ngIf="emailForm.get('description')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Email: subject</mat-label>
<input matInput formControlName="subject" />
<mat-error *ngIf="emailForm.get('subject')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Email: message</mat-label>
<textarea matInput formControlName="message" required rows="16" style="font-size: 0.8em;"></textarea>
<mat-error *ngIf="emailForm.get('message')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" type="submit" [disabled]="!emailForm.valid">Submit</button>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
<mat-error *ngIf="emailForm.errors?.['serverError']">
{{ emailForm.errors?.['serverError'] }}
</mat-error>
</div>
</form>

View File

@ -0,0 +1,42 @@
<h2>Email Templates</h2>
<button mat-stroked-button color="primary" (click)="openAddEmailTemplateDialog()">
<mat-icon fontIcon="add"></mat-icon>
create a new template
</button>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; margin-top: 10px;">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter..." #input />
</mat-form-field>
<table mat-table [dataSource]="emailsDatasource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef style="width: 25%;" mat-sort-header sortActionDescription="Sort by ID"> Id
</th>
<td mat-cell *matCellDef="let element">
<a (click)="openEditEmailTemplateDialog(element)">{{element.id}}</a>
</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Sort by Description"> Description </th>
<td mat-cell *matCellDef="let element"> {{element.description}} </td>
</ng-container>
<ng-container matColumnDef="buttons">
<th mat-header-cell *matHeaderCellDef style="text-align: right;" style="width: 20%"></th>
<td mat-cell *matCellDef="let element" class="table-buttons">
<button mat-stroked-button color="warn" (click)="deleteEmailTemplate(element)">delete</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="colums"></tr>
<tr mat-row *matRowDef="let row; columns: colums;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>

View File

@ -0,0 +1,98 @@
import { AfterViewInit, Component, Inject, OnInit, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { EmailTemplate } from '../common/is.model';
import { ISService } from '../common/is.service';
@Component({
selector: 'app-emails',
templateUrl: './emails.component.html',
styleUrls: ['./emails.component.css']
})
export class EmailsComponent implements OnInit, AfterViewInit {
emailsDatasource: MatTableDataSource<EmailTemplate> = new MatTableDataSource<EmailTemplate>([]);
colums: string[] = ['id', 'description', 'buttons'];
@ViewChild(MatSort) sort: MatSort | undefined
searchText: string = '';
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) { }
ngOnInit() { this.reload() }
ngAfterViewInit() { if (this.sort) this.emailsDatasource.sort = this.sort; }
reload() { this.service.loadEmailTemplates((data: EmailTemplate[]) => this.emailsDatasource.data = data); }
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
this.emailsDatasource.filter = filterValue;
}
openAddEmailTemplateDialog(): void {
const dialogRef = this.dialog.open(EmailDialog, {
data: {
id: '',
description: '',
subject: '',
message: ''
},
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
openEditEmailTemplateDialog(email: EmailTemplate): void {
const dialogRef = this.dialog.open(EmailDialog, {
data: email,
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
deleteEmailTemplate(email: EmailTemplate) {
if (confirm('Are you sure?')) {
this.service.deleteEmailTemplate(email.id, (data: void) => this.reload());
}
}
}
@Component({
selector: 'email-dialog',
templateUrl: './email-dialog.html',
styleUrls: ['./emails.component.css']
})
export class EmailDialog {
emailForm = new FormGroup({
id: new FormControl(''),
description: new FormControl('', [Validators.required]),
subject: new FormControl('', [Validators.required]),
message: new FormControl('', [Validators.required])
});
constructor(public dialogRef: MatDialogRef<EmailDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
this.emailForm.get('id')?.setValue(data.id);
this.emailForm.get('description')?.setValue(data.description);
this.emailForm.get('subject')?.setValue(data.subject);
this.emailForm.get('message')?.setValue(data.message);
}
onSubmit(): void {
const email = Object.assign({}, this.data, this.emailForm.value);
this.service.saveEmailTemplate(email, (data: void) => this.dialogRef.close(1), this.emailForm);
}
onNoClick(): void {
this.dialogRef.close();
}
}

View File

@ -0,0 +1,66 @@
<h2>Container Info</h2>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter..." #input />
</mat-form-field>
<div *ngFor="let section of kvDatasources">
<div style="margin-top: 2em;" *ngIf="section.datasource.filteredData.length > 0">
<h3>{{section.name}}</h3>
<table mat-table [dataSource]="section.datasource" class="mat-elevation-z8">
<ng-container matColumnDef="k">
<th mat-header-cell *matHeaderCellDef> Property </th>
<td mat-cell *matCellDef="let element" style="width: 30%"> {{element.k}} </td>
</ng-container>
<ng-container matColumnDef="v">
<th mat-header-cell *matHeaderCellDef> Value </th>
<td mat-cell *matCellDef="let element" style="width: 70%"> {{element.v}} </td>
</ng-container>
<tr mat-row *matRowDef="let row; columns: displayedKVColumns;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="2" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>
</div>
</div>
<div style="margin-top: 3em;" *ngIf="moduleDatasource.filteredData.length > 0">
<h3>Modules</h3>
<table mat-table [dataSource]="moduleDatasource" class="mat-elevation-z8">
<ng-container matColumnDef="group">
<th mat-header-cell *matHeaderCellDef style="width: 15%;"> Group </th>
<td mat-cell *matCellDef="let element"> {{element.group}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef style="width: 15%;"> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="versions">
<th mat-header-cell *matHeaderCellDef style="width: 10%;"> Versions </th>
<td mat-cell *matCellDef="let element"> <span *ngFor="let v of element.versions">{{v}}<br /></span> </td>
</ng-container>
<ng-container matColumnDef="files">
<th mat-header-cell *matHeaderCellDef style="width: 60%;"> Files </th>
<td mat-cell *matCellDef="let element"> <span *ngFor="let f of element.files">{{f}}<br /></span> </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedModuleColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedModuleColumns;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>
</div>

View File

@ -0,0 +1,45 @@
import { Component } from '@angular/core';
import { ISService } from '../common/is.service';
import { MatTableDataSource } from '@angular/material/table';
import { Module } from '../common/is.model';
export interface KeyValueDatasource {
name: string;
datasource: MatTableDataSource<any>;
}
@Component({
selector: 'app-info',
templateUrl: './info.component.html',
styleUrls: ['./info.component.css']
})
export class InfoComponent {
kvDatasources: KeyValueDatasource[] = [];
moduleDatasource: MatTableDataSource<Module> = new MatTableDataSource<Module>([]);
displayedKVColumns: string[] = ['k', 'v'];
displayedModuleColumns: string[] = ['group', 'name', 'versions', 'files'];
constructor(public service: ISService) {
this.service.loadInfo((data: any[]) => {
data.forEach(section => {
if (section['name'] == 'Modules') {
this.moduleDatasource.data = section['data'];
} else {
this.kvDatasources.push({
name: section['name'],
datasource: new MatTableDataSource(section['data'])
});
}
})
});
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
this.kvDatasources.forEach(s => s.datasource.filter = filterValue)
this.moduleDatasource.filter = filterValue;
}
}

View File

@ -0,0 +1,50 @@
.menu-count {
padding-top: 0.1em;
padding-bottom: 0.1em;
padding-left: 0.4em;
padding-right: 0.4em;
border-radius: 1em;
color: #fff;
background-color: cornflowerblue;
text-align: center;
}
.collapse-buttons {
text-align: right;
}
.collapse-buttons button {
font-size: 0.6em;
}
.mat-expansion-panel-spacing {
margin: 0;
}
.menu-item {
padding-top: 0.3em;
padding-bottom: 0.3em;
padding-left: 7%;
padding-right: 3%;
font-size: 0.9em;
width: 90%;
color: #696969;
text-decoration: none !important;
display: inline-block;
}
.menu-item:hover {
color: #999;
background-color: #eaeaea;
}
.menu-count {
padding-left: 0.5em;
padding-right: 0.5em;
border-radius: 1em;
font-size: 0.7em;
color: #fff;
text-align: center;
float: right;
width: 2em;
}

View File

@ -0,0 +1,92 @@
<div class="collapse-buttons">
<button mat-button color="primary" (click)="accordion.openAll()">Expand All</button>
<button mat-button color="primary" (click)="accordion.closeAll()">Collapse All</button>
</div>
<mat-accordion multi>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Home</mat-panel-title>
</mat-expansion-panel-header>
<a class="menu-item" routerLink="">Home</a>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Datasources</mat-panel-title>
</mat-expansion-panel-header>
<div>
<a class="menu-item" routerLink="dsm/search">Search</a>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Simple Resources</mat-panel-title>
</mat-expansion-panel-header>
<div>
<ng-container *ngFor="let r of resTypes">
<div *ngIf="r.simple">
<a class="menu-item" [routerLink]="['resources/' + r.id]">
{{r.name}}
<span class="menu-count">{{r.count}}</span>
</a>
</div>
</ng-container>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Advanced Resources</mat-panel-title>
</mat-expansion-panel-header>
<div>
<ng-container *ngFor="let r of resTypes">
<div *ngIf="!r.simple">
<a class="menu-item" [routerLink]="['adv_resources/' + r.id]">
{{r.name}}
<span class="menu-count">{{r.count}}</span>
</a>
</div>
</ng-container>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Workflows</mat-panel-title>
</mat-expansion-panel-header>
<div>
<div *ngFor="let s of wfSections">
<a class="menu-item" [routerLink]="['wfs', s.id]">{{s.name}}</a>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Tools</mat-panel-title>
</mat-expansion-panel-header>
<div>
<a class="menu-item" routerLink="mdstores">MDStore Inspector</a>
<a class="menu-item" routerLink="cleaner">Cleaner Tester</a>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Logs</mat-panel-title>
</mat-expansion-panel-header>
<div>
<a class="menu-item" routerLink="wf_history">Workflow History</a>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Info</mat-panel-title>
</mat-expansion-panel-header>
<div>
<a class="menu-item" routerLink="info">Container Info</a>
</div>
</mat-expansion-panel>
</mat-accordion>

View File

@ -0,0 +1,24 @@
import { Component, ViewChild } from '@angular/core';
import { KeyValue, ResourceType, WfSection } from '../common/is.model';
import { ISService } from '../common/is.service';
import { MatAccordion } from '@angular/material/expansion';
@Component({
selector: 'app-main-menu-panels',
templateUrl: './main-menu-panels.component.html',
styleUrls: ['./main-menu-panels.component.css']
})
export class MainMenuPanelsComponent {
@ViewChild(MatAccordion)
accordion!: MatAccordion;
resTypes: ResourceType[] = [];
wfSections: WfSection[] = [];
constructor(public service: ISService) {
service.loadResourceTypes((data: ResourceType[]) => this.resTypes = data);
service.loadWfSections((data: WfSection[]) => this.wfSections = data);
}
}

View File

@ -0,0 +1,61 @@
<form [formGroup]="newMdstoreForm" (ngSubmit)="onSubmit()">
<h1 mat-dialog-title>New MDStore</h1>
<div mat-dialog-content>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Format</mat-label>
<input matInput formControlName="format" />
<mat-error *ngIf="newMdstoreForm.get('format')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Layout</mat-label>
<input matInput formControlName="layout" />
<mat-error *ngIf="newMdstoreForm.get('layout')?.invalid">This field is <strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Interpretation</mat-label>
<input matInput formControlName="interpretation" />
<mat-error *ngIf="newMdstoreForm.get('interpretation')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Type</mat-label>
<mat-select matInput formControlName="type">
<mat-option value="HDFS">hdfs</mat-option>
<mat-option value="MOCK">mock</mat-option>
</mat-select>
<mat-error *ngIf="newMdstoreForm.get('type')?.invalid">This field is
<strong>required</strong></mat-error>
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Datasource Name</mat-label>
<input matInput formControlName="dsName" />
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>Datasource ID</mat-label>
<input matInput formControlName="dsId" />
</mat-form-field>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%;">
<mat-label>API ID</mat-label>
<input matInput formControlName="apiId" />
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" type="submit" [disabled]="!newMdstoreForm.valid">Submit</button>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
<mat-error *ngIf="newMdstoreForm.errors?.['serverError']">
{{ newMdstoreForm.errors?.['serverError'] }}
</mat-error>
</div>
</form>

View File

@ -0,0 +1,95 @@
<h2>Metadata Store Inspector</h2>
<p>
<a mat-stroked-button color="primary" [routerLink]="['/mdstores']">
<mat-icon fontIcon="home"></mat-icon>
return to mdstore list
</a>
</p>
<table class="mdstore-table">
<tr>
<th rowspan="4" class="col-xs-1" style="width: 10em">MdStore</th>
<th style="width: 20em">ID</th>
<td>{{mdstore?.id}}</td>
</tr>
<tr>
<th>Type</th>
<td>{{mdstore?.type}}</td>
</tr>
<tr>
<th>Format / Layout / Interpretation</th>
<td>{{mdstore?.format}} / {{mdstore?.layout}} / {{mdstore?.interpretation}}</td>
</tr>
<tr>
<th>Related Datasource</th>
<td>{{mdstore?.datasourceName}}</td>
</tr>
<tr>
<th [attr.rowspan]="(version?.params | keyvalue).length + 3">Version</th>
<th>ID</th>
<td>
<span *ngIf="version?.id == mdstore?.currentVersion" class="badge-label badge-success">current</span>
<span *ngIf="version?.writing && (version?.id != mdstore?.currentVersion)"
class="badge-label badge-warning">writing</span>
<span *ngIf="!version?.writing && (version?.id != mdstore?.currentVersion)"
class=" badge-label badge-failure">expired</span>
{{version?.id}}
</td>
</tr>
<tr *ngFor="let e of version?.params | keyvalue">
<th>Parameter {{e.key}}</th>
<td>{{e.value}}</td>
</tr>
<tr>
<th>Last Update</th>
<td>{{version?.lastUpdate}}</td>
</tr>
<tr>
<th>Size</th>
<td>{{version?.size}}</td>
</tr>
</table>
<h3 *ngIf="records.length > 0" class="muted" style="margin-top: 2em; text-align: center;">The display is limited to the
first {{limit}}
records</h3>
<mat-card *ngFor="let rec of records" style="margin-top: 1em;">
<mat-card-header>
<mat-card-title *ngIf="!rec.id">the record is unreadable</mat-card-title>
<mat-card-title *ngIf="rec.id">{{rec.id}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<br />
<p *ngIf="!rec.id">Invalid record format</p>
<table class="mdstore-table" *ngIf="rec.id">
<tr>
<th style="width: 15em;">Original Id</th>
<td>{{rec.originalId}}</td>
</tr>
<tr>
<th>Collected on</th>
<td>{{rec.dateOfCollection | date:'medium'}}</td>
</tr>
<tr>
<th>Transformed on</th>
<td>{{rec.dateOfTransformation | date:'medium'}}</td>
</tr>
<tr>
<th>Provenance</th>
<td class="small">
<span *ngIf="rec.provenance.datasourceName"><b>Datasource Name</b>:
{{rec.provenance.datasourceName}}<br /></span>
<span *ngIf="rec.provenance.datasourceId"><b>Datasource ID</b>: {{rec.provenance.datasourceId}}<br /></span>
<span *ngIf="rec.provenance.nsPrefix"><b>Prefix</b>: {{rec.provenance.nsPrefix}}<br /></span>
</td>
</tr>
<tr>
<th>Format</th>
<td>{{rec.encoding}}</td>
</tr>
</table>
<pre class="small">{{rec.body}}</pre>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,48 @@
<h1 mat-dialog-title>MDStore Versions</h1>
<div mat-dialog-content>
<div style="text-align: right;">
<button mat-stroked-button color="primary" (click)="reload()">refresh</button>
</div>
<table class="mdstore-table small">
<thead>
<tr>
<th style="width: 50%">ID</th>
<th style="width: 20%; text-align: center;">Read Count</th>
<th style="width: 20%; text-align: center;">Last Update</th>
<th style="width: 10%; text-align: right;">Size</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let v of versions" [ngClass]="{'active-row': v.id == data.currentVersion}">
<td title="{{v.id}}">
<mat-icon fontIcon="edit" *ngIf="v.writing" title="writing..."></mat-icon>
<b>{{v.id}}</b>
<br />
<span class="small" *ngFor="let e of v.params | keyvalue">
<b>{{e.key}}</b>:
{{e.value}}<br />
</span>
<button mat-stroked-button color="primary" (click)="openInspectorPage(v)">inspect</button>
<button mat-stroked-button color="basic" *ngIf="v.writing" (click)="commitVersion(v)">commit</button>
<button mat-stroked-button color="warn" *ngIf="v.writing" (click)="abortVersion(v)">abort</button>
<button mat-stroked-button color="warn" *ngIf="!v.writing && v.readCount == 0 && v.id != data.currentVersion"
(click)="deleteVersion(v)">delete</button>
</td>
<td style="text-align: center;">
{{v.readCount}}
<button mat-stroked-button color="primary" (click)="resetReading(v)"
[disabled]="v.readCount == 0">reset</button>
</td>
<td style="text-align: center;" title="{{v.lastUpdate}}">{{v.lastUpdate | date:"MMM dd,
yyyy 'at' HH:mm"}}</td>
<td style="text-align: right;">{{v.size}}</td>
</tr>
</tbody>
</table>
</div>
<div mat-dialog-actions>
<button mat-stroked-button color="primary" mat-dialog-close>Close</button>
</div>

View File

@ -0,0 +1,34 @@
.mdstore-table {
border-collapse: collapse;
border-bottom: 1pt solid lightgrey;
}
.mdstore-table tr {
border-top: 1pt solid lightgrey;
}
.mdstore-table th,
.mdstore-table td {
text-align: left;
font-size: 0.9em;
vertical-align: top;
padding-left: 1em;
padding-right: 1em;
}
.mdstore-table td button,
.mdstore-table td a.mdc-button {
font-size: 0.8em !important;
padding: 0 !important;
height: 2.5em !important;
}
.mdstore-table tr.active-row {
background-color: #daffda;
}
.mdstore-table mat-icon {
width: 1em;
height: 1em;
font-size: 1em;
}

View File

@ -0,0 +1,73 @@
<h2>Metadata Stores</h2>
<p>
<button mat-stroked-button color="primary" (click)="openAddMdstoreDialog()">
<mat-icon fontIcon="add"></mat-icon>
create a new mdstore
</button>
<button mat-stroked-button color="primary" (click)="reload()">
<mat-icon fontIcon="refresh"></mat-icon>
refresh
</button>
</p>
<mat-form-field appearance="fill" floatLabel="always" style="width: 100%; margin-top: 10px;">
<mat-label><b>Filter</b> (Total: {{(mdstores | searchFilter: searchText).length}})</mat-label>
<input matInput [(ngModel)]="searchText" placeholder="Filter..." autofocus />
</mat-form-field>
<mat-card *ngFor="let md of mdstores | searchFilter: searchText" style="margin-top: 10px;">
<mat-card-header>
<mat-card-title>{{md.id}}</mat-card-title>
<mat-card-subtitle>{{md.datasourceName}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content style="padding-top: 1em; padding-bottom: 1em;">
<table class="mdstore-table">
<tr>
<th style="width: 30%">Format / Layout / Interpretation</th>
<td style="width: 70%">{{md.format}} / {{md.layout}} / {{md.interpretation}}</td>
</tr>
<tr>
<th>Datasource</th>
<td>
<b>Name:</b> {{md.datasourceName}}<br />
<b>ID:</b> {{md.datasourceId}}<br />
<b>API:</b> {{md.apiId}}
</td>
</tr>
<tr>
<th>Creation Date</th>
<td>{{md.creationDate}}</td>
</tr>
<tr>
<th>Last Update</th>
<td>{{md.lastUpdate}}</td>
</tr>
<tr>
<th>Size</th>
<td>{{md.size}}</td>
</tr>
<tr>
<th>Type</th>
<td>{{md.type}}</td>
</tr>
<tr *ngFor="let e of md.params | keyvalue">
<th>Parameter {{e.key}}</th>
<td>{{e.value}}</td>
</tr>
<tr>
<th>Versions</th>
<td>
<a (click)="openVersionsDialog(md)">{{md.numberOfVersions}} version(s)</a>
/
<a (click)="createNewVersion(md)">prepare new version</a>
</td>
</tr>
</table>
</mat-card-content>
<mat-card-actions>
<a [routerLink]="['/mdrecords/' + md.currentVersion + '/50']" mat-stroked-button color="primary">inspect</a>
<button mat-stroked-button color="warn" (click)="deleteMdstore(md)">delete</button>
<button mat-stroked-button color="info">zeppelin</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,179 @@
import { Component, Inject, OnInit } from '@angular/core';
import { ISService } from '../common/is.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MDStore, MDStoreRecord, MDStoreVersion } from '../common/is.model';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-mdstores',
templateUrl: './mdstores.component.html',
styleUrls: ['./mdstores.component.css']
})
export class MdstoresComponent implements OnInit {
mdstores: MDStore[] = [];
searchText: string = '';
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) { }
ngOnInit() { this.reload() }
reload() { this.service.loadMDStores((data: MDStore[]) => this.mdstores = data); }
openVersionsDialog(md: MDStore): void {
const dialogRef = this.dialog.open(MDStoreVersionsDialog, {
data: md,
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
openAddMdstoreDialog(): void {
const dialogRef = this.dialog.open(AddMDStoreDialog, {
data: {},
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
createNewVersion(md: MDStore): void {
this.service.prepareNewMDStoreVersion(md.id, (data: MDStoreVersion) => {
md.numberOfVersions = md.numberOfVersions + 1;
this.openVersionsDialog(md);
});
}
deleteMdstore(md: MDStore) {
if (confirm('Are you sure?')) {
this.service.deleteMDStore(md.id, (data: void) => this.reload());
}
}
}
@Component({
selector: 'app-mdstore-inspector',
templateUrl: './mdstore-inspector.component.html',
styleUrls: ['./mdstores.component.css']
})
export class MdstoreInspectorComponent implements OnInit {
mdstore?: MDStore = undefined;
version?: MDStoreVersion = undefined;
records: MDStoreRecord[] = [];
limit: number = 0;
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
}
ngOnInit() {
this.route.params.subscribe(params => {
const versionId = params['versionId'];
this.limit = params['limit'];
this.service.loadMDStoreVersion(versionId, (data: MDStoreVersion) => {
this.version = data;
this.service.loadMDStore(this.version.mdstore, (data: MDStore) => {
this.mdstore = data;
this.service.loadMDStoreVersionRecords(versionId, this.limit, (data: MDStoreRecord[]) => {
this.records = data;
});
});
});
});
}
}
@Component({
selector: 'mdstores-versions-dialog',
templateUrl: './mdstores-versions-dialog.html',
styleUrls: ['./mdstores.component.css']
})
export class MDStoreVersionsDialog {
versions: MDStoreVersion[] = [];
constructor(public dialogRef: MatDialogRef<MDStoreVersionsDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService, public router: Router) {
this.reload();
}
reload() {
this.service.loadMDStoreVersions(this.data.id, (res: MDStoreVersion[]) => this.versions = res);
}
openInspectorPage(version: MDStoreVersion): void {
const url = this.router.serializeUrl(
this.router.createUrlTree(['/mdrecords/' + encodeURIComponent(version.id) + '/50'])
);
window.open(url, '_blank');
}
resetReading(version: MDStoreVersion): void {
this.service.resetReadingMDStoreVersion(version.id, (data: void) => version.readCount = 0);
}
commitVersion(version: MDStoreVersion): void {
let size: number = parseInt(prompt("New Size", "0") || "0");
if (size >= 0) {
this.service.commitMDStoreVersion(version.id, size, (data: void) => {
this.data.currentVersion = version.id;
this.reload();
});
}
}
abortVersion(version: MDStoreVersion): void {
this.service.abortMDStoreVersion(version.id, (data: void) => this.reload());
}
deleteVersion(version: MDStoreVersion): void {
this.service.deleteMDStoreVersion(version.id, (data: void) => this.reload());
}
onNoClick(): void {
this.dialogRef.close();
}
}
@Component({
selector: 'add-mdstore-dialog',
templateUrl: './add-mdstore-dialog.html',
styleUrls: ['./mdstores.component.css']
})
export class AddMDStoreDialog {
newMdstoreForm = new FormGroup({
format: new FormControl('', [Validators.required]),
layout: new FormControl('', [Validators.required]),
interpretation: new FormControl('', [Validators.required]),
type: new FormControl('', [Validators.required]),
dsName: new FormControl(''),
dsId: new FormControl(''),
apiId: new FormControl(''),
});
constructor(public dialogRef: MatDialogRef<MDStoreVersionsDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
}
onSubmit(): void {
let format: string = this.newMdstoreForm.get('format')?.value!;
let layout: string = this.newMdstoreForm.get('layout')?.value!;
let interpretation: string = this.newMdstoreForm.get('interpretation')?.value!;
let type: string = this.newMdstoreForm.get('type')?.value!;
let dsName: string = this.newMdstoreForm.get('dsName')?.value!;
let dsId: string = this.newMdstoreForm.get('dsId')?.value!;
let apiId: string = this.newMdstoreForm.get('apiId')?.value!;
this.service.addMDStore(format, layout, interpretation, type, dsName, dsId, apiId, (data: void) => this.dialogRef.close(1), this.newMdstoreForm);
}
onNoClick(): void {
this.dialogRef.close();
}
}

Some files were not shown because too many files have changed in this diff Show More