• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kit-data-manager / pit-service / #449

22 Jan 2025 04:24PM UTC coverage: 75.383% (+3.0%) from 72.4%
#449

Pull #218

github

web-flow
Merge 81f9d2335 into 459f0c036
Pull Request #218: Type-Api support and validation speedup

257 of 322 new or added lines in 18 files covered. (79.81%)

3 existing lines in 2 files now uncovered.

885 of 1174 relevant lines covered (75.38%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/src/main/java/edu/kit/datamanager/pit/domain/Operations.java
1
package edu.kit.datamanager.pit.domain;
2

3
import java.io.IOException;
4
import java.util.ArrayList;
5
import java.util.Arrays;
6
import java.util.Collection;
7
import java.util.Comparator;
8
import java.util.Date;
9
import java.util.List;
10
import java.util.Optional;
11
import java.util.concurrent.CompletableFuture;
12
import java.util.stream.Collectors;
13

14
import edu.kit.datamanager.pit.pidsystem.IIdentifierSystem;
15
import edu.kit.datamanager.pit.typeregistry.AttributeInfo;
16
import edu.kit.datamanager.pit.typeregistry.ITypeRegistry;
17
import org.apache.commons.lang3.stream.Streams;
18
import org.joda.time.DateTime;
19
import org.joda.time.format.DateTimeFormatter;
20
import org.joda.time.format.ISODateTimeFormat;
21

22
/**
23
 * Simple operations on PID records.
24
 * 
25
 * Caches results e.g. for type queries
26
 */
27
public class Operations {
28

29
    private static final String[] KNOWN_DATE_CREATED = {
1✔
30
        "21.T11148/29f92bd203dd3eaa5a1f",
31
        "21.T11148/aafd5fb4c7222e2d950a"
32
    };
33

34
    private static final String[] KNOWN_DATE_MODIFIED = {
1✔
35
        "21.T11148/397d831aa3a9d18eb52c"
36
    };
37

38
    private ITypeRegistry typeRegistry;
39
    private IIdentifierSystem identifierSystem;
40

41
    public Operations(ITypeRegistry typeRegistry, IIdentifierSystem identifierSystem) {
1✔
42
        this.typeRegistry = typeRegistry;
1✔
43
        this.identifierSystem = identifierSystem;
1✔
44
    }
1✔
45

46
    /**
47
     * Tries to get the date when a FAIR DO was created from a PID record.
48
     * 
49
     * Strategy:
50
     * - try to get it from known "dateCreated" types
51
     * - as a fallback, try to get it by its human readable name
52
     * 
53
     * Semantic reasoning in some sense is planned but not yet supported.
54
     * 
55
     * @param pidRecord the record to extract the information from.
56
     * @return the date, if it could been extracted.
57
     * @throws IOException on IO errors regarding resolving types.
58
     */
59
    public Optional<Date> findDateCreated(PIDRecord pidRecord) throws IOException {
60
        /* try known types */
61
        List<String> knownDateTypes = Arrays.asList(Operations.KNOWN_DATE_CREATED);
1✔
62
        Optional<Date> date = knownDateTypes
1✔
63
            .stream()
1✔
64
            .map(pidRecord::getPropertyValues)
1✔
65
            .map(Arrays::asList)
1✔
66
            .flatMap(List<String>::stream)
1✔
67
            .map(this::extractDate)
1✔
68
            .filter(Optional<Date>::isPresent)
1✔
69
            .map(Optional<Date>::get)
1✔
70
            .sorted(Comparator.comparingLong(Date::getTime))
1✔
71
            .findFirst();
1✔
72
        if (date.isPresent()) {
1✔
73
            return date;
1✔
74
        }
75

76
        Collection<AttributeInfo> types = new ArrayList<>();
1✔
77
        List<CompletableFuture<?>> futures = Streams.failableStream(
1✔
78
                pidRecord.getPropertyIdentifiers().stream())
1✔
79
                .filter(attributePid -> this.identifierSystem.isPidRegistered(attributePid))
1✔
80
                .map(attributePid -> {
1✔
NEW
81
                    return this.typeRegistry
×
NEW
82
                            .queryAttributeInfo(attributePid)
×
NEW
83
                            .thenAcceptAsync(types::add);
×
84
                })
85
                .collect(Collectors.toList());
1✔
86
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
1✔
87

88
        /*
89
         * as a last fallback, try find types with human readable names containing
90
         * "dateCreated" or "createdAt" or "creationDate".
91
         * 
92
         * This can be removed as soon as we have some default FAIR DO types new type
93
         * definitions can refer to (e.g. "extend" them or declare the same meaning as
94
         * our known types, see above)
95
         */
96
        return types
1✔
97
            .stream()
1✔
98
            .filter(type -> 
1✔
NEW
99
                type.name().equalsIgnoreCase("dateCreated")
×
NEW
100
                || type.name().equalsIgnoreCase("createdAt")
×
NEW
101
                || type.name().equalsIgnoreCase("creationDate"))
×
102
            .map(type -> pidRecord.getPropertyValues(type.pid()))
1✔
103
            .map(Arrays::asList)
1✔
104
            .flatMap(List<String>::stream)
1✔
105
            .map(this::extractDate)
1✔
106
            .filter(Optional<Date>::isPresent)
1✔
107
            .map(Optional<Date>::get)
1✔
108
            .sorted(Comparator.comparingLong(Date::getTime))
1✔
109
            .findFirst();
1✔
110
    }
111

112
    /**
113
     * Tries to get the date when a FAIR DO was modified from a PID record.
114
     * 
115
     * Strategy:
116
     * - try to get it from known "dateModified" types
117
     * - as a fallback, try to get it by its human readable name
118
     * 
119
     * Semantic reasoning in some sense is planned but not yet supported.
120
     * 
121
     * @param pidRecord the record to extract the information from.
122
     * @return the date, if it could been extracted.
123
     * @throws IOException on IO errors regarding resolving types.
124
     */
125
    public Optional<Date> findDateModified(PIDRecord pidRecord) throws IOException {
126
        /* try known types */
127
        List<String> knownDateTypes = Arrays.asList(Operations.KNOWN_DATE_MODIFIED);
1✔
128
        Optional<Date> date = knownDateTypes
1✔
129
            .stream()
1✔
130
            .map(pidRecord::getPropertyValues)
1✔
131
            .map(Arrays::asList)
1✔
132
            .flatMap(List<String>::stream)
1✔
133
            .map(this::extractDate)
1✔
134
            .filter(Optional<Date>::isPresent)
1✔
135
            .map(Optional<Date>::get)
1✔
136
            .sorted(Comparator.comparingLong(Date::getTime))
1✔
137
            .findFirst();
1✔
138
        if (date.isPresent()) {
1✔
139
            return date;
1✔
140
        }
141

142
        Collection<AttributeInfo> types = new ArrayList<>();
1✔
143
        List<CompletableFuture<?>> futures = Streams.failableStream(pidRecord.getPropertyIdentifiers().stream())
1✔
144
                .filter(attributePid -> this.identifierSystem.isPidRegistered(attributePid))
1✔
145
                .map(attributePid -> {
1✔
NEW
146
                    return this.typeRegistry
×
NEW
147
                            .queryAttributeInfo(attributePid)
×
NEW
148
                            .thenAcceptAsync(types::add);
×
149
                })
150
                .collect(Collectors.toList());
1✔
151
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
1✔
152

153
        /*
154
         * as a last fallback, try find types with human readable names containing
155
         * "dateModified" or "lastModified" or "modificationDate".
156
         * 
157
         * This can be removed as soon as we have some default FAIR DO types new type
158
         * definitions can refer to (e.g. "extend" them or declare the same meaning as
159
         * our known types, see above)
160
         */
161
        return types
1✔
162
            .stream()
1✔
163
            .filter(type -> 
1✔
NEW
164
                type.name().equalsIgnoreCase("dateModified")
×
NEW
165
                || type.name().equalsIgnoreCase("lastModified")
×
NEW
166
                || type.name().equalsIgnoreCase("modificationDate"))
×
167
            .map(type -> pidRecord.getPropertyValues(type.pid()))
1✔
168
            .map(Arrays::asList)
1✔
169
            .flatMap(List<String>::stream)
1✔
170
            .map(this::extractDate)
1✔
171
            .filter(Optional<Date>::isPresent)
1✔
172
            .map(Optional<Date>::get)
1✔
173
            .sorted(Comparator.comparingLong(Date::getTime))
1✔
174
            .findFirst();
1✔
175
    }
176

177
    /**
178
     * Tries to extract a Date object from a String.
179
     * 
180
     * @param dateString the date string to extract the date from.
181
     * @return the extracted Date object.
182
     */
183
    protected Optional<Date> extractDate(String dateString) {
184
        DateTimeFormatter dateFormatter = ISODateTimeFormat.dateTime();
1✔
185
        try {
186
            DateTime dateTime = dateFormatter.parseDateTime(dateString);
1✔
187
            return Optional.of(dateTime.toDate());
1✔
188
        } catch (Exception e) {
1✔
189
            return Optional.empty();
1✔
190
        }
191
    }
192
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc