2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.junit.Assert.fail;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.when;
18 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
20 import com.google.common.collect.ImmutableList;
21 import java.util.Collections;
22 import java.util.Optional;
23 import javax.ws.rs.core.MultivaluedHashMap;
24 import javax.ws.rs.core.UriInfo;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.mockito.Mock;
28 import org.mockito.Mockito;
29 import org.mockito.MockitoAnnotations;
30 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
31 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
33 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
34 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
35 import org.opendaylight.restconf.common.context.WriterParameters;
36 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
37 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
38 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
39 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
40 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
41 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.ReadData;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
50 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
51 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 public class ReadDataTransactionUtilTest {
57 private static final TestData DATA = new TestData();
58 private static final YangInstanceIdentifier.NodeIdentifier NODE_IDENTIFIER = new YangInstanceIdentifier
59 .NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
61 private TransactionVarsWrapper wrapper;
63 private DOMTransactionChain transactionChain;
65 private InstanceIdentifierContext<ContainerSchemaNode> context;
67 private DOMDataTreeReadTransaction read;
69 private SchemaContext schemaContext;
71 private ContainerSchemaNode containerSchemaNode;
73 private LeafSchemaNode containerChildNode;
74 private QName containerChildQName;
78 MockitoAnnotations.initMocks(this);
80 containerChildQName = QName.create("ns", "2016-02-28", "container-child");
82 when(transactionChain.newReadOnlyTransaction()).thenReturn(read);
83 when(context.getSchemaContext()).thenReturn(schemaContext);
84 when(context.getSchemaNode()).thenReturn(containerSchemaNode);
85 when(containerSchemaNode.getQName()).thenReturn(NODE_IDENTIFIER.getNodeType());
86 when(containerChildNode.getQName()).thenReturn(containerChildQName);
87 when(containerSchemaNode.getDataChildByName(containerChildQName)).thenReturn(containerChildNode);
89 DOMDataBroker mockDataBroker = Mockito.mock(DOMDataBroker.class);
90 Mockito.doReturn(transactionChain).when(mockDataBroker).createTransactionChain(Mockito.any());
91 wrapper = new TransactionVarsWrapper(this.context, null, new TransactionChainHandler(mockDataBroker));
95 public void readDataConfigTest() {
96 doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
97 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
98 doReturn(DATA.path).when(context).getInstanceIdentifier();
99 final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
100 final NormalizedNode<?, ?> normalizedNode =
101 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
102 assertEquals(DATA.data3, normalizedNode);
106 public void readAllHavingOnlyConfigTest() {
107 doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
108 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
109 doReturn(immediateFluentFuture(Optional.empty())).when(read)
110 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
111 doReturn(DATA.path).when(context).getInstanceIdentifier();
112 final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
113 final NormalizedNode<?, ?> normalizedNode =
114 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
115 assertEquals(DATA.data3, normalizedNode);
119 public void readAllHavingOnlyNonConfigTest() {
120 doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(read)
121 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
122 doReturn(immediateFluentFuture(Optional.empty())).when(read)
123 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
124 doReturn(DATA.path2).when(context).getInstanceIdentifier();
125 final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
126 final NormalizedNode<?, ?> normalizedNode =
127 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
128 assertEquals(DATA.data2, normalizedNode);
132 public void readDataNonConfigTest() {
133 doReturn(immediateFluentFuture(Optional.of(DATA.data2))).when(read)
134 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
135 doReturn(DATA.path2).when(context).getInstanceIdentifier();
136 final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
137 final NormalizedNode<?, ?> normalizedNode =
138 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
139 assertEquals(DATA.data2, normalizedNode);
143 public void readContainerDataAllTest() {
144 doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
145 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
146 doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
147 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
148 doReturn(DATA.path).when(context).getInstanceIdentifier();
149 final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
150 final NormalizedNode<?, ?> normalizedNode =
151 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
152 final ContainerNode checkingData = Builders
154 .withNodeIdentifier(NODE_IDENTIFIER)
155 .withChild(DATA.contentLeaf)
156 .withChild(DATA.contentLeaf2)
158 assertEquals(checkingData, normalizedNode);
162 public void readContainerDataConfigNoValueOfContentTest() {
163 doReturn(immediateFluentFuture(Optional.of(DATA.data3))).when(read)
164 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
165 doReturn(immediateFluentFuture(Optional.of(DATA.data4))).when(read)
166 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
167 doReturn(DATA.path).when(context).getInstanceIdentifier();
168 final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
169 RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
170 final ContainerNode checkingData = Builders
172 .withNodeIdentifier(NODE_IDENTIFIER)
173 .withChild(DATA.contentLeaf)
174 .withChild(DATA.contentLeaf2)
176 assertEquals(checkingData, normalizedNode);
180 public void readListDataAllTest() {
181 doReturn(immediateFluentFuture(Optional.of(DATA.listData))).when(read)
182 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
183 doReturn(immediateFluentFuture(Optional.of(DATA.listData2))).when(read)
184 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
185 doReturn(DATA.path3).when(context).getInstanceIdentifier();
186 final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
187 final NormalizedNode<?, ?> normalizedNode =
188 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
189 final MapNode checkingData = Builders
191 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
192 .withChild(DATA.checkData)
194 assertEquals(checkingData, normalizedNode);
198 public void readOrderedListDataAllTest() {
199 doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode1))).when(read)
200 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
201 doReturn(immediateFluentFuture(Optional.of(DATA.orderedMapNode2))).when(read)
202 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
203 doReturn(DATA.path3).when(context).getInstanceIdentifier();
205 final NormalizedNode<?, ?> normalizedNode =
206 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
208 final MapNode expectedData = Builders.orderedMapBuilder()
209 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname)).withChild(DATA.checkData)
211 assertEquals(expectedData, normalizedNode);
215 public void readUnkeyedListDataAllTest() {
216 doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode1))).when(read)
217 .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
218 doReturn(immediateFluentFuture(Optional.of(DATA.unkeyedListNode2))).when(read)
219 .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
220 doReturn(DATA.path3).when(context).getInstanceIdentifier();
222 final NormalizedNode<?, ?> normalizedNode =
223 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
225 final UnkeyedListNode expectedData = Builders.unkeyedListBuilder()
226 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
227 .withChild(Builders.unkeyedListEntryBuilder().withNodeIdentifier(
228 new YangInstanceIdentifier.NodeIdentifier(DATA.listQname))
229 .withChild(DATA.unkeyedListEntryNode1.getValue().iterator().next())
230 .withChild(DATA.unkeyedListEntryNode2.getValue().iterator().next()).build()).build();
231 assertEquals(expectedData, normalizedNode);
235 public void readLeafListDataAllTest() {
236 doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode1))).when(read)
237 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
238 doReturn(immediateFluentFuture(Optional.of(DATA.leafSetNode2))).when(read)
239 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
240 doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
242 final NormalizedNode<?, ?> normalizedNode =
243 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
245 final LeafSetNode<String> expectedData = Builders.<String>leafSetBuilder().withNodeIdentifier(
246 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
247 ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.leafSetNode1.getValue())
248 .addAll(DATA.leafSetNode2.getValue()).build()).build();
249 assertEquals(expectedData, normalizedNode);
253 public void readOrderedLeafListDataAllTest() {
254 doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode1))).when(read)
255 .read(LogicalDatastoreType.OPERATIONAL, DATA.leafSetNodePath);
256 doReturn(immediateFluentFuture(Optional.of(DATA.orderedLeafSetNode2))).when(read)
257 .read(LogicalDatastoreType.CONFIGURATION, DATA.leafSetNodePath);
258 doReturn(DATA.leafSetNodePath).when(context).getInstanceIdentifier();
260 final NormalizedNode<?, ?> normalizedNode =
261 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
263 final LeafSetNode<String> expectedData = Builders.<String>orderedLeafSetBuilder().withNodeIdentifier(
264 new YangInstanceIdentifier.NodeIdentifier(DATA.leafListQname)).withValue(
265 ImmutableList.<LeafSetEntryNode<String>>builder().addAll(DATA.orderedLeafSetNode1.getValue())
266 .addAll(DATA.orderedLeafSetNode2.getValue()).build()).build();
267 assertEquals(expectedData, normalizedNode);
271 public void readDataWrongPathOrNoContentTest() {
272 doReturn(immediateFluentFuture(Optional.empty())).when(read)
273 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
274 doReturn(DATA.path2).when(context).getInstanceIdentifier();
275 final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
276 final NormalizedNode<?, ?> normalizedNode =
277 ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
278 assertNull(normalizedNode);
281 @Test(expected = RestconfDocumentedException.class)
282 public void readDataFailTest() {
283 final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
284 final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
285 valueOfContent, wrapper, schemaContext);
286 assertNull(normalizedNode);
290 * Test of parsing default parameters from URI request.
293 public void parseUriParametersDefaultTest() {
294 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
295 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
297 // no parameters, default values should be used
298 when(uriInfo.getQueryParameters()).thenReturn(parameters);
300 final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
302 assertEquals("Not correctly parsed URI parameter",
303 RestconfDataServiceConstant.ReadData.ALL, parsedParameters.getContent());
304 assertNull("Not correctly parsed URI parameter",
305 parsedParameters.getDepth());
306 assertNull("Not correctly parsed URI parameter",
307 parsedParameters.getFields());
311 * Test of parsing user defined parameters from URI request.
314 public void parseUriParametersUserDefinedTest() {
315 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
316 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
318 final String content = "config";
319 final String depth = "10";
320 final String fields = containerChildQName.getLocalName();
322 parameters.put("content", Collections.singletonList(content));
323 parameters.put("depth", Collections.singletonList(depth));
324 parameters.put("fields", Collections.singletonList(fields));
326 when(uriInfo.getQueryParameters()).thenReturn(parameters);
328 final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
331 assertEquals("Not correctly parsed URI parameter",
332 content, parsedParameters.getContent());
335 assertNotNull("Not correctly parsed URI parameter",
336 parsedParameters.getDepth());
337 assertEquals("Not correctly parsed URI parameter",
338 depth, parsedParameters.getDepth().toString());
341 assertNotNull("Not correctly parsed URI parameter",
342 parsedParameters.getFields());
343 assertEquals("Not correctly parsed URI parameter",
344 1, parsedParameters.getFields().size());
345 assertEquals("Not correctly parsed URI parameter",
346 1, parsedParameters.getFields().get(0).size());
347 assertEquals("Not correctly parsed URI parameter",
348 containerChildQName, parsedParameters.getFields().get(0).iterator().next());
352 * Negative test of parsing request URI parameters when content parameter has not allowed value.
355 public void parseUriParametersContentParameterNegativeTest() {
356 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
357 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
359 parameters.put("content", Collections.singletonList("not-allowed-parameter-value"));
360 when(uriInfo.getQueryParameters()).thenReturn(parameters);
363 ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
364 fail("Test expected to fail due to not allowed parameter value");
365 } catch (final RestconfDocumentedException e) {
367 assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
368 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
369 assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
374 * Negative test of parsing request URI parameters when depth parameter has not allowed value.
377 public void parseUriParametersDepthParameterNegativeTest() {
378 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
379 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
381 // inserted value is not allowed
382 parameters.put("depth", Collections.singletonList("bounded"));
383 when(uriInfo.getQueryParameters()).thenReturn(parameters);
386 ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
387 fail("Test expected to fail due to not allowed parameter value");
388 } catch (final RestconfDocumentedException e) {
390 assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
391 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
392 assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
397 * Negative test of parsing request URI parameters when depth parameter has not allowed value (less than minimum).
400 public void parseUriParametersDepthMinimalParameterNegativeTest() {
401 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
402 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
404 // inserted value is too low
406 "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MIN_DEPTH - 1)));
407 when(uriInfo.getQueryParameters()).thenReturn(parameters);
410 ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
411 fail("Test expected to fail due to not allowed parameter value");
412 } catch (final RestconfDocumentedException e) {
414 assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
415 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
416 assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
421 * Negative test of parsing request URI parameters when depth parameter has not allowed value (more than maximum).
424 public void parseUriParametersDepthMaximalParameterNegativeTest() {
425 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
426 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
428 // inserted value is too high
430 "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MAX_DEPTH + 1)));
431 when(uriInfo.getQueryParameters()).thenReturn(parameters);
434 ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
435 fail("Test expected to fail due to not allowed parameter value");
436 } catch (final RestconfDocumentedException e) {
438 assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
439 assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
440 assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
445 * Testing parsing of with-defaults parameter which value doesn't match report-all or report-all-tagged patterns
446 * - non-reporting setting.
449 public void parseUriParametersWithDefaultAndNonTaggedTest() {
450 // preparation of input data
451 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
452 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
453 final String preparedDefaultValue = "sample-default";
454 parameters.put(RestconfDataServiceConstant.ReadData.WITH_DEFAULTS,
455 Collections.singletonList(preparedDefaultValue));
456 when(uriInfo.getQueryParameters()).thenReturn(parameters);
458 final WriterParameters writerParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
459 assertEquals(preparedDefaultValue, writerParameters.getWithDefault());
460 assertFalse(writerParameters.isTagged());
464 * Testing parsing of with-defaults parameter which value matches 'report-all-tagged' setting - default value should
465 * be set to {@code null} and tagged flag should be set to {@code true}.
468 public void parseUriParametersWithDefaultAndTaggedTest() {
469 // preparation of input data
470 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
471 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
472 parameters.put(RestconfDataServiceConstant.ReadData.WITH_DEFAULTS,
473 Collections.singletonList(ReadData.REPORT_ALL_TAGGED_DEFAULT_VALUE));
474 when(uriInfo.getQueryParameters()).thenReturn(parameters);
476 final WriterParameters writerParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
477 assertNull(writerParameters.getWithDefault());
478 assertTrue(writerParameters.isTagged());
482 * Testing parsing of with-defaults parameter which value matches 'report-all' setting - default value should
483 * be set to {@code null} and tagged flag should be set to {@code false}.
486 public void parseUriParametersWithDefaultAndReportAllTest() {
487 // preparation of input data
488 final UriInfo uriInfo = Mockito.mock(UriInfo.class);
489 final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
490 parameters.put(RestconfDataServiceConstant.ReadData.WITH_DEFAULTS,
491 Collections.singletonList(ReadData.REPORT_ALL_DEFAULT_VALUE));
492 when(uriInfo.getQueryParameters()).thenReturn(parameters);
494 final WriterParameters writerParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
495 assertNull(writerParameters.getWithDefault());
496 assertFalse(writerParameters.isTagged());