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

knowledgepixels / nanodash / 26560064952

28 May 2026 07:04AM UTC coverage: 20.672% (+0.03%) from 20.643%
26560064952

Pull #473

github

web-flow
Merge e68ff10df into ad92b3169
Pull Request #473: refactor: collapse repo cache + push view-display admin gate server-side

1009 of 6168 branches covered (16.36%)

Branch coverage included in aggregate %.

2594 of 11261 relevant lines covered (23.04%)

3.3 hits per line

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

79.76
src/main/java/com/knowledgepixels/nanodash/repository/MaintainedResourceRepository.java
1
package com.knowledgepixels.nanodash.repository;
2

3
import com.knowledgepixels.nanodash.ApiCache;
4
import com.knowledgepixels.nanodash.QueryApiAccess;
5
import com.knowledgepixels.nanodash.domain.MaintainedResource;
6
import com.knowledgepixels.nanodash.domain.MaintainedResourceFactory;
7
import com.knowledgepixels.nanodash.domain.Space;
8
import org.nanopub.extra.services.ApiResponse;
9
import org.nanopub.extra.services.ApiResponseEntry;
10
import org.nanopub.extra.services.QueryRef;
11

12
import java.util.ArrayList;
13
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19

20
public class MaintainedResourceRepository {
21

22
    private static final MaintainedResourceRepository INSTANCE = new MaintainedResourceRepository();
15✔
23

24
    /**
25
     * Get the singleton instance of MaintainedResourceRepository.
26
     *
27
     * @return The singleton instance of MaintainedResourceRepository.
28
     */
29
    public static MaintainedResourceRepository get() {
30
        return INSTANCE;
6✔
31
    }
32

33
    private MaintainedResourceRepository() {
6✔
34
    }
3✔
35

36
    private static final class Snapshot {
37
        final Map<String, MaintainedResource> resourcesById;
38
        final Map<String, MaintainedResource> resourcesByNamespace;
39
        final Map<Space, List<MaintainedResource>> resourcesBySpace;
40

41
        Snapshot(Map<String, MaintainedResource> byId,
42
                 Map<String, MaintainedResource> byNs,
43
                 Map<Space, List<MaintainedResource>> bySpace) {
6✔
44
            this.resourcesById = byId;
9✔
45
            this.resourcesByNamespace = byNs;
9✔
46
            this.resourcesBySpace = bySpace;
9✔
47
        }
3✔
48

49
        static final Snapshot EMPTY = new Snapshot(
9✔
50
                Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
15✔
51
    }
52

53
    // Source of truth is ApiCache (60s TTL on its own); we memoise the derived
54
    // maps keyed by the ApiResponse instance identity so they get rebuilt
55
    // exactly when ApiCache returns a fresh response and not on every call.
56
    private volatile ApiResponse cachedFor;
57
    private volatile Snapshot snapshot = Snapshot.EMPTY;
9✔
58

59
    private Snapshot current() {
60
        ApiResponse resp = ApiCache.retrieveResponseSync(new QueryRef(QueryApiAccess.GET_MAINTAINED_RESOURCES), false);
21✔
61
        if (resp == null) {
6!
62
            return snapshot;
×
63
        }
64
        if (resp == cachedFor) {
12✔
65
            return snapshot;
9✔
66
        }
67
        synchronized (this) {
12✔
68
            if (resp == cachedFor) {
12!
69
                return snapshot;
×
70
            }
71
            Snapshot built = build(resp);
12✔
72
            snapshot = built;
9✔
73
            cachedFor = resp;
9✔
74
            return built;
12✔
75
        }
76
    }
77

78
    /**
79
     * Build the resource lookup maps from the spaces-repo response. Pulls
80
     * server-validated {@code npa:isMaintainedBy} links from the current
81
     * space-state graph; only the most recent declaration per resource is kept
82
     * (the {@code get-maintained-resources} query orders by {@code DESC(?date)},
83
     * so the first row per resource wins).
84
     */
85
    private Snapshot build(ApiResponse resp) {
86
        Map<String, MaintainedResource> byId = new HashMap<>();
12✔
87
        Map<String, MaintainedResource> byNamespace = new HashMap<>();
12✔
88
        Map<Space, List<MaintainedResource>> bySpace = new HashMap<>();
12✔
89
        Set<String> seenResources = new HashSet<>();
12✔
90
        for (ApiResponseEntry r : resp.getData()) {
33✔
91
            String resourceId = r.get("resource");
12✔
92
            if (resourceId == null || resourceId.isEmpty()) continue;
15!
93
            if (!seenResources.add(resourceId)) continue; // first row (newest date) wins
15✔
94
            String spaceId = r.get("space");
12✔
95
            Space space = SpaceRepository.get().findById(spaceId);
12✔
96
            if (space == null) continue;
6!
97
            ApiResponseEntry entry = new ApiResponseEntry();
12✔
98
            entry.add("resource", resourceId);
12✔
99
            entry.add("np", r.get("np"));
18✔
100
            String label = r.get("label");
12✔
101
            if (label != null && !label.isEmpty()) entry.add("label", label);
27!
102
            String namespace = r.get("namespace");
12✔
103
            if (namespace != null && !namespace.isEmpty()) entry.add("namespace", namespace);
27!
104
            MaintainedResource resource = MaintainedResourceFactory.getOrCreate(entry, space);
12✔
105
            byId.put(resourceId, resource);
15✔
106
            bySpace.computeIfAbsent(space, k -> new ArrayList<>()).add(resource);
36✔
107
            if (resource.getNamespace() != null) {
9✔
108
                // TODO Handle conflicts when two resources claim the same namespace:
109
                byNamespace.put(resource.getNamespace(), resource);
18✔
110
            }
111
        }
3✔
112
        MaintainedResourceFactory.removeStale(byId.keySet());
9✔
113
        return new Snapshot(byId, byNamespace, bySpace);
21✔
114
    }
115

116
    /**
117
     * Find a maintained resource by its namespace.
118
     *
119
     * @param namespace The namespace to search for.
120
     * @return The MaintainedResource with the given namespace, or null if not found.
121
     */
122
    public MaintainedResource findByNamespace(String namespace) {
123
        return current().resourcesByNamespace.get(namespace);
×
124
    }
125

126
    /**
127
     * Find the maintained resources belonging to a given space.
128
     *
129
     * @param space The space to look up.
130
     * @return The list of maintained resources for the space, possibly empty.
131
     */
132
    public List<MaintainedResource> findResourcesBySpace(Space space) {
133
        List<MaintainedResource> l = current().resourcesBySpace.get(space);
×
134
        return l != null ? l : new ArrayList<>();
×
135
    }
136

137
    /**
138
     * Get a maintained resource by its id.
139
     *
140
     * @param id The id of the resource.
141
     * @return The corresponding MaintainedResource object, or null if not found.
142
     */
143
    public MaintainedResource findById(String id) {
144
        return current().resourcesById.get(id);
21✔
145
    }
146

147
    /**
148
     * Invalidate the underlying ApiCache entry, optionally delaying the next refresh.
149
     *
150
     * @param waitMillis Delay in milliseconds before the next access may trigger a refresh; 0 for immediate.
151
     */
152
    public void forceRootRefresh(long waitMillis) {
153
        ApiCache.clearCache(new QueryRef(QueryApiAccess.GET_MAINTAINED_RESOURCES), waitMillis);
×
154
    }
×
155

156
}
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