Skip to content

Commit 0f8df85

Browse files
authored
Sitemap DSL serialization and parsing (openhab#5459)
Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
1 parent 9fd8692 commit 0f8df85

30 files changed

Lines changed: 2158 additions & 136 deletions

File tree

bundles/org.openhab.core.io.rest.core/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
<artifactId>org.openhab.core.persistence</artifactId>
4646
<version>${project.version}</version>
4747
</dependency>
48+
<dependency>
49+
<groupId>org.openhab.core.bundles</groupId>
50+
<artifactId>org.openhab.core.sitemap</artifactId>
51+
<version>${project.version}</version>
52+
</dependency>
4853
<dependency>
4954
<groupId>org.openhab.core.bundles</groupId>
5055
<artifactId>org.openhab.core.config.discovery</artifactId>

bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/fileformat/FileFormatDTO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.util.List;
1616

17+
import org.openhab.core.sitemap.dto.SitemapDefinitionDTO;
1718
import org.openhab.core.thing.dto.ThingDTO;
1819

1920
import io.swagger.v3.oas.annotations.media.Schema;
@@ -23,6 +24,7 @@
2324
* in a file format (items, things, ...).
2425
*
2526
* @author Laurent Garnier - Initial contribution
27+
* @author Mark Herwege - Add sitemaps
2628
*/
2729
@Schema(name = "FileFormat")
2830
public class FileFormatDTO {
@@ -31,4 +33,6 @@ public class FileFormatDTO {
3133
public List<FileFormatItemDTO> items;
3234
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED)
3335
public List<ThingDTO> things;
36+
@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED)
37+
public List<SitemapDefinitionDTO> sitemaps;
3438
}

bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/fileformat/FileFormatResource.java

Lines changed: 178 additions & 8 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.core.io.rest.sitemap.internal;
14+
15+
import org.openhab.core.sitemap.dto.SitemapDefinitionDTO;
16+
17+
import io.swagger.v3.oas.annotations.media.Schema;
18+
19+
/**
20+
* This is a data transfer object that is used to serialize sitemaps to represent or edit in the UI.
21+
*
22+
* @author Mark Herwege - Initial contribution
23+
*/
24+
@Schema(name = "EnrichedSitemapDefinition")
25+
public class EnrichedSitemapDefinitionDTO extends SitemapDefinitionDTO {
26+
27+
public boolean editable;
28+
29+
public EnrichedSitemapDefinitionDTO(SitemapDefinitionDTO dto) {
30+
this.name = dto.name;
31+
this.label = dto.label;
32+
this.icon = dto.icon;
33+
this.widgets = dto.widgets;
34+
}
35+
}

bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapDTO.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,19 @@
1212
*/
1313
package org.openhab.core.io.rest.sitemap.internal;
1414

15+
import org.openhab.core.sitemap.dto.AbstractSitemapDTO;
16+
1517
import io.swagger.v3.oas.annotations.media.Schema;
1618

