1
2
3
4
5
6 package org.tailormap.api.configuration.dev;
7
8 import static org.tailormap.api.persistence.Configuration.HOME_PAGE;
9 import static org.tailormap.api.persistence.Configuration.PORTAL_MENU;
10 import static org.tailormap.api.persistence.json.GeoServiceProtocol.WMS;
11 import static org.tailormap.api.persistence.json.GeoServiceProtocol.WMTS;
12 import static org.tailormap.api.persistence.json.GeoServiceProtocol.XYZ;
13 import static org.tailormap.api.security.AuthorizationService.ACCESS_TYPE_READ;
14
15 import com.fasterxml.jackson.core.JsonProcessingException;
16 import com.fasterxml.jackson.databind.ObjectMapper;
17 import java.io.IOException;
18 import java.lang.invoke.MethodHandles;
19 import java.time.OffsetDateTime;
20 import java.time.ZoneId;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.NoSuchElementException;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.UUID;
29 import org.apache.solr.client.solrj.SolrServerException;
30 import org.quartz.SchedulerException;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.springframework.beans.factory.annotation.Value;
34 import org.springframework.boot.SpringApplication;
35 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
36 import org.springframework.boot.context.event.ApplicationReadyEvent;
37 import org.springframework.context.ApplicationContext;
38 import org.springframework.context.event.EventListener;
39 import org.springframework.core.io.ClassPathResource;
40 import org.springframework.transaction.annotation.Transactional;
41 import org.tailormap.api.admin.model.TaskSchedule;
42 import org.tailormap.api.geotools.featuresources.FeatureSourceFactoryHelper;
43 import org.tailormap.api.geotools.featuresources.JDBCFeatureSourceHelper;
44 import org.tailormap.api.geotools.featuresources.WFSFeatureSourceHelper;
45 import org.tailormap.api.persistence.Application;
46 import org.tailormap.api.persistence.Catalog;
47 import org.tailormap.api.persistence.Configuration;
48 import org.tailormap.api.persistence.GeoService;
49 import org.tailormap.api.persistence.Group;
50 import org.tailormap.api.persistence.Page;
51 import org.tailormap.api.persistence.SearchIndex;
52 import org.tailormap.api.persistence.TMFeatureSource;
53 import org.tailormap.api.persistence.TMFeatureType;
54 import org.tailormap.api.persistence.Upload;
55 import org.tailormap.api.persistence.User;
56 import org.tailormap.api.persistence.helper.GeoServiceHelper;
57 import org.tailormap.api.persistence.json.AdminAdditionalProperty;
58 import org.tailormap.api.persistence.json.AppContent;
59 import org.tailormap.api.persistence.json.AppLayerSettings;
60 import org.tailormap.api.persistence.json.AppSettings;
61 import org.tailormap.api.persistence.json.AppTreeLayerNode;
62 import org.tailormap.api.persistence.json.AppTreeLevelNode;
63 import org.tailormap.api.persistence.json.AppTreeNode;
64 import org.tailormap.api.persistence.json.AttributeSettings;
65 import org.tailormap.api.persistence.json.AuthorizationRule;
66 import org.tailormap.api.persistence.json.AuthorizationRuleDecision;
67 import org.tailormap.api.persistence.json.Bounds;
68 import org.tailormap.api.persistence.json.CatalogNode;
69 import org.tailormap.api.persistence.json.FeatureTypeRef;
70 import org.tailormap.api.persistence.json.FeatureTypeTemplate;
71 import org.tailormap.api.persistence.json.GeoServiceDefaultLayerSettings;
72 import org.tailormap.api.persistence.json.GeoServiceLayerSettings;
73 import org.tailormap.api.persistence.json.GeoServiceSettings;
74 import org.tailormap.api.persistence.json.JDBCConnectionProperties;
75 import org.tailormap.api.persistence.json.MenuItem;
76 import org.tailormap.api.persistence.json.PageTile;
77 import org.tailormap.api.persistence.json.ServiceAuthentication;
78 import org.tailormap.api.persistence.json.TailormapObjectRef;
79 import org.tailormap.api.persistence.json.TileLayerHiDpiMode;
80 import org.tailormap.api.repository.ApplicationRepository;
81 import org.tailormap.api.repository.CatalogRepository;
82 import org.tailormap.api.repository.ConfigurationRepository;
83 import org.tailormap.api.repository.FeatureSourceRepository;
84 import org.tailormap.api.repository.GeoServiceRepository;
85 import org.tailormap.api.repository.GroupRepository;
86 import org.tailormap.api.repository.PageRepository;
87 import org.tailormap.api.repository.SearchIndexRepository;
88 import org.tailormap.api.repository.UploadRepository;
89 import org.tailormap.api.repository.UserRepository;
90 import org.tailormap.api.scheduling.FailingPocTask;
91 import org.tailormap.api.scheduling.IndexTask;
92 import org.tailormap.api.scheduling.InterruptablePocTask;
93 import org.tailormap.api.scheduling.PocTask;
94 import org.tailormap.api.scheduling.TMJobDataMap;
95 import org.tailormap.api.scheduling.Task;
96 import org.tailormap.api.scheduling.TaskManagerService;
97 import org.tailormap.api.scheduling.TaskType;
98 import org.tailormap.api.security.InternalAdminAuthentication;
99 import org.tailormap.api.solr.SolrHelper;
100 import org.tailormap.api.solr.SolrService;
101 import org.tailormap.api.viewer.model.AppStyling;
102 import org.tailormap.api.viewer.model.Component;
103 import org.tailormap.api.viewer.model.ComponentConfig;
104
105
106
107
108
109 @org.springframework.context.annotation.Configuration
110 @ConditionalOnProperty(name = "tailormap-api.database.populate-testdata", havingValue = "true")
111 public class PopulateTestData {
112
113 private static final Logger logger =
114 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
115 private final ApplicationContext appContext;
116 private final UserRepository userRepository;
117 private final GroupRepository groupRepository;
118 private final CatalogRepository catalogRepository;
119 private final GeoServiceRepository geoServiceRepository;
120 private final GeoServiceHelper geoServiceHelper;
121 private final SolrService solrService;
122 private final TaskManagerService taskManagerService;
123 private final FeatureSourceRepository featureSourceRepository;
124 private final ApplicationRepository applicationRepository;
125 private final ConfigurationRepository configurationRepository;
126 private final SearchIndexRepository searchIndexRepository;
127 private final FeatureSourceFactoryHelper featureSourceFactoryHelper;
128 private final UploadRepository uploadRepository;
129 private final PageRepository pageRepository;
130
131 @Value("${spatial.dbs.connect:false}")
132 private boolean connectToSpatialDbs;
133
134 @Value("#{'${tailormap-api.database.populate-testdata.categories}'.split(',')}")
135 private Set<String> categories;
136
137 @Value("${spatial.dbs.localhost:true}")
138 private boolean connectToSpatialDbsAtLocalhost;
139
140 @Value("${tailormap-api.database.populate-testdata.admin-hashed-password}")
141 private String adminHashedPassword;
142
143 @Value("${tailormap-api.database.populate-testdata.exit:false}")
144 private boolean exit;
145
146 @Value("${MAP5_URL:#{null}}")
147 private String map5url;
148
149 @Value("${tailormap-api.solr-batch-size:1000}")
150 private int solrBatchSize;
151
152 @Value("${tailormap-api.solr-geometry-validation-rule:repairBuffer0}")
153 private String solrGeometryValidationRule;
154
155 public PopulateTestData(
156 ApplicationContext appContext,
157 UserRepository userRepository,
158 GroupRepository groupRepository,
159 CatalogRepository catalogRepository,
160 GeoServiceRepository geoServiceRepository,
161 GeoServiceHelper geoServiceHelper,
162 SolrService solrService,
163 TaskManagerService taskManagerService,
164 FeatureSourceRepository featureSourceRepository,
165 ApplicationRepository applicationRepository,
166 ConfigurationRepository configurationRepository,
167 FeatureSourceFactoryHelper featureSourceFactoryHelper,
168 SearchIndexRepository searchIndexRepository,
169 UploadRepository uploadRepository,
170 PageRepository pageRepository) {
171 this.appContext = appContext;
172 this.userRepository = userRepository;
173 this.groupRepository = groupRepository;
174 this.catalogRepository = catalogRepository;
175 this.geoServiceRepository = geoServiceRepository;
176 this.geoServiceHelper = geoServiceHelper;
177 this.solrService = solrService;
178 this.taskManagerService = taskManagerService;
179 this.featureSourceRepository = featureSourceRepository;
180 this.applicationRepository = applicationRepository;
181 this.configurationRepository = configurationRepository;
182 this.featureSourceFactoryHelper = featureSourceFactoryHelper;
183 this.searchIndexRepository = searchIndexRepository;
184 this.uploadRepository = uploadRepository;
185 this.pageRepository = pageRepository;
186 }
187
188 @EventListener(ApplicationReadyEvent.class)
189 @Transactional
190 public void populate() throws Exception {
191 InternalAdminAuthentication.setInSecurityContext();
192 try {
193
194
195 createTestUsersAndGroups();
196 createConfigurationTestData();
197 if (categories.contains("catalog")) {
198 createCatalogTestData();
199 }
200 if (categories.contains("apps")) {
201 createAppTestData();
202 }
203 if (categories.contains("search-index")) {
204 try {
205 createSolrIndex();
206 } catch (Exception e) {
207 logger.error("Exception creating Solr Index for testdata (continuing)", e);
208 }
209 }
210 if (categories.contains("tasks")) {
211 createPocTasks();
212 }
213 if (categories.contains("pages")) {
214 createPages();
215 }
216 logger.info("Test entities created");
217 } finally {
218 InternalAdminAuthentication.clearSecurityContextAuthentication();
219 }
220 if (exit) {
221
222
223 new Thread(
224 () -> {
225 try {
226 Thread.sleep(5000);
227 } catch (InterruptedException ignored) {
228
229 }
230 SpringApplication.exit(appContext, () -> 0);
231 System.exit(0);
232 })
233 .start();
234 }
235 }
236
237 public void createTestUsersAndGroups() throws NoSuchElementException {
238 Group groupFoo = new Group().setName("test-foo").setDescription("Used for integration tests.");
239 groupRepository.save(groupFoo);
240
241 AdminAdditionalProperty gp1 = new AdminAdditionalProperty();
242 gp1.setKey("group-property");
243 gp1.setValue(Boolean.TRUE);
244 gp1.setIsPublic(true);
245 AdminAdditionalProperty gp2 = new AdminAdditionalProperty();
246 gp2.setKey("group-private-property");
247 gp2.setValue(999.9);
248 gp2.setIsPublic(false);
249 Group groupBar =
250 new Group()
251 .setName("test-bar")
252 .setDescription("Used for integration tests.")
253 .setAdditionalProperties(List.of(gp1, gp2));
254 groupRepository.save(groupBar);
255
256 Group groupBaz = new Group().setName("test-baz").setDescription("Used for integration tests.");
257 groupRepository.save(groupBaz);
258
259
260 User u = new User().setUsername("user").setPassword("{noop}user").setEmail("user@example.com");
261 u.getGroups().addAll(List.of(groupFoo, groupBar, groupBaz));
262 userRepository.save(u);
263
264
265 AdminAdditionalProperty up1 = new AdminAdditionalProperty();
266 up1.setKey("some-property");
267 up1.setValue("some-value");
268 up1.setIsPublic(true);
269 AdminAdditionalProperty up2 = new AdminAdditionalProperty();
270 up2.setKey("admin-property");
271 up2.setValue("private-value");
272 up2.setIsPublic(false);
273 u =
274 new User()
275 .setUsername("tm-admin")
276 .setPassword(adminHashedPassword)
277 .setAdditionalProperties(List.of(up1, up2));
278 u.getGroups().add(groupRepository.findById(Group.ADMIN).orElseThrow());
279 u.getGroups().add(groupBar);
280 userRepository.save(u);
281 }
282
283 private final List<AuthorizationRule> ruleAnonymousRead =
284 List.of(
285 new AuthorizationRule()
286 .groupName(Group.ANONYMOUS)
287 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW)));
288
289 private final List<AuthorizationRule> ruleLoggedIn =
290 List.of(
291 new AuthorizationRule()
292 .groupName(Group.AUTHENTICATED)
293 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW)));
294
295 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
296 private void createCatalogTestData() throws Exception {
297 Catalog catalog = catalogRepository.findById(Catalog.MAIN).orElseThrow();
298 CatalogNode rootCatalogNode = catalog.getNodes().get(0);
299 CatalogNode catalogNode = new CatalogNode().id("test").title("Test services");
300 rootCatalogNode.addChildrenItem(catalogNode.getId());
301 catalog.getNodes().add(catalogNode);
302
303 String osmAttribution =
304 "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors";
305
306 Bounds rdTileGridExtent =
307 new Bounds().minx(-285401.92).maxx(595401.92).miny(22598.08).maxy(903401.92);
308
309 Upload legend =
310 new Upload()
311 .setCategory(Upload.CATEGORY_LEGEND)
312 .setFilename("gemeentegebied-legend.png")
313 .setMimeType("image/png")
314 .setContent(
315 new ClassPathResource("test/gemeentegebied-legend.png").getContentAsByteArray())
316 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
317 uploadRepository.save(legend);
318
319 Collection<GeoService> services =
320 List.of(
321 new GeoService()
322 .setId("demo")
323 .setProtocol(WMS)
324 .setTitle("Demo")
325 .setPublished(true)
326 .setAuthorizationRules(ruleAnonymousRead)
327 .setUrl("https://demo.tailormap.com/geoserver/geodata/ows?SERVICE=WMS"),
328 new GeoService()
329 .setId("osm")
330 .setProtocol(XYZ)
331 .setTitle("OSM")
332 .setUrl("https://tile.openstreetmap.org/{z}/{x}/{y}.png")
333 .setAuthorizationRules(ruleAnonymousRead)
334 .setSettings(
335 new GeoServiceSettings()
336 .xyzCrs("EPSG:3857")
337 .layerSettings(
338 Map.of(
339 "xyz",
340 new GeoServiceLayerSettings()
341 .attribution(osmAttribution)
342 .maxZoom(19)))),
343
344 new GeoService()
345 .setId("snapshot-geoserver")
346 .setProtocol(WMS)
347 .setTitle("Test GeoServer")
348 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
349 .setAuthorizationRules(ruleAnonymousRead)
350 .setPublished(true),
351 new GeoService()
352 .setId("filtered-snapshot-geoserver")
353 .setProtocol(WMS)
354 .setTitle("Test GeoServer (with authorization rules)")
355 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
356 .setAuthorizationRules(
357 List.of(
358 new AuthorizationRule()
359 .groupName("test-foo")
360 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW)),
361 new AuthorizationRule()
362 .groupName("test-baz")
363 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW))))
364 .setSettings(
365 new GeoServiceSettings()
366 .layerSettings(
367 Map.of(
368 "BGT",
369 new GeoServiceLayerSettings()
370 .addAuthorizationRulesItem(
371 new AuthorizationRule()
372 .groupName("test-foo")
373 .decisions(
374 Map.of(
375 ACCESS_TYPE_READ,
376 AuthorizationRuleDecision.DENY)))
377 .addAuthorizationRulesItem(
378 new AuthorizationRule()
379 .groupName("test-baz")
380 .decisions(
381 Map.of(
382 ACCESS_TYPE_READ,
383 AuthorizationRuleDecision.ALLOW))))))
384 .setPublished(true),
385 new GeoService()
386 .setId("snapshot-geoserver-proxied")
387 .setProtocol(WMS)
388 .setTitle("Test GeoServer (proxied)")
389 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
390 .setAuthorizationRules(ruleAnonymousRead)
391 .setSettings(new GeoServiceSettings().useProxy(true)),
392 new GeoService()
393 .setId("openbasiskaart")
394 .setProtocol(WMTS)
395 .setTitle("Openbasiskaart")
396 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
397 .setAuthorizationRules(ruleAnonymousRead)
398 .setSettings(
399 new GeoServiceSettings()
400 .defaultLayerSettings(
401 new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
402 .layerSettings(
403 Map.of(
404 "osm",
405 new GeoServiceLayerSettings()
406 .title("Openbasiskaart")
407 .hiDpiDisabled(false)
408 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
409 .hiDpiSubstituteLayer("osm-hq")))),
410 new GeoService()
411 .setId("openbasiskaart-proxied")
412 .setProtocol(WMTS)
413 .setTitle("Openbasiskaart (proxied)")
414 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
415 .setAuthorizationRules(ruleAnonymousRead)
416
417
418 .setAuthentication(
419 new ServiceAuthentication()
420 .method(ServiceAuthentication.MethodEnum.PASSWORD)
421 .username("test")
422 .password("test"))
423 .setSettings(
424 new GeoServiceSettings()
425 .useProxy(true)
426 .defaultLayerSettings(
427 new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
428 .layerSettings(
429 Map.of(
430 "osm",
431 new GeoServiceLayerSettings()
432 .hiDpiDisabled(false)
433 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
434 .hiDpiSubstituteLayer("osm-hq")))),
435 new GeoService()
436 .setId("openbasiskaart-tms")
437 .setProtocol(XYZ)
438 .setTitle("Openbasiskaart (TMS)")
439 .setUrl("https://openbasiskaart.nl/mapcache/tms/1.0.0/osm@rd/{z}/{x}/{-y}.png")
440 .setAuthorizationRules(ruleAnonymousRead)
441 .setSettings(
442 new GeoServiceSettings()
443 .xyzCrs("EPSG:28992")
444 .defaultLayerSettings(
445 new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
446 .layerSettings(
447 Map.of(
448 "xyz",
449 new GeoServiceLayerSettings()
450 .maxZoom(15)
451 .tileGridExtent(rdTileGridExtent)
452 .hiDpiDisabled(false)
453 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERTILEPIXELRATIOONLY)
454 .hiDpiSubstituteLayer(
455 "https://openbasiskaart.nl/mapcache/tms/1.0.0/osm-hq@rd-hq/{z}/{x}/{-y}.png")))),
456 new GeoService()
457 .setId("pdok-hwh-luchtfotorgb")
458 .setProtocol(WMTS)
459 .setTitle("PDOK HWH luchtfoto")
460 .setUrl("https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0")
461 .setAuthorizationRules(ruleAnonymousRead)
462 .setPublished(true)
463 .setSettings(
464 new GeoServiceSettings()
465 .defaultLayerSettings(
466 new GeoServiceDefaultLayerSettings()
467 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
468 .hiDpiDisabled(false))
469 .putLayerSettingsItem(
470 "Actueel_orthoHR", new GeoServiceLayerSettings().title("Luchtfoto"))),
471 new GeoService()
472 .setId("b3p-mapproxy-luchtfoto")
473 .setProtocol(XYZ)
474 .setTitle("Luchtfoto (TMS)")
475 .setUrl("https://mapproxy.b3p.nl/tms/1.0.0/luchtfoto/EPSG28992/{z}/{x}/{-y}.jpeg")
476 .setAuthorizationRules(ruleAnonymousRead)
477 .setPublished(true)
478 .setSettings(
479 new GeoServiceSettings()
480 .xyzCrs("EPSG:28992")
481 .defaultLayerSettings(
482 new GeoServiceDefaultLayerSettings()
483 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
484 .hiDpiDisabled(false))
485 .layerSettings(
486 Map.of(
487 "xyz",
488 new GeoServiceLayerSettings()
489 .maxZoom(14)
490 .tileGridExtent(rdTileGridExtent)
491 .hiDpiMode(TileLayerHiDpiMode.SHOWNEXTZOOMLEVEL)))),
492 new GeoService()
493 .setId("at-basemap")
494 .setProtocol(WMTS)
495 .setTitle("basemap.at")
496 .setUrl("https://basemap.at/wmts/1.0.0/WMTSCapabilities.xml")
497 .setAuthorizationRules(ruleAnonymousRead)
498 .setPublished(true)
499 .setSettings(
500 new GeoServiceSettings()
501 .defaultLayerSettings(
502 new GeoServiceDefaultLayerSettings()
503 .attribution("© [basemap.at](https://basemap.at)")
504 .hiDpiDisabled(true))
505 .layerSettings(
506 Map.of(
507 "geolandbasemap",
508 new GeoServiceLayerSettings()
509 .title("Basemap")
510 .hiDpiDisabled(false)
511 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERTILEPIXELRATIOONLY)
512 .hiDpiSubstituteLayer("bmaphidpi"),
513 "bmaporthofoto30cm",
514 new GeoServiceLayerSettings()
515 .title("Orthophoto")
516 .hiDpiDisabled(false)))),
517 new GeoService()
518 .setId("pdok-kadaster-bestuurlijkegebieden")
519 .setProtocol(WMS)
520 .setUrl(
521 "https://service.pdok.nl/kadaster/bestuurlijkegebieden/wms/v1_0?service=WMS")
522 .setAuthorizationRules(ruleAnonymousRead)
523 .setSettings(
524 new GeoServiceSettings()
525 .defaultLayerSettings(
526 new GeoServiceDefaultLayerSettings()
527 .description("This layer shows an administrative boundary."))
528
529 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
530 .useProxy(true)
531 .putLayerSettingsItem(
532 "Gemeentegebied",
533 new GeoServiceLayerSettings().legendImageId(legend.getId().toString())))
534 .setPublished(true)
535 .setTitle("PDOK Kadaster bestuurlijke gebieden"),
536 new GeoService()
537 .setId("bestuurlijkegebieden-proxied")
538 .setProtocol(WMS)
539 .setUrl(
540 "https://service.pdok.nl/kadaster/bestuurlijkegebieden/wms/v1_0?service=WMS")
541 .setAuthorizationRules(ruleAnonymousRead)
542
543
544
545 .setAuthentication(
546 new ServiceAuthentication()
547 .method(ServiceAuthentication.MethodEnum.PASSWORD)
548 .username("test")
549 .password("test"))
550 .setSettings(
551 new GeoServiceSettings()
552
553 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
554 .useProxy(true))
555 .setPublished(true)
556 .setTitle("Bestuurlijke gebieden (proxied met auth)")
557
558 );
559
560 if (map5url != null) {
561 GeoServiceLayerSettings osmAttr = new GeoServiceLayerSettings().attribution(osmAttribution);
562 GeoServiceLayerSettings map5Attr =
563 new GeoServiceLayerSettings()
564 .attribution("Kaarten: [Map5.nl](https://map5.nl), data: " + osmAttribution);
565 services = new ArrayList<>(services);
566 services.add(
567 new GeoService()
568 .setId("map5")
569 .setProtocol(WMTS)
570 .setTitle("Map5")
571 .setUrl(map5url)
572 .setAuthorizationRules(ruleAnonymousRead)
573 .setSettings(
574 new GeoServiceSettings()
575 .defaultLayerSettings(
576 new GeoServiceDefaultLayerSettings().hiDpiDisabled(true))
577 .layerSettings(
578 Map.of(
579 "openlufo",
580 new GeoServiceLayerSettings()
581 .attribution(
582 "© [Beeldmateriaal.nl](https://beeldmateriaal.nl), "
583 + osmAttribution),
584 "luforoadslabels",
585 osmAttr,
586 "map5topo",
587 new GeoServiceLayerSettings()
588 .attribution(map5Attr.getAttribution())
589 .hiDpiDisabled(false)
590 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
591 .hiDpiSubstituteLayer("map5topo_hq"),
592 "map5topo_gray",
593 map5Attr,
594 "map5topo_simple",
595 map5Attr,
596 "map5topo_simple_gray",
597 map5Attr,
598 "opensimpletopo",
599 osmAttr,
600 "opensimpletopo_gray",
601 osmAttr,
602 "opentopo",
603 osmAttr,
604 "opentopo_gray",
605 osmAttr))));
606 }
607
608 for (GeoService geoService : services) {
609 geoServiceHelper.loadServiceCapabilities(geoService);
610
611 geoServiceRepository.save(geoService);
612 catalogNode.addItemsItem(
613 new TailormapObjectRef()
614 .kind(TailormapObjectRef.KindEnum.GEO_SERVICE)
615 .id(geoService.getId()));
616 }
617
618 CatalogNode wfsFeatureSourceCatalogNode =
619 new CatalogNode().id("wfs_feature_sources").title("WFS feature sources");
620 rootCatalogNode.addChildrenItem(wfsFeatureSourceCatalogNode.getId());
621 catalog.getNodes().add(wfsFeatureSourceCatalogNode);
622
623 services.stream()
624 .filter(s -> s.getProtocol() == WMS)
625 .forEach(
626 s -> {
627 geoServiceHelper.findAndSaveRelatedWFS(s);
628 List<TMFeatureSource> linkedSources =
629 featureSourceRepository.findByLinkedServiceId(s.getId());
630 for (TMFeatureSource linkedSource : linkedSources) {
631 wfsFeatureSourceCatalogNode.addItemsItem(
632 new TailormapObjectRef()
633 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
634 .id(linkedSource.getId().toString()));
635 }
636 });
637
638 String geodataPassword = "980f1c8A-25933b2";
639
640 Map<String, TMFeatureSource> featureSources =
641 Map.of(
642 "postgis",
643 new TMFeatureSource()
644 .setProtocol(TMFeatureSource.Protocol.JDBC)
645 .setTitle("PostGIS")
646 .setJdbcConnection(
647 new JDBCConnectionProperties()
648 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
649 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
650 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
651 .database("geodata")
652 .schema("public")
653 .additionalProperties(
654 Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
655 .setAuthentication(
656 new ServiceAuthentication()
657 .method(ServiceAuthentication.MethodEnum.PASSWORD)
658 .username("geodata")
659 .password(geodataPassword)),
660 "postgis_osm",
661 new TMFeatureSource()
662 .setProtocol(TMFeatureSource.Protocol.JDBC)
663 .setTitle("PostGIS OSM")
664 .setJdbcConnection(
665 new JDBCConnectionProperties()
666 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
667 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
668 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
669 .database("geodata")
670 .schema("osm")
671 .additionalProperties(
672 Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
673 .setAuthentication(
674 new ServiceAuthentication()
675 .method(ServiceAuthentication.MethodEnum.PASSWORD)
676 .username("geodata")
677 .password(geodataPassword)),
678 "oracle",
679 new TMFeatureSource()
680 .setProtocol(TMFeatureSource.Protocol.JDBC)
681 .setTitle("Oracle")
682 .setJdbcConnection(
683 new JDBCConnectionProperties()
684 .dbtype(JDBCConnectionProperties.DbtypeEnum.ORACLE)
685 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "oracle")
686 .database("/FREEPDB1")
687 .schema("GEODATA")
688 .additionalProperties(
689 Map.of("connectionOptions", "?oracle.jdbc.J2EE13Compliant=true")))
690 .setAuthentication(
691 new ServiceAuthentication()
692 .method(ServiceAuthentication.MethodEnum.PASSWORD)
693 .username("geodata")
694 .password(geodataPassword)),
695 "sqlserver",
696 new TMFeatureSource()
697 .setProtocol(TMFeatureSource.Protocol.JDBC)
698 .setTitle("MS SQL Server")
699 .setJdbcConnection(
700 new JDBCConnectionProperties()
701 .dbtype(JDBCConnectionProperties.DbtypeEnum.SQLSERVER)
702 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "sqlserver")
703 .database("geodata")
704 .schema("dbo")
705 .additionalProperties(Map.of("connectionOptions", ";encrypt=false")))
706 .setAuthentication(
707 new ServiceAuthentication()
708 .method(ServiceAuthentication.MethodEnum.PASSWORD)
709 .username("geodata")
710 .password(geodataPassword)),
711 "pdok-kadaster-bestuurlijkegebieden",
712 new TMFeatureSource()
713 .setProtocol(TMFeatureSource.Protocol.WFS)
714 .setUrl(
715 "https://service.pdok.nl/kadaster/bestuurlijkegebieden/wfs/v1_0?VERSION=2.0.0")
716 .setTitle("Bestuurlijke gebieden")
717 .setNotes(
718 "Overzicht van de bestuurlijke indeling van Nederland in gemeenten en provincies alsmede de rijksgrens. Gegevens zijn afgeleid uit de Basisregistratie Kadaster (BRK)."));
719 featureSourceRepository.saveAll(featureSources.values());
720
721 new WFSFeatureSourceHelper()
722 .loadCapabilities(featureSources.get("pdok-kadaster-bestuurlijkegebieden"));
723 geoServiceRepository
724 .findById("pdok-kadaster-bestuurlijkegebieden")
725 .ifPresent(
726 geoService -> {
727 geoService
728 .getSettings()
729 .getLayerSettings()
730 .put(
731 "Provinciegebied",
732 new GeoServiceLayerSettings()
733 .description(
734 "The administrative boundary of Dutch Provinces, connected to a WFS.")
735 .featureType(
736 new FeatureTypeRef()
737 .featureSourceId(
738 featureSources
739 .get("pdok-kadaster-bestuurlijkegebieden")
740 .getId())
741 .featureTypeName("bestuurlijkegebieden:Provinciegebied"))
742 .title("Provinciegebied (WFS)"));
743 geoServiceRepository.save(geoService);
744 });
745
746 geoServiceRepository
747 .findById("bestuurlijkegebieden-proxied")
748 .ifPresent(
749 geoService -> {
750 geoService
751 .getSettings()
752 .getLayerSettings()
753 .put(
754 "Provinciegebied",
755 new GeoServiceLayerSettings()
756 .featureType(
757 new FeatureTypeRef()
758 .featureSourceId(
759 featureSources
760 .get("pdok-kadaster-bestuurlijkegebieden")
761 .getId())
762 .featureTypeName("bestuurlijkegebieden:Provinciegebied"))
763 .title("Provinciegebied (WFS, proxied met auth)"));
764 geoServiceRepository.save(geoService);
765 });
766
767 CatalogNode featureSourceCatalogNode =
768 new CatalogNode().id("feature_sources").title("Test feature sources");
769 rootCatalogNode.addChildrenItem(featureSourceCatalogNode.getId());
770 catalog.getNodes().add(featureSourceCatalogNode);
771
772 for (TMFeatureSource featureSource : featureSources.values()) {
773 featureSourceCatalogNode.addItemsItem(
774 new TailormapObjectRef()
775 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
776 .id(featureSource.getId().toString()));
777 }
778 catalogRepository.save(catalog);
779
780 if (connectToSpatialDbs) {
781 featureSources
782 .values()
783 .forEach(
784 fs -> {
785 try {
786 if (fs.getProtocol() == TMFeatureSource.Protocol.JDBC) {
787 new JDBCFeatureSourceHelper().loadCapabilities(fs);
788 } else if (fs.getProtocol() == TMFeatureSource.Protocol.WFS) {
789 new WFSFeatureSourceHelper().loadCapabilities(fs);
790 }
791 } catch (Exception e) {
792 logger.error(
793 "Error loading capabilities for feature source {}", fs.getTitle(), e);
794 }
795 });
796
797 services.stream()
798
799
800 .filter(s -> s.getId().startsWith("snapshot-geoserver"))
801 .forEach(
802 s ->
803 s.getSettings()
804 .layerSettings(
805 Map.of(
806 "postgis:begroeidterreindeel",
807 new GeoServiceLayerSettings()
808 .description(
809 """
810 This layer shows data from https:
811
812 https:
813 .featureType(
814 new FeatureTypeRef()
815 .featureSourceId(featureSources.get("postgis").getId())
816 .featureTypeName("begroeidterreindeel")),
817 "sqlserver:wegdeel",
818 new GeoServiceLayerSettings()
819 .attribution(
820 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)")
821 .description(
822 """
823 This layer shows data from [MS SQL Server](https:
824
825 https:
826 .featureType(
827 new FeatureTypeRef()
828 .featureSourceId(featureSources.get("sqlserver").getId())
829 .featureTypeName("wegdeel")),
830 "oracle:WATERDEEL",
831 new GeoServiceLayerSettings()
832 .description("This layer shows data from Oracle Spatial.")
833 .featureType(
834 new FeatureTypeRef()
835 .featureSourceId(featureSources.get("oracle").getId())
836 .featureTypeName("WATERDEEL")),
837 "postgis:osm_polygon",
838 new GeoServiceLayerSettings()
839 .description("This layer shows OSM data from postgis.")
840 .featureType(
841 new FeatureTypeRef()
842 .featureSourceId(
843 featureSources.get("postgis_osm").getId())
844 .featureTypeName("osm_polygon")))));
845 }
846
847 featureSources.get("pdok-kadaster-bestuurlijkegebieden").getFeatureTypes().stream()
848 .filter(ft -> ft.getName().equals("bestuurlijkegebieden:Provinciegebied"))
849 .findFirst()
850 .ifPresent(
851 ft -> {
852 ft.getSettings().addHideAttributesItem("identificatie");
853 ft.getSettings().addHideAttributesItem("ligtInLandCode");
854 ft.getSettings().addHideAttributesItem("fuuid");
855 ft.getSettings()
856 .putAttributeSettingsItem("naam", new AttributeSettings().title("Naam"));
857 ft.getSettings()
858 .setTemplate(
859 new FeatureTypeTemplate()
860 .templateLanguage("simple")
861 .markupLanguage("markdown")
862 .template(
863 """
864 ### Provincie
865 Deze provincie heet **{{naam}}** en ligt in _{{ligtInLandNaam}}_.
866
867 | Attribuut | Waarde |
868 | --------- | ------------------ |
869 | `code` | {{code}} |
870 | `naam` | {{naam}} |
871 | `ligt in` | {{ligtInLandNaam}} |"""));
872 });
873
874 featureSources.get("postgis").getFeatureTypes().stream()
875 .filter(ft -> ft.getName().equals("begroeidterreindeel"))
876 .findFirst()
877 .ifPresent(
878 ft -> {
879 ft.getSettings().addHideAttributesItem("terminationdate");
880 ft.getSettings().addHideAttributesItem("geom_kruinlijn");
881 ft.getSettings()
882 .putAttributeSettingsItem("gmlid", new AttributeSettings().title("GML ID"));
883 ft.getSettings()
884 .putAttributeSettingsItem(
885 "identificatie", new AttributeSettings().title("Identificatie"));
886 ft.getSettings()
887 .putAttributeSettingsItem(
888 "tijdstipregistratie", new AttributeSettings().title("Registratie"));
889 ft.getSettings()
890 .putAttributeSettingsItem(
891 "eindregistratie", new AttributeSettings().title("Eind registratie"));
892 ft.getSettings()
893 .putAttributeSettingsItem("class", new AttributeSettings().title("Klasse"));
894 ft.getSettings()
895 .putAttributeSettingsItem(
896 "bronhouder", new AttributeSettings().title("Bronhouder"));
897 ft.getSettings()
898 .putAttributeSettingsItem(
899 "inonderzoek", new AttributeSettings().title("In onderzoek"));
900 ft.getSettings()
901 .putAttributeSettingsItem(
902 "relatievehoogteligging",
903 new AttributeSettings().title("Relatieve hoogteligging"));
904 ft.getSettings()
905 .putAttributeSettingsItem(
906 "bgt_status", new AttributeSettings().title("BGT status"));
907 ft.getSettings()
908 .putAttributeSettingsItem(
909 "plus_status", new AttributeSettings().title("Plus-status"));
910 ft.getSettings()
911 .putAttributeSettingsItem(
912 "plus_fysiekvoorkomen",
913 new AttributeSettings().title("Plus-fysiek voorkomen"));
914 ft.getSettings()
915 .putAttributeSettingsItem(
916 "begroeidterreindeeloptalud", new AttributeSettings().title("Op talud"));
917 ft.getSettings().addAttributeOrderItem("identificatie");
918 ft.getSettings().addAttributeOrderItem("bronhouder");
919 ft.getSettings().addAttributeOrderItem("class");
920 });
921 }
922
923 public void createAppTestData() throws Exception {
924 Upload logo =
925 new Upload()
926 .setCategory(Upload.CATEGORY_APP_LOGO)
927 .setFilename("gradient.svg")
928 .setMimeType("image/svg+xml")
929 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
930 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
931 uploadRepository.save(logo);
932
933 List<AppTreeNode> baseNodes =
934 List.of(
935 new AppTreeLayerNode()
936 .objectType("AppTreeLayerNode")
937 .id("lyr:openbasiskaart:osm")
938 .serviceId("openbasiskaart")
939 .layerName("osm")
940 .visible(true),
941 new AppTreeLayerNode()
942 .objectType("AppTreeLayerNode")
943 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
944 .serviceId("pdok-hwh-luchtfotorgb")
945 .layerName("Actueel_orthoHR")
946 .visible(false));
947
948 Application app =
949 new Application()
950 .setName("default")
951 .setTitle("Tailormap demo")
952 .setCrs("EPSG:28992")
953 .setAuthorizationRules(ruleAnonymousRead)
954 .setComponents(
955 List.of(
956 new Component().type("EDIT").config(new ComponentConfig().enabled(true)),
957 new Component()
958 .type("COORDINATE_LINK_WINDOW")
959 .config(
960 new ComponentConfig()
961 .enabled(true)
962 .putAdditionalProperty(
963 "urls",
964 List.of(
965 Map.of(
966 "id",
967 "google-maps",
968 "url",
969 "https://www.google.com/maps/@[lat],[lon],18z",
970 "alias",
971 "Google Maps",
972 "projection",
973 "EPSG:4326"),
974 Map.of(
975 "id",
976 "tm-demo",
977 "url",
978 "https://demo.tailormap.com/#@[X],[Y],18",
979 "alias",
980 "Tailormap demo",
981 "projection",
982 "EPSG:28992"))))))
983 .setContentRoot(
984 new AppContent()
985 .addBaseLayerNodesItem(
986 new AppTreeLevelNode()
987 .objectType("AppTreeLevelNode")
988 .id("root-base-layers")
989 .root(true)
990 .title("Base layers")
991 .childrenIds(
992 List.of(
993 "lyr:openbasiskaart:osm",
994 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
995 "lyr:openbasiskaart-proxied:osm",
996 "lyr:openbasiskaart-tms:xyz",
997 "lyr:b3p-mapproxy-luchtfoto:xyz")))
998 .addBaseLayerNodesItem(
999
1000
1001 new AppTreeLayerNode()
1002 .objectType("AppTreeLayerNode")
1003 .id("lyr:openbasiskaart-proxied:osm")
1004 .serviceId("openbasiskaart-proxied")
1005 .layerName("osm")
1006 .visible(false))
1007 .addBaseLayerNodesItem(
1008 new AppTreeLayerNode()
1009 .objectType("AppTreeLayerNode")
1010 .id("lyr:openbasiskaart-tms:xyz")
1011 .serviceId("openbasiskaart-tms")
1012 .layerName("xyz")
1013 .visible(false))
1014 .addBaseLayerNodesItem(
1015 new AppTreeLayerNode()
1016 .objectType("AppTreeLayerNode")
1017 .id("lyr:b3p-mapproxy-luchtfoto:xyz")
1018 .serviceId("b3p-mapproxy-luchtfoto")
1019 .layerName("xyz")
1020 .visible(false))
1021 .addLayerNodesItem(
1022 new AppTreeLevelNode()
1023 .objectType("AppTreeLevelNode")
1024 .id("root")
1025 .root(true)
1026 .title("Layers")
1027 .childrenIds(
1028 List.of(
1029 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1030 "lyr:bestuurlijkegebieden-proxied:Provinciegebied",
1031 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1032 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1033 "lyr:snapshot-geoserver:sqlserver:wegdeel",
1034 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1035 "lyr:snapshot-geoserver:BGT",
1036 "lvl:proxied",
1037 "lvl:osm",
1038 "lvl:archeo")))
1039 .addLayerNodesItem(
1040 new AppTreeLayerNode()
1041 .objectType("AppTreeLayerNode")
1042 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1043 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1044 .layerName("Provinciegebied")
1045 .visible(true))
1046
1047
1048
1049 .addLayerNodesItem(
1050 new AppTreeLayerNode()
1051 .objectType("AppTreeLayerNode")
1052 .id("lyr:bestuurlijkegebieden-proxied:Provinciegebied")
1053 .serviceId("bestuurlijkegebieden-proxied")
1054 .layerName("Provinciegebied")
1055 .visible(false))
1056 .addLayerNodesItem(
1057 new AppTreeLayerNode()
1058 .objectType("AppTreeLayerNode")
1059 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1060 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1061 .layerName("Gemeentegebied")
1062 .visible(true))
1063 .addLayerNodesItem(
1064 new AppTreeLayerNode()
1065 .objectType("AppTreeLayerNode")
1066 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1067 .serviceId("snapshot-geoserver")
1068 .layerName("postgis:begroeidterreindeel")
1069 .visible(true))
1070 .addLayerNodesItem(
1071 new AppTreeLayerNode()
1072 .objectType("AppTreeLayerNode")
1073 .id("lyr:snapshot-geoserver:sqlserver:wegdeel")
1074 .serviceId("snapshot-geoserver")
1075 .layerName("sqlserver:wegdeel")
1076 .visible(true))
1077 .addLayerNodesItem(
1078 new AppTreeLayerNode()
1079 .objectType("AppTreeLayerNode")
1080 .id("lyr:snapshot-geoserver:oracle:WATERDEEL")
1081 .serviceId("snapshot-geoserver")
1082 .layerName("oracle:WATERDEEL")
1083 .visible(true))
1084 .addLayerNodesItem(
1085 new AppTreeLayerNode()
1086 .objectType("AppTreeLayerNode")
1087 .id("lyr:snapshot-geoserver:BGT")
1088 .serviceId("snapshot-geoserver")
1089 .layerName("BGT")
1090 .visible(false))
1091 .addLayerNodesItem(
1092 new AppTreeLevelNode()
1093 .objectType("AppTreeLevelNode")
1094 .id("lvl:proxied")
1095 .title("Proxied")
1096 .childrenIds(
1097 List.of(
1098 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1099 .addLayerNodesItem(
1100 new AppTreeLayerNode()
1101 .objectType("AppTreeLayerNode")
1102 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1103 .serviceId("snapshot-geoserver-proxied")
1104 .layerName("postgis:begroeidterreindeel")
1105 .visible(false))
1106 .addLayerNodesItem(
1107 new AppTreeLevelNode()
1108 .objectType("AppTreeLevelNode")
1109 .id("lvl:osm")
1110 .title("OSM")
1111 .childrenIds(List.of("lyr:snapshot-geoserver:postgis:osm_polygon")))
1112 .addLayerNodesItem(
1113 new AppTreeLayerNode()
1114 .objectType("AppTreeLayerNode")
1115 .id("lyr:snapshot-geoserver:postgis:osm_polygon")
1116 .serviceId("snapshot-geoserver")
1117 .layerName("postgis:osm_polygon")
1118 .visible(false))
1119 .addLayerNodesItem(
1120 new AppTreeLevelNode()
1121 .objectType("AppTreeLevelNode")
1122 .id("lvl:archeo")
1123 .title("Archeology")
1124 .childrenIds(List.of("lyr:demo:geomorfologie")))
1125 .addLayerNodesItem(
1126 new AppTreeLayerNode()
1127 .objectType("AppTreeLayerNode")
1128 .id("lyr:demo:geomorfologie")
1129 .serviceId("demo")
1130 .layerName("geomorfologie")
1131 .visible(true)))
1132 .setStyling(new AppStyling().logo(logo.getId().toString()))
1133 .setSettings(
1134 new AppSettings()
1135 .putLayerSettingsItem(
1136 "lyr:openbasiskaart:osm", new AppLayerSettings().title("Openbasiskaart"))
1137 .putLayerSettingsItem(
1138 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1139 new AppLayerSettings().title("Luchtfoto"))
1140 .putLayerSettingsItem(
1141 "lyr:openbasiskaart-proxied:osm",
1142 new AppLayerSettings().title("Openbasiskaart (proxied)"))
1143 .putLayerSettingsItem(
1144 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1145 new AppLayerSettings()
1146 .opacity(50)
1147 .title("Waterdeel overridden title")
1148 .editable(true)
1149 .description(
1150 "This is the layer description from the app layer setting.")
1151 .attribution(
1152 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)"))
1153 .putLayerSettingsItem(
1154 "lyr:snapshot-geoserver:postgis:osm_polygon",
1155 new AppLayerSettings()
1156 .description("OpenStreetMap polygon data in EPSG:3857")
1157 .opacity(60)
1158 .editable(true)
1159 .title("OSM Polygon (EPSG:3857)")
1160 .attribution(
1161 "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors"))
1162 .putLayerSettingsItem(
1163 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1164 new AppLayerSettings()
1165 .editable(true)
1166 .addHideAttributesItem("begroeidterreindeeloptalud")
1167 .addReadOnlyAttributesItem("eindregistratie"))
1168 .putLayerSettingsItem(
1169 "lyr:snapshot-geoserver:sqlserver:wegdeel",
1170 new AppLayerSettings().editable(true))
1171 .putLayerSettingsItem(
1172 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1173 new AppLayerSettings().editable(false)));
1174
1175 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1176 app.setInitialExtent(new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d));
1177 app.setMaxExtent(new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d));
1178
1179 if (map5url != null) {
1180 AppTreeLevelNode root = (AppTreeLevelNode) app.getContentRoot().getBaseLayerNodes().get(0);
1181 List<String> childrenIds = new ArrayList<>(root.getChildrenIds());
1182 childrenIds.add("lyr:map5:map5topo");
1183 childrenIds.add("lyr:map5:map5topo_simple");
1184 childrenIds.add("lvl:luchtfoto-labels");
1185 root.setChildrenIds(childrenIds);
1186 app.getSettings()
1187 .putLayerSettingsItem("lyr:map5:map5topo", new AppLayerSettings().title("Map5"))
1188 .putLayerSettingsItem(
1189 "lyr:map5:map5topo_simple", new AppLayerSettings().title("Map5 simple"));
1190 app.getContentRoot()
1191 .addBaseLayerNodesItem(
1192 new AppTreeLayerNode()
1193 .objectType("AppTreeLayerNode")
1194 .id("lyr:map5:map5topo")
1195 .serviceId("map5")
1196 .layerName("map5topo")
1197 .visible(false))
1198 .addBaseLayerNodesItem(
1199 new AppTreeLayerNode()
1200 .objectType("AppTreeLayerNode")
1201 .id("lyr:map5:map5topo_simple")
1202 .serviceId("map5")
1203 .layerName("map5topo_simple")
1204 .visible(false))
1205 .addBaseLayerNodesItem(
1206 new AppTreeLevelNode()
1207 .objectType("AppTreeLevelNode")
1208 .id("lvl:luchtfoto-labels")
1209 .title("Luchtfoto met labels")
1210 .addChildrenIdsItem("lyr:map5:luforoadslabels")
1211 .addChildrenIdsItem("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2"))
1212 .addBaseLayerNodesItem(
1213 new AppTreeLayerNode()
1214 .objectType("AppTreeLayerNode")
1215 .id("lyr:map5:luforoadslabels")
1216 .serviceId("map5")
1217 .layerName("luforoadslabels")
1218 .visible(false))
1219 .addBaseLayerNodesItem(
1220 new AppTreeLayerNode()
1221 .objectType("AppTreeLayerNode")
1222 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2")
1223 .serviceId("pdok-hwh-luchtfotorgb")
1224 .layerName("Actueel_orthoHR")
1225 .visible(false));
1226 }
1227
1228 applicationRepository.save(app);
1229
1230 app =
1231 new Application()
1232 .setName("base")
1233 .setTitle("Service base app")
1234 .setCrs("EPSG:28992")
1235 .setAuthorizationRules(ruleAnonymousRead)
1236 .setContentRoot(
1237 new AppContent()
1238 .addBaseLayerNodesItem(
1239 new AppTreeLevelNode()
1240 .objectType("AppTreeLevelNode")
1241 .id("root-base-layers")
1242 .root(true)
1243 .title("Base layers")
1244 .childrenIds(
1245 List.of(
1246 "lyr:openbasiskaart:osm",
1247 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR"))));
1248 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1249 applicationRepository.save(app);
1250
1251 app =
1252 new Application()
1253 .setName("secured")
1254 .setTitle("secured app")
1255 .setCrs("EPSG:28992")
1256 .setAuthorizationRules(ruleLoggedIn)
1257 .setContentRoot(
1258 new AppContent()
1259 .addBaseLayerNodesItem(
1260 new AppTreeLevelNode()
1261 .objectType("AppTreeLevelNode")
1262 .id("root-base-layers")
1263 .root(true)
1264 .title("Base layers")
1265 .childrenIds(
1266 List.of(
1267 "lyr:openbasiskaart:osm",
1268 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1269 "lyr:openbasiskaart-proxied:osm")))
1270 .addBaseLayerNodesItem(
1271 new AppTreeLayerNode()
1272 .objectType("AppTreeLayerNode")
1273 .id("lyr:openbasiskaart-proxied:osm")
1274 .serviceId("openbasiskaart-proxied")
1275 .layerName("osm")
1276 .visible(false))
1277 .addLayerNodesItem(
1278 new AppTreeLevelNode()
1279 .objectType("AppTreeLevelNode")
1280 .id("root")
1281 .root(true)
1282 .title("Layers")
1283 .childrenIds(
1284 List.of(
1285 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1286 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1287 "lvl:proxied")))
1288 .addLayerNodesItem(
1289 new AppTreeLayerNode()
1290 .objectType("AppTreeLayerNode")
1291 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1292 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1293 .layerName("Gemeentegebied")
1294 .visible(true))
1295 .addLayerNodesItem(
1296 new AppTreeLayerNode()
1297 .objectType("AppTreeLayerNode")
1298 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1299 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1300 .layerName("Provinciegebied")
1301 .visible(false))
1302 .addLayerNodesItem(
1303 new AppTreeLevelNode()
1304 .objectType("AppTreeLevelNode")
1305 .id("lvl:proxied")
1306 .title("Proxied")
1307 .childrenIds(
1308 List.of(
1309 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1310 .addLayerNodesItem(
1311 new AppTreeLayerNode()
1312 .objectType("AppTreeLayerNode")
1313 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1314 .serviceId("snapshot-geoserver-proxied")
1315 .layerName("postgis:begroeidterreindeel")
1316 .visible(false)))
1317 .setSettings(
1318 new AppSettings()
1319 .putLayerSettingsItem(
1320 "lyr:openbasiskaart-proxied:osm",
1321 new AppLayerSettings().title("Openbasiskaart (proxied)")));
1322
1323 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1324 applicationRepository.save(app);
1325
1326 app =
1327 new Application()
1328 .setName("secured-auth")
1329 .setTitle("secured (with authorizations)")
1330 .setCrs("EPSG:28992")
1331 .setAuthorizationRules(
1332 List.of(
1333 new AuthorizationRule()
1334 .groupName("test-foo")
1335 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW)),
1336 new AuthorizationRule()
1337 .groupName("test-bar")
1338 .decisions(Map.of(ACCESS_TYPE_READ, AuthorizationRuleDecision.ALLOW))))
1339 .setContentRoot(
1340 new AppContent()
1341 .addLayerNodesItem(
1342 new AppTreeLevelNode()
1343 .objectType("AppTreeLevelNode")
1344 .id("root")
1345 .root(true)
1346 .title("Layers")
1347 .childrenIds(List.of("lyr:needs-auth", "lyr:public")))
1348 .addLayerNodesItem(
1349 new AppTreeLevelNode()
1350 .objectType("AppTreeLevelNode")
1351 .id("lvl:public")
1352 .title("Public")
1353 .childrenIds(List.of("lyr:snapshot-geoserver:BGT")))
1354 .addLayerNodesItem(
1355 new AppTreeLevelNode()
1356 .objectType("AppTreeLevelNode")
1357 .id("lvl:needs-auth")
1358 .title("Needs auth")
1359 .childrenIds(
1360 List.of(
1361 "lyr:filtered-snapshot-geoserver:BGT",
1362 "lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1363 .addLayerNodesItem(
1364 new AppTreeLayerNode()
1365 .objectType("AppTreeLayerNode")
1366 .id("lyr:filtered-snapshot-geoserver:BGT")
1367 .serviceId("filtered-snapshot-geoserver")
1368 .layerName("BGT")
1369 .visible(true))
1370 .addLayerNodesItem(
1371 new AppTreeLayerNode()
1372 .objectType("AppTreeLayerNode")
1373 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1374 .serviceId("filtered-snapshot-geoserver")
1375 .layerName("postgis:begroeidterreindeel")
1376 .visible(true))
1377 .addLayerNodesItem(
1378 new AppTreeLayerNode()
1379 .objectType("AppTreeLayerNode")
1380 .id("lyr:snapshot-geoserver:BGT")
1381 .serviceId("snapshot-geoserver")
1382 .layerName("BGT")
1383 .visible(true)));
1384
1385 applicationRepository.save(app);
1386
1387 app =
1388 new Application()
1389 .setName("austria")
1390 .setCrs("EPSG:3857")
1391 .setAuthorizationRules(ruleAnonymousRead)
1392 .setTitle("Austria")
1393 .setInitialExtent(
1394 new Bounds().minx(987982d).miny(5799551d).maxx(1963423d).maxy(6320708d))
1395 .setMaxExtent(new Bounds().minx(206516d).miny(5095461d).maxx(3146930d).maxy(7096232d))
1396 .setContentRoot(
1397 new AppContent()
1398 .addBaseLayerNodesItem(
1399 new AppTreeLevelNode()
1400 .objectType("AppTreeLevelNode")
1401 .id("root-base-layers")
1402 .root(true)
1403 .title("Base layers")
1404 .childrenIds(
1405 List.of(
1406 "lyr:at-basemap:geolandbasemap",
1407 "lyr:at-basemap:orthofoto",
1408 "lvl:orthofoto-labels",
1409 "lyr:osm:xyz")))
1410 .addBaseLayerNodesItem(
1411 new AppTreeLayerNode()
1412 .objectType("AppTreeLayerNode")
1413 .id("lyr:at-basemap:geolandbasemap")
1414 .serviceId("at-basemap")
1415 .layerName("geolandbasemap")
1416 .visible(true))
1417 .addBaseLayerNodesItem(
1418 new AppTreeLayerNode()
1419 .objectType("AppTreeLayerNode")
1420 .id("lyr:at-basemap:orthofoto")
1421 .serviceId("at-basemap")
1422 .layerName("bmaporthofoto30cm")
1423 .visible(false))
1424 .addBaseLayerNodesItem(
1425 new AppTreeLevelNode()
1426 .objectType("AppTreeLevelNode")
1427 .id("lvl:orthofoto-labels")
1428 .title("Orthophoto with labels")
1429 .childrenIds(
1430 List.of(
1431 "lyr:at-basemap:bmapoverlay", "lyr:at-basemap:orthofoto_2")))
1432 .addBaseLayerNodesItem(
1433 new AppTreeLayerNode()
1434 .objectType("AppTreeLayerNode")
1435 .id("lyr:at-basemap:bmapoverlay")
1436 .serviceId("at-basemap")
1437 .layerName("bmapoverlay")
1438 .visible(false))
1439 .addBaseLayerNodesItem(
1440 new AppTreeLayerNode()
1441 .objectType("AppTreeLayerNode")
1442 .id("lyr:at-basemap:orthofoto_2")
1443 .serviceId("at-basemap")
1444 .layerName("bmaporthofoto30cm")
1445 .visible(false))
1446 .addBaseLayerNodesItem(
1447 new AppTreeLayerNode()
1448 .objectType("AppTreeLayerNode")
1449 .id("lyr:osm:xyz")
1450 .serviceId("osm")
1451 .layerName("xyz")
1452 .visible(false)));
1453
1454 applicationRepository.save(app);
1455
1456 Configuration config = new Configuration();
1457 config.setKey(Configuration.DEFAULT_APP);
1458 config.setValue("default");
1459 configurationRepository.save(config);
1460 config = new Configuration();
1461 config.setKey(Configuration.DEFAULT_BASE_APP);
1462 config.setValue("base");
1463 configurationRepository.save(config);
1464 }
1465
1466 private void createConfigurationTestData() throws JsonProcessingException {
1467 Configuration config = new Configuration();
1468 config.setKey("test");
1469 config.setAvailableForViewer(true);
1470 config.setValue("test value");
1471 config.setJsonValue(
1472 new ObjectMapper().readTree("{ \"someProperty\": 1, \"nestedObject\": { \"num\": 42 } }"));
1473 configurationRepository.save(config);
1474 }
1475
1476 @Transactional
1477 public void createSolrIndex() throws Exception {
1478 if (connectToSpatialDbs) {
1479
1480
1481 featureSourceRepository.flush();
1482
1483 logger.info("Creating Solr index");
1484 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
1485 final String solrUrl =
1486 "http://" + (connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "solr") + ":8983/solr/";
1487 this.solrService.setSolrUrl(solrUrl);
1488 SolrHelper solrHelper =
1489 new SolrHelper(this.solrService.getSolrClientForIndexing())
1490 .withBatchSize(solrBatchSize)
1491 .withGeometryValidationRule(solrGeometryValidationRule);
1492 GeoService geoService = geoServiceRepository.findById("snapshot-geoserver").orElseThrow();
1493 Application defaultApp = applicationRepository.findByName("default");
1494
1495 TMFeatureType begroeidterreindeelFT =
1496 geoService.findFeatureTypeForLayer(
1497 geoService.findLayer("postgis:begroeidterreindeel"), featureSourceRepository);
1498
1499 TMFeatureType wegdeelFT =
1500 geoService.findFeatureTypeForLayer(
1501 geoService.findLayer("sqlserver:wegdeel"), featureSourceRepository);
1502
1503 try (solrHelper) {
1504 SearchIndex begroeidterreindeelIndex = null;
1505 if (begroeidterreindeelFT != null) {
1506 begroeidterreindeelIndex =
1507 new SearchIndex()
1508 .setName("Begroeidterreindeel")
1509 .setFeatureTypeId(begroeidterreindeelFT.getId())
1510 .setSearchFieldsUsed(List.of("class", "plus_fysiekvoorkomen", "bronhouder"))
1511 .setSearchDisplayFieldsUsed(List.of("class", "plus_fysiekvoorkomen"));
1512 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1513 begroeidterreindeelIndex =
1514 solrHelper.addFeatureTypeIndex(
1515 begroeidterreindeelIndex,
1516 begroeidterreindeelFT,
1517 featureSourceFactoryHelper,
1518 searchIndexRepository);
1519 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1520 }
1521
1522 SearchIndex wegdeelIndex = null;
1523 if (wegdeelFT != null) {
1524 wegdeelIndex =
1525 new SearchIndex()
1526 .setName("Wegdeel")
1527 .setFeatureTypeId(wegdeelFT.getId())
1528 .setSearchFieldsUsed(
1529 List.of(
1530 "function_",
1531 "plus_fysiekvoorkomenwegdeel",
1532 "surfacematerial",
1533 "bronhouder"))
1534 .setSearchDisplayFieldsUsed(List.of("function_", "plus_fysiekvoorkomenwegdeel"));
1535 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1536 wegdeelIndex =
1537 solrHelper.addFeatureTypeIndex(
1538 wegdeelIndex, wegdeelFT, featureSourceFactoryHelper, searchIndexRepository);
1539 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1540
1541 featureSourceRepository
1542 .getByTitle("PostGIS")
1543 .flatMap(
1544 fs ->
1545 fs.getFeatureTypes().stream()
1546 .filter(ft -> ft.getName().equals("bak"))
1547 .findFirst())
1548 .ifPresent(
1549 ft -> {
1550 SearchIndex bak =
1551 new SearchIndex()
1552 .setName("bak")
1553 .setFeatureTypeId(ft.getId())
1554 .setSearchFieldsUsed(List.of("gmlid", "identificatie", "plus_type"))
1555 .setSearchDisplayFieldsUsed(List.of("gmlid", "plus_type"));
1556 searchIndexRepository.save(bak);
1557 try {
1558 bak =
1559 solrHelper.addFeatureTypeIndex(
1560 bak, ft, featureSourceFactoryHelper, searchIndexRepository);
1561 searchIndexRepository.save(bak);
1562 } catch (IOException | SolrServerException e) {
1563 throw new RuntimeException(e);
1564 }
1565 });
1566 }
1567
1568 AppTreeLayerNode begroeidTerreindeelLayerNode =
1569 defaultApp
1570 .getAllAppTreeLayerNode()
1571 .filter(
1572 node ->
1573 node.getId().equals("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1574 .findFirst()
1575 .orElse(null);
1576
1577 if (begroeidTerreindeelLayerNode != null && begroeidterreindeelIndex != null) {
1578 defaultApp
1579 .getAppLayerSettings(begroeidTerreindeelLayerNode)
1580 .setSearchIndexId(begroeidterreindeelIndex.getId());
1581 }
1582
1583 AppTreeLayerNode wegdeel =
1584 defaultApp
1585 .getAllAppTreeLayerNode()
1586 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:sqlserver:wegdeel"))
1587 .findFirst()
1588 .orElse(null);
1589
1590 if (wegdeel != null && wegdeelIndex != null) {
1591 defaultApp.getAppLayerSettings(wegdeel).setSearchIndexId(wegdeelIndex.getId());
1592 }
1593
1594 applicationRepository.save(defaultApp);
1595 }
1596 }
1597 }
1598
1599 private void createPocTasks() {
1600
1601 try {
1602 logger.info("Creating POC tasks");
1603 logger.info(
1604 "Created 15 minutely task with key: {}",
1605 taskManagerService.createTask(
1606 PocTask.class,
1607 new TMJobDataMap(
1608 Map.of(
1609 Task.TYPE_KEY,
1610 TaskType.POC.getValue(),
1611 "foo",
1612 "foobar",
1613 Task.DESCRIPTION_KEY,
1614 "POC task that runs every 15 minutes")),
1615 "0 0/15 * 1/1 * ? *"));
1616 logger.info(
1617 "Created hourly task with key: {}",
1618 taskManagerService.createTask(
1619 PocTask.class,
1620 new TMJobDataMap(
1621 Map.of(
1622 Task.TYPE_KEY,
1623 TaskType.POC.getValue(),
1624 "foo",
1625 "bar",
1626 Task.DESCRIPTION_KEY,
1627 "POC task that runs every hour",
1628 Task.PRIORITY_KEY,
1629 10)),
1630 "0 0 0/1 1/1 * ? *"));
1631
1632 logger.info(
1633 "Created hourly failing task with key: {}",
1634 taskManagerService.createTask(
1635 FailingPocTask.class,
1636 new TMJobDataMap(
1637 Map.of(
1638 Task.TYPE_KEY,
1639 TaskType.FAILINGPOC.getValue(),
1640 Task.DESCRIPTION_KEY,
1641 "POC task that fails every hour with low priority",
1642 Task.PRIORITY_KEY,
1643 100)),
1644 "0 0 0/1 1/1 * ? *"));
1645 logger.info(
1646 "Created daily task with key: {}",
1647 taskManagerService.createTask(
1648 InterruptablePocTask.class,
1649 new TMJobDataMap(
1650 Map.of(
1651 Task.TYPE_KEY,
1652 TaskType.INTERRUPTABLEPOC.getValue(),
1653 Task.DESCRIPTION_KEY,
1654 "Interruptable POC task that runs every 15 minutes",
1655 Task.PRIORITY_KEY,
1656 5)),
1657 "0 0/15 * 1/1 * ? *"));
1658 } catch (SchedulerException e) {
1659 logger.error("Error creating scheduled one or more poc tasks", e);
1660 }
1661
1662 if (categories.contains("search-index")) {
1663 logger.info("Creating INDEX task");
1664 searchIndexRepository
1665 .findByName("Begroeidterreindeel")
1666 .ifPresent(
1667 index -> {
1668 index.setSchedule(
1669 new TaskSchedule()
1670
1671 .cronExpression("0 0 0/1 1/1 * ? *")
1672
1673
1674 .description("Update Solr index \"Begroeidterreindeel\" every time"));
1675 try {
1676 final UUID uuid =
1677 taskManagerService.createTask(
1678 IndexTask.class,
1679 new TMJobDataMap(
1680 Map.of(
1681 Task.TYPE_KEY,
1682 TaskType.INDEX,
1683 Task.DESCRIPTION_KEY,
1684 index.getSchedule().getDescription(),
1685 IndexTask.INDEX_KEY,
1686 index.getId().toString(),
1687 Task.PRIORITY_KEY,
1688 10)),
1689 index.getSchedule().getCronExpression());
1690
1691 index.getSchedule().setUuid(uuid);
1692 searchIndexRepository.save(index);
1693
1694 logger.info("Created task to update Solr index with key: {}", uuid);
1695 } catch (SchedulerException e) {
1696 logger.error("Error creating scheduled solr index task", e);
1697 }
1698 });
1699 }
1700 }
1701
1702 private void createPages() throws IOException {
1703 Upload logo =
1704 new Upload()
1705 .setCategory(Upload.CATEGORY_PORTAL_IMAGE)
1706 .setFilename("gradient.svg")
1707 .setMimeType("image/svg+xml")
1708 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
1709 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
1710 uploadRepository.save(logo);
1711
1712 Page about = new Page();
1713 about.setName("about");
1714 about.setType("page");
1715 about.setContent("About Tailormap");
1716 about.setContent(
1717 """
1718 # About Tailormap
1719
1720 This is a page about *Tailormap*. It doesn't say much yet.
1721 """);
1722 pageRepository.save(about);
1723
1724 Page page = new Page();
1725 page.setName("home");
1726 page.setType("page");
1727 page.setTitle("Tailormap - Home");
1728 page.setContent(
1729 """
1730 # Welcome to Tailormap!
1731
1732 This page is only visible when you implement a frontend to display pages, or get it (including a simple CMS)
1733 from [B3Partners](https:
1734 """);
1735 page.setClassName(null);
1736 page.setTiles(
1737 List.of(
1738 new PageTile()
1739 .id(UUID.randomUUID().toString())
1740 .title("Default app")
1741 .applicationId(
1742 Optional.ofNullable(applicationRepository.findByName("default"))
1743 .map(Application::getId)
1744 .orElse(null))
1745 .image(logo.getId().toString())
1746 .content("*Default app* tile content")
1747 .filterRequireAuthorization(false)
1748 .openInNewWindow(false),
1749 new PageTile()
1750 .id(UUID.randomUUID().toString())
1751 .title("Secured app")
1752 .applicationId(
1753 Optional.ofNullable(applicationRepository.findByName("secured"))
1754 .map(Application::getId)
1755 .orElse(null))
1756 .filterRequireAuthorization(true)
1757 .content("Secure app, only shown if user has authorization")
1758 .openInNewWindow(false),
1759 new PageTile()
1760 .id(UUID.randomUUID().toString())
1761 .title("Secured app (unfiltered)")
1762 .applicationId(
1763 Optional.ofNullable(applicationRepository.findByName("secured"))
1764 .map(Application::getId)
1765 .orElse(null))
1766 .filterRequireAuthorization(false)
1767 .content("Secure app, tile shown to everyone")
1768 .openInNewWindow(false),
1769 new PageTile()
1770 .id(UUID.randomUUID().toString())
1771 .title("About")
1772 .pageId(about.getId())
1773 .openInNewWindow(false),
1774 new PageTile()
1775 .id(UUID.randomUUID().toString())
1776 .title("B3Partners")
1777 .url("https://www.b3partners.nl/")
1778 .openInNewWindow(true)));
1779 pageRepository.save(page);
1780
1781 Configuration c = new Configuration();
1782 c.setKey(HOME_PAGE);
1783 c.setValue(page.getId().toString());
1784 configurationRepository.save(c);
1785
1786 List<MenuItem> globalMenuItems =
1787 List.of(
1788 new MenuItem().pageId(about.getId()).label("About").openInNewWindow(false),
1789 new MenuItem()
1790 .label("B3Partners website")
1791 .url("https://www.b3partners.nl/")
1792 .openInNewWindow(true)
1793 .exclusiveOnPageId(about.getId()));
1794 c = new Configuration();
1795 c.setKey(PORTAL_MENU);
1796 c.setJsonValue(new ObjectMapper().valueToTree(globalMenuItems));
1797 configurationRepository.save(c);
1798 }
1799 }