From 12e8bc56bf5b0b0bf00b910029e568b017653aa3 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 25 Oct 2021 19:20:05 +0200 Subject: [PATCH] Integrate ParserFieldsParameter with FieldsParameter We have the baseline string parser reimplemented, which means we can just interpret the list of selection nodes. Take out all the string parsing code and reimplement the core loop, making things a lot clearer. JIRA: NETCONF-820 Change-Id: I915794323a75937996a6a3ba693a10ab2878d910 Signed-off-by: Robert Varga --- .../rfc8040/databind/jaxrs/QueryParams.java | 4 +- .../utils/parser/ParserFieldsParameter.java | 192 ++++-------------- .../nb/rfc8040/FieldsParameterTest.java | 4 + .../parser/ParserFieldsParameterTest.java | 136 ++++--------- 4 files changed, 93 insertions(+), 243 deletions(-) diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java index db7bb59c3f..d1fe9ec092 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java @@ -112,8 +112,8 @@ public final class QueryParams { } return identifier.getMountPoint() != null - ? QueryParameters.ofFieldPaths(params, parseFieldsPaths(identifier, fields.paramValue())) - : QueryParameters.ofFields(params, parseFieldsParameter(identifier, fields.paramValue())); + ? QueryParameters.ofFieldPaths(params, parseFieldsPaths(identifier, fields)) + : QueryParameters.ofFields(params, parseFieldsParameter(identifier, fields)); } /** diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java index f4b7b77e9b..f2325315bb 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java @@ -21,6 +21,8 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.nb.rfc8040.FieldsParam; +import org.opendaylight.restconf.nb.rfc8040.FieldsParam.NodeSelector; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; import org.opendaylight.yangtools.yang.common.QName; @@ -29,9 +31,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** * Utilities used for parsing of fields query parameter content. @@ -53,7 +55,7 @@ public abstract class ParserFieldsParameter { * @return {@link List} of levels; each level contains set of {@link QName} */ public static @NonNull List> parseFieldsParameter(final @NonNull InstanceIdentifierContext identifier, - final @NonNull String input) { + final @NonNull FieldsParam input) { return QNAME_PARSER.parseFields(identifier, input); } @@ -66,7 +68,7 @@ public abstract class ParserFieldsParameter { * of provided {@code identifier} */ public static @NonNull List parseFieldsPaths( - final @NonNull InstanceIdentifierContext identifier, final @NonNull String input) { + final @NonNull InstanceIdentifierContext identifier, final @NonNull FieldsParam input) { final List> levels = PATH_PARSER.parseFields(identifier, input); final List> mappedLevels = mapLevelsContentByIdentifiers(levels); return buildPaths(mappedLevels); @@ -125,10 +127,7 @@ public abstract class ParserFieldsParameter { * @return {@link List} of levels; each level contains {@link Set} of identifiers of type {@link T} */ private @NonNull List> parseFields(final @NonNull InstanceIdentifierContext identifier, - final @NonNull String input) { - final List> parsed = new ArrayList<>(); - final SchemaContext context = identifier.getSchemaContext(); - final QNameModule startQNameModule = identifier.getSchemaNode().getQName().getModule(); + final @NonNull FieldsParam input) { final DataSchemaContextNode startNode = DataSchemaContextNode.fromDataSchemaNode( (DataSchemaNode) identifier.getSchemaNode()); @@ -137,117 +136,54 @@ public abstract class ParserFieldsParameter { "Start node missing in " + input, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - parseInput(input, startQNameModule, startNode, parsed, context); + final List> parsed = new ArrayList<>(); + processSelectors(parsed, identifier.getSchemaContext(), identifier.getSchemaNode().getQName().getModule(), + startNode, input.nodeSelectors()); return parsed; } - /** - * Parse input value of fields parameter and create list of sets. Each set represents one level of child nodes. - * - * @param input input value of fields parameter - * @param startQNameModule starting qname module - * @param startNode starting node - * @param parsed list of results - * @param context schema context - */ - private void parseInput(final @NonNull String input, final @NonNull QNameModule startQNameModule, - final @NonNull DataSchemaContextNode startNode, - final @NonNull List> parsed, final SchemaContext context) { + private void processSelectors(final List> parsed, final EffectiveModelContext context, + final QNameModule startNamespace, final DataSchemaContextNode startNode, + final List selectors) { final Set startLevel = new HashSet<>(); parsed.add(startLevel); - int currentPosition = 0; - int startPosition = 0; - DataSchemaContextNode currentNode = startNode; - QNameModule currentQNameModule = startQNameModule; - Set currentLevel = startLevel; - - while (currentPosition < input.length()) { - final char currentChar = input.charAt(currentPosition); - - if (ParserConstants.YANG_IDENTIFIER_PART.matches(currentChar)) { - currentPosition++; - continue; - } - - switch (currentChar) { - case '/': - // add parsed identifier to results for current level - currentNode = addChildToResult(currentNode, input.substring(startPosition, currentPosition), - currentQNameModule, currentLevel); - // go one level down - currentLevel = prepareQNameLevel(parsed, currentLevel); + for (var selector : selectors) { + var node = startNode; + var namespace = startNamespace; + var level = startLevel; + + + // Note: path is guaranteed to have at least one step + final var it = selector.path().iterator(); + while (true) { + // FIXME: The layout of this loop is rather weird, which is due to how prepareQNameLevel() operates. We + // need to call it only when we know there is another identifier coming, otherwise we would end + // up with empty levels sneaking into the mix. + // + // Dealing with that weirdness requires understanding what the expected end results are and a + // larger rewrite of the algorithms involved. + final var step = it.next(); + final var module = step.module(); + if (module != null) { + // FIXME: this is not defensive enough, as we can fail to find the module + namespace = context.findModules(module).iterator().next().getQNameModule(); + } - currentPosition++; - break; - case ':': - // new namespace and revision found - currentQNameModule = context.findModules( - input.substring(startPosition, currentPosition)).iterator().next().getQNameModule(); - currentPosition++; + // add parsed identifier to results for current level + node = addChildToResult(node, step.identifier().bindTo(namespace), level); + if (!it.hasNext()) { break; - case '(': - // add current child to parsed results for current level - final DataSchemaContextNode child = addChildToResult( - currentNode, - input.substring(startPosition, currentPosition), currentQNameModule, currentLevel); - // call with child node as new start node for one level down - final int closingParenthesis = currentPosition - + findClosingParenthesis(input.substring(currentPosition + 1)); - parseInput( - input.substring(currentPosition + 1, closingParenthesis), - currentQNameModule, - child, - parsed, - context); - - // closing parenthesis must be at the end of input or separator and one more character is expected - currentPosition = closingParenthesis + 1; - if (currentPosition != input.length()) { - if (currentPosition + 1 < input.length()) { - if (input.charAt(currentPosition) == ';') { - currentPosition++; - } else { - throw new RestconfDocumentedException( - "Missing semicolon character after " - + child.getIdentifier().getNodeType().getLocalName() - + " child nodes", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } - } else { - throw new RestconfDocumentedException( - "Unexpected character '" - + input.charAt(currentPosition) - + "' found in fields parameter value", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } - } + } - break; - case ';': - // complete identifier found - addChildToResult( - currentNode, - input.substring(startPosition, currentPosition), currentQNameModule, currentLevel); - currentPosition++; - - // next nodes can be placed on already utilized level-s - currentNode = startNode; - currentQNameModule = startQNameModule; - currentLevel = startLevel; - break; - default: - throw new RestconfDocumentedException( - "Unexpected character '" + currentChar + "' found in fields parameter value", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + // go one level down + level = prepareQNameLevel(parsed, level); } - startPosition = currentPosition; - } - - // parse input to end - if (startPosition < input.length()) { - addChildToResult(currentNode, input.substring(startPosition), currentQNameModule, currentLevel); + final var subs = selector.subSelectors(); + if (!subs.isEmpty()) { + processSelectors(parsed, context, namespace, node, subs); + } } } @@ -280,43 +216,6 @@ public abstract class ParserFieldsParameter { return nextLevel; } - /** - * Find position of matching parenthesis increased by one, but at most equals to input size. - * - * @param input input where to find for closing parenthesis - * @return int position of closing parenthesis increased by one - */ - private static int findClosingParenthesis(final @NonNull String input) { - int position = 0; - int count = 1; - - while (position < input.length()) { - final char currentChar = input.charAt(position); - - if (currentChar == '(') { - count++; - } - - if (currentChar == ')') { - count--; - } - - if (count == 0) { - break; - } - - position++; - } - - // closing parenthesis was not found - if (position >= input.length()) { - throw new RestconfDocumentedException("Missing closing parenthesis in fields parameter", - ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); - } - - return ++position; - } - /** * Add parsed child of current node to result for current level. * @@ -328,11 +227,6 @@ public abstract class ParserFieldsParameter { abstract @NonNull DataSchemaContextNode addChildToResult(@NonNull DataSchemaContextNode currentNode, @NonNull QName childQName, @NonNull Set level); - private @NonNull DataSchemaContextNode addChildToResult(final @NonNull DataSchemaContextNode currentNode, - final @NonNull String localName, final @NonNull QNameModule namespace, final @NonNull Set level) { - return addChildToResult(currentNode, QName.create(namespace, localName), level); - } - /** * Fields parser that stores set of {@link QName}s in each level. Because of this fact, from the output * it is is only possible to assume on what depth the selected element is placed. Identifiers of intermediary diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/FieldsParameterTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/FieldsParameterTest.java index 8579921761..8ab7d3dbe3 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/FieldsParameterTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/FieldsParameterTest.java @@ -161,6 +161,7 @@ public class FieldsParameterTest { assertInvalidFields("a:.", "Expecting [a-ZA-Z_], not '.'", 2); assertInvalidFields("a:b+", "Expecting [a-zA-Z_.-/(:;], not '+'", 3); assertInvalidFields("a;)", "Expecting [a-ZA-Z_], not ')'", 2); + assertInvalidFields("*", "Expecting [a-ZA-Z_], not '*'", 0); } @Test @@ -168,11 +169,14 @@ public class FieldsParameterTest { assertInvalidFields("a;", "Unexpected end of input", 2); assertInvalidFields("a(", "Unexpected end of input", 2); assertInvalidFields("a(a", "Unexpected end of input", 3); + assertInvalidFields("library(", "Unexpected end of input", 8); + assertInvalidFields("library(album);", "Unexpected end of input", 15); } @Test public void testUnexpectedRightParent() { assertInvalidFields("a)", "Expecting ';', not ')'", 1); + assertInvalidFields("library(album)player", "Expecting ';', not 'p'", 14); } private static void assertInvalidFields(final String str, final String message, final int errorOffset) { diff --git a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameterTest.java b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameterTest.java index 375485ba57..ba2447f9ac 100644 --- a/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameterTest.java +++ b/restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameterTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import java.text.ParseException; import java.util.List; import java.util.Optional; import java.util.Set; @@ -24,6 +25,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.opendaylight.restconf.common.context.InstanceIdentifierContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.nb.rfc8040.FieldsParam; import org.opendaylight.restconf.nb.rfc8040.TestRestconfUtils; import org.opendaylight.yangtools.yang.common.ErrorTag; import org.opendaylight.yangtools.yang.common.ErrorType; @@ -223,8 +225,7 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterSimplePathTest() { - final String input = "library"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input); + final List> parsedFields = assertFieldsParameter(identifierJukebox, "library"); assertNotNull(parsedFields); assertEquals(1, parsedFields.size()); @@ -237,8 +238,7 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterDoublePathTest() { - final String input = "library;player"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input); + final List> parsedFields = assertFieldsParameter(identifierJukebox, "library;player"); assertNotNull(parsedFields); assertEquals(1, parsedFields.size()); @@ -252,8 +252,7 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterSubPathTest() { - final String input = "library/album/name"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input); + final List> parsedFields = assertFieldsParameter(identifierJukebox, "library/album/name"); assertNotNull(parsedFields); assertEquals(3, parsedFields.size()); @@ -273,8 +272,7 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterChildrenPathTest() { - final String input = "library(album(name))"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input); + final List> parsedFields = assertFieldsParameter(identifierJukebox, "library(album(name))"); assertNotNull(parsedFields); assertEquals(3, parsedFields.size()); @@ -294,8 +292,8 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterNamespaceTest() { - final String input = "augmented-jukebox:augmented-library"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input); + final List> parsedFields = assertFieldsParameter(identifierJukebox, + "augmented-jukebox:augmented-library"); assertNotNull(parsedFields); assertEquals(1, parsedFields.size()); @@ -310,8 +308,8 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterWithMultipleChildrenTest1() { - final String input = "services(type-of-service;instance/instance-name;instance/provider)"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierTestServices, input); + final List> parsedFields = assertFieldsParameter(identifierTestServices, + "services(type-of-service;instance/instance-name;instance/provider)"); assertNotNull(parsedFields); assertEquals(parsedFields.size(), 3); @@ -332,8 +330,8 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterWithMultipleChildrenTest2() { - final String input = "services(type-of-service;instance(instance-name;provider))"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierTestServices, input); + final List> parsedFields = assertFieldsParameter(identifierTestServices, + "services(type-of-service;instance(instance-name;provider))"); assertNotNull(parsedFields); assertEquals(parsedFields.size(), 3); @@ -354,8 +352,8 @@ public class ParserFieldsParameterTest { */ @Test public void parseFieldsParameterWithMultipleChildrenTest3() { - final String input = "services(instance/instance-name;type-of-service;next-data/next-service)"; - final List> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierTestServices, input); + final List> parsedFields = assertFieldsParameter(identifierTestServices, + "services(instance/instance-name;type-of-service;next-data/next-service)"); assertNotNull(parsedFields); assertEquals(parsedFields.size(), 3); @@ -372,52 +370,12 @@ public class ParserFieldsParameterTest { List.of(INSTANCE_NAME_Q_NAME, NEXT_SERVICE_Q_NAME))); } - /** - * Test parse fields parameter containing not expected character. - */ - @Test - public void parseFieldsParameterNotExpectedCharacterNegativeTest() { - final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, - () -> ParserFieldsParameter.parseFieldsParameter(identifierJukebox, "*")); - // Bad request - assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag()); - } - - /** - * Test parse fields parameter with missing closing parenthesis. - */ - @Test - public void parseFieldsParameterMissingParenthesisNegativeTest() { - final String input = "library("; - - final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, - () -> ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input)); - // Bad request - assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag()); - } - /** * Test parse fields parameter when not existing child node selected. */ @Test - public void parseFieldsParameterMissingChildNodeNegativeTest() { - final String input = "library(not-existing)"; - - final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, - () -> ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input)); - // Bad request - assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag()); - } - - /** - * Test parse fields parameter with unexpected character after parenthesis. - */ - @Test - public void parseFieldsParameterAfterParenthesisNegativeTest() { - final String input = "library(album);"; + public void parseFieldsParameterMissingChildNodeNegativeTest() throws ParseException { + final FieldsParam input = FieldsParam.parse("library(not-existing)"); final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, () -> ParserFieldsParameter.parseFieldsParameter(identifierJukebox, input)); @@ -426,25 +384,9 @@ public class ParserFieldsParameterTest { assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag()); } - /** - * Test parse fields parameter with missing semicolon after parenthesis. - */ - @Test - public void parseFieldsParameterMissingSemicolonNegativeTest() { - final String input = "library(album)player"; - - final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, - () -> ParserFieldsParameter.parseFieldsParameter(this.identifierJukebox, input)); - // Bad request - assertEquals("Error type is not correct", ErrorType.PROTOCOL, ex.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, ex.getErrors().get(0).getErrorTag()); - } - @Test public void parseTopLevelContainerToPathTest() { - final String input = "library"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierJukebox, input); + final List parsedFields = assertFieldsPaths(identifierJukebox, "library"); assertNotNull(parsedFields); assertEquals(1, parsedFields.size()); @@ -456,8 +398,7 @@ public class ParserFieldsParameterTest { @Test public void parseTwoTopLevelContainersToPathsTest() { final String input = "library;player"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierJukebox, input); + final List parsedFields = assertFieldsPaths(identifierJukebox, input); assertNotNull(parsedFields); assertEquals(2, parsedFields.size()); @@ -473,9 +414,7 @@ public class ParserFieldsParameterTest { @Test public void parseNestedLeafToPathTest() { - final String input = "library/album/name"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierJukebox, input); + final List parsedFields = assertFieldsPaths(identifierJukebox, "library/album/name"); assertEquals(1, parsedFields.size()); final List pathArguments = parsedFields.get(0).getPathArguments(); @@ -488,9 +427,8 @@ public class ParserFieldsParameterTest { @Test public void parseAugmentedLeafToPathTest() { - final String input = "player/augmented-jukebox:speed"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierJukebox, input); + final List parsedFields = assertFieldsPaths(identifierJukebox, + "player/augmented-jukebox:speed"); assertEquals(1, parsedFields.size()); final List pathArguments = parsedFields.get(0).getPathArguments(); @@ -503,9 +441,8 @@ public class ParserFieldsParameterTest { @Test public void parseMultipleFieldsOnDifferentLevelsToPathsTest() { - final String input = "services(type-of-service;instance/instance-name;instance/provider)"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierTestServices, input); + final List parsedFields = assertFieldsPaths(identifierTestServices, + "services(type-of-service;instance/instance-name;instance/provider)"); assertEquals(3, parsedFields.size()); @@ -524,9 +461,8 @@ public class ParserFieldsParameterTest { @Test public void parseListFieldUnderListToPathTest() { - final String input = "services/instance"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierTestServices, input); + final List parsedFields = assertFieldsPaths(identifierTestServices, + "services/instance"); assertEquals(1, parsedFields.size()); final List pathArguments = parsedFields.get(0).getPathArguments(); @@ -540,9 +476,7 @@ public class ParserFieldsParameterTest { @Test public void parseLeafListFieldToPathTest() { - final String input = "protocols"; - final List parsedFields = ParserFieldsParameter.parseFieldsPaths( - identifierTestServices, input); + final List parsedFields = assertFieldsPaths(identifierTestServices, "protocols"); assertEquals(1, parsedFields.size()); final List pathArguments = parsedFields.get(0).getPathArguments(); @@ -557,4 +491,22 @@ public class ParserFieldsParameterTest { .filter(path -> lastPathArg.equals(path.getLastPathArgument().getNodeType())) .findAny(); } + + private static List> assertFieldsParameter(final InstanceIdentifierContext identifier, + final String input) { + return ParserFieldsParameter.parseFieldsParameter(identifier, assertFields(input)); + } + + private static List assertFieldsPaths(final InstanceIdentifierContext identifier, + final String input) { + return ParserFieldsParameter.parseFieldsPaths(identifier, assertFields(input)); + } + + private static FieldsParam assertFields(final String input) { + try { + return FieldsParam.parse(input); + } catch (ParseException e) { + throw new AssertionError(e); + } + } } \ No newline at end of file -- 2.36.6