1719
/**
1820
* This is a data transfer object that is used to serialize sitemaps.
19-
*
21+
*
2022
* @author Kai Kreuzer - Initial contribution
2123
* @author Chris Jackson - Initial contribution
24+
* @author Mark Herwege - Moved to abstract class and extend
2225
*/
2326
@Schema(name = "Sitemap")
24-
public class SitemapDTO {
25-
26-
public String name;
27-
public String icon;
28-
public String label;
27+
public class SitemapDTO extends AbstractSitemapDTO {
2928

3029
public String link;
3130

bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java

Lines changed: 146 additions & 22 deletions
Large diffs are not rendered by default.

bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetDTO.java

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
*/
1313
package org.openhab.core.io.rest.sitemap.internal;
1414

15-
import java.math.BigDecimal;
1615
import java.util.ArrayList;
1716
import java.util.List;
1817

1918
import org.openhab.core.io.rest.core.item.EnrichedItemDTO;
19+
import org.openhab.core.sitemap.dto.AbstractWidgetDTO;
2020

2121
import io.swagger.v3.oas.annotations.media.Schema;
2222

@@ -31,54 +31,22 @@
3131
* @author Danny Baumann - New field labelSource
3232
* @author Laurent Garnier - Remove field columns
3333
* @author Laurent Garnier - New fields row, column, command, releaseCommand and stateless for Button element
34+
* @author Mark Herwege - Extends abstract widget DTO
3435
*/
3536
@Schema(name = "Widget")
36-
public class WidgetDTO {
37+
public class WidgetDTO extends AbstractWidgetDTO {
3738

3839
public String widgetId;
39-
public String type;
40-
public String name;
4140
public boolean visibility;
42-
43-
public String label;
4441
public String labelSource;
45-
public String icon;
46-
/**
47-
* staticIcon is a boolean indicating if the widget state must be ignored when requesting the icon.
48-
* It is set to true when the widget has either the staticIcon property set or the icon property set
49-
* with conditional rules.
50-
*/
51-
public Boolean staticIcon;
42+
5243
public String labelcolor;
5344
public String valuecolor;
5445
public String iconcolor;
5546

5647
public String pattern;
5748
public String unit;
5849

59-
// widget-specific attributes
60-
public final List<MappingDTO> mappings = new ArrayList<>();
61-
public Boolean switchSupport;
62-
public Boolean releaseOnly;
63-
public Integer refresh;
64-
public Integer height;
65-
public BigDecimal minValue;
66-
public BigDecimal maxValue;
67-
public BigDecimal step;
68-
public String inputHint;
69-
public String url;
70-
public String encoding;
71-
public String service;
72-
public String period;
73-
public String yAxisDecimalPattern;
74-
public String interpolation;
75-
public Boolean legend;
76-
public Boolean forceAsItem;
77-
public Integer row;
78-
public Integer column;
79-
public String command;
80-
public String releaseCommand;
81-
public Boolean stateless;
8250
public String state;
8351

8452
public EnrichedItemDTO item;

bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static org.hamcrest.CoreMatchers.*;
1616
import static org.hamcrest.MatcherAssert.assertThat;
1717
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
18+
import static org.mockito.ArgumentMatchers.any;
1819
import static org.mockito.Mockito.*;
1920

2021
import java.util.ArrayList;
@@ -52,10 +53,14 @@
5253
import org.openhab.core.sitemap.Rule;
5354
import org.openhab.core.sitemap.Sitemap;
5455
import org.openhab.core.sitemap.Widget;
56+
import org.openhab.core.sitemap.dto.SitemapDefinitionDTO;
57+
import org.openhab.core.sitemap.internal.SitemapImpl;
58+
import org.openhab.core.sitemap.registry.SitemapFactory;
5559
import org.openhab.core.sitemap.registry.SitemapRegistry;
5660
import org.openhab.core.test.java.JavaTest;
5761
import org.openhab.core.types.Command;
5862
import org.openhab.core.types.State;
63+
import org.openhab.core.ui.components.ManagedSitemapProvider;
5964
import org.openhab.core.ui.items.ItemUIRegistry;
6065
import org.openhab.core.ui.items.ItemUIRegistry.WidgetLabelSource;
6166
import org.osgi.framework.BundleContext;
@@ -116,7 +121,9 @@ public class SitemapResourceTest extends JavaTest {
116121
private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock;
117122
private @Mock @NonNullByDefault({}) LocaleService localeServiceMock;
118123
private @Mock @NonNullByDefault({}) HttpServletRequest requestMock;
124+
private @Mock @NonNullByDefault({}) SitemapFactory sitemapFactory;
119125
private @Mock @NonNullByDefault({}) SitemapRegistry sitemapRegistryMock;
126+
private @Mock @NonNullByDefault({}) ManagedSitemapProvider managedSitemapProviderMock;
120127
private @Mock @NonNullByDefault({}) UriInfo uriInfoMock;
121128
private @Mock @NonNullByDefault({}) BundleContext bundleContextMock;
122129

@@ -127,8 +134,8 @@ public void setup() throws Exception {
127134
subscriptions = new SitemapSubscriptionService(Collections.emptyMap(), itemUIRegistryMock, sitemapRegistryMock,
128135
timeZoneProviderMock, bundleContextMock);
129136

130-
sitemapResource = new SitemapResource(itemUIRegistryMock, sitemapRegistryMock, localeServiceMock,
131-
timeZoneProviderMock, subscriptions);
137+
sitemapResource = new SitemapResource(itemUIRegistryMock, sitemapFactory, sitemapRegistryMock,
138+
managedSitemapProviderMock, localeServiceMock, timeZoneProviderMock, subscriptions);
132139

133140
when(uriInfoMock.getAbsolutePathBuilder()).thenReturn(UriBuilder.fromPath(SITEMAP_PATH));
134141
when(uriInfoMock.getBaseUriBuilder()).thenReturn(UriBuilder.fromPath(SITEMAP_PATH));
@@ -156,6 +163,103 @@ public void setup() throws Exception {
156163
when(headersMock.getRequestHeader(HTTP_HEADER_X_ATMOSPHERE_TRANSPORT)).thenReturn(null);
157164
}
158165

166+
@Test
167+
public void whenGetSitemapsDefinitionShouldSetEditableFlag() {
168+
// sitemapRegistryMock.getAll() already returns Set.of(defaultSitemapMock) via configureSitemapRegistryMock
169+
// This test will have that sitemap be a managed sitemap
170+
when(managedSitemapProviderMock.get(SITEMAP_NAME)).thenReturn(new SitemapImpl(SITEMAP_NAME));
171+
172+
Response resp = sitemapResource.getSitemapsDefinition();
173+
assertThat(resp.getStatus(), is(200));
174+
175+
@SuppressWarnings("unchecked")
176+
List<EnrichedSitemapDefinitionDTO> body = (List<EnrichedSitemapDefinitionDTO>) resp.getEntity();
177+
assertThat(body, hasSize(1));
178+
assertThat(body.get(0).name, is(SITEMAP_NAME));
179+
assertThat(body.get(0).editable, is(true));
180+
}
181+
182+
@Test
183+
public void whenGetSitemapDefinitionNotFoundShouldReturn404() {
184+
when(sitemapRegistryMock.get("noexist")).thenReturn(null);
185+
Response resp = sitemapResource.getSitemapDefinition(headersMock, "noexist");
186+
assertThat(resp.getStatus(), is(Response.Status.NOT_FOUND.getStatusCode()));
187+
}
188+
189+
@Test
190+
public void whenCreateOrUpdateSitemapNullBodyShouldReturnBadRequest() {
191+
Object resp = sitemapResource.createOrUpdateSitemap(headersMock, "any", null);
192+
assertThat(((Response) resp).getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode()));
193+
}
194+
195+
@Test
196+
public void whenCreateOrUpdateSitemapNameMismatchShouldReturnBadRequest() {
197+
SitemapDefinitionDTO dto = new SitemapDefinitionDTO();
198+
dto.name = "other";
199+
Object resp = sitemapResource.createOrUpdateSitemap(headersMock, "pathName", dto);
200+
assertThat(((Response) resp).getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode()));
201+
}
202+
203+
@Test
204+
public void whenCreateOrUpdateSitemapCreateNewShouldAddManagedSitemap() {
205+
SitemapDefinitionDTO dto = new SitemapDefinitionDTO();
206+
dto.name = "s1";
207+
208+
when(sitemapRegistryMock.get("s1")).thenReturn(null);
209+
when(sitemapFactory.createSitemap("s1")).thenReturn(new SitemapImpl("s1"));
210+
211+
Object respObj = sitemapResource.createOrUpdateSitemap(headersMock, "s1", dto);
212+
Response resp = (Response) respObj;
213+
assertThat(resp.getStatus(), is(Response.Status.CREATED.getStatusCode()));
214+
215+
verify(managedSitemapProviderMock, times(1)).add(any());
216+
}
217+
218+
@Test
219+
public void whenCreateOrUpdateSitemapUpdateManagedShouldUpdateManagedSitemap() {
220+
SitemapDefinitionDTO dto = new SitemapDefinitionDTO();
221+
dto.name = "s2";
222+
223+
when(sitemapRegistryMock.get("s2")).thenReturn(mock(Sitemap.class));
224+
when(managedSitemapProviderMock.get("s2")).thenReturn(mock(Sitemap.class));
225+
when(sitemapFactory.createSitemap("s2")).thenReturn(new SitemapImpl("s2"));
226+
227+
Object respObj = sitemapResource.createOrUpdateSitemap(headersMock, "s2", dto);
228+
Response resp = (Response) respObj;
229+
assertThat(resp.getStatus(), is(Response.Status.OK.getStatusCode()));
230+
231+
verify(managedSitemapProviderMock, times(1)).update(any());
232+
}
233+
234+
@Test
235+
public void whenRemoveSitemapNotFoundShouldReturn404() {
236+
when(sitemapRegistryMock.get("xyz")).thenReturn(null);
237+
Response resp = sitemapResource.removeSitemap("xyz");
238+
assertThat(resp.getStatus(), is(Response.Status.NOT_FOUND.getStatusCode()));
239+
}
240+
241+
@Test
242+
public void whenRemoveSitemapNotManagedShouldReturnMethodNotAllowed() {
243+
Sitemap sitemap = mock(Sitemap.class);
244+
when(sitemapRegistryMock.get("sdel")).thenReturn(sitemap);
245+
when(managedSitemapProviderMock.remove("sdel")).thenReturn(null);
246+
247+
Response resp = sitemapResource.removeSitemap("sdel");
248+
assertThat(resp.getStatus(), is(Response.Status.METHOD_NOT_ALLOWED.getStatusCode()));
249+
verify(managedSitemapProviderMock, times(1)).remove("sdel");
250+
}
251+
252+
@Test
253+
public void whenRemoveSitemapManagedShouldReturnOk() {
254+
Sitemap sitemap = mock(Sitemap.class);
255+
when(sitemapRegistryMock.get("sdel2")).thenReturn(sitemap);
256+
when(managedSitemapProviderMock.remove("sdel2")).thenReturn(sitemap);
257+
258+
Response resp = sitemapResource.removeSitemap("sdel2");
259+
assertThat(resp.getStatus(), is(Response.Status.OK.getStatusCode()));
260+
verify(managedSitemapProviderMock, times(1)).remove("sdel2");
261+
}
262+
159263
@Test
160264
public void whenSitemapsAreProvidedShouldReturnSitemapBeans() {
161265
Response sitemaps = sitemapResource.getSitemaps();

bundles/org.openhab.core.model.sitemap/bnd.bnd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Import-Package: org.apache.log4j,\
1717
org.openhab.core.items.dto,\
1818
org.openhab.core.model.core,\
1919
org.openhab.core.sitemap, \
20+
org.openhab.core.sitemap.fileconverter, \
2021
org.openhab.core.sitemap.registry, \
2122
org.eclipse.xtext.xbase.lib,\
2223
org.osgi.framework,\

bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapRuntimeModule.xtend

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import org.eclipse.xtext.conversion.IValueConverterService
2121
import org.eclipse.xtext.linking.lazy.LazyURIEncoder
2222
import com.google.inject.Binder
2323
import com.google.inject.name.Names
24+
import org.eclipse.xtext.formatting.IFormatter
25+
import org.openhab.core.model.sitemap.formatting.SitemapFormatter
2426

2527
/**
2628
* Use this class to register components to be used at runtime / without the Equinox extension registry.
@@ -30,6 +32,10 @@ class SitemapRuntimeModule extends org.openhab.core.model.sitemap.AbstractSitema
3032
return SitemapConverters
3133
}
3234

35+
override Class<? extends IFormatter> bindIFormatter() {
36+
return SitemapFormatter
37+
}
38+
3339
override void configureUseIndexFragmentsForLazyLinking(Binder binder) {
3440
binder.bind(Boolean.TYPE).annotatedWith(Names.named(LazyURIEncoder.USE_INDEXED_FRAGMENTS_BINDING)).toInstance(
3541
Boolean.FALSE)

0 commit comments

Comments
 (0)