fix: simplifies the parsing routine, which accounts for leading 0's (#28102)

closes: #27839

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-03-22 04:19:52 -04:00 committed by GitHub
parent 2672b362c9
commit 619775b8db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 62 additions and 42 deletions

View file

@ -192,6 +192,13 @@ In this example, you start a session authenticated as the `admin` user in the `m
The `create` and `update` commands send a JSON body to the server. You can use `-f FILENAME` to read a pre-made document from a file. When you can use the `-f -` option, {project_name} reads the message body from the standard input. You can specify individual attributes and their values, as seen in the `create users` example. {project_name} composes the attributes into a JSON body and sends them to the server. The `create` and `update` commands send a JSON body to the server. You can use `-f FILENAME` to read a pre-made document from a file. When you can use the `-f -` option, {project_name} reads the message body from the standard input. You can specify individual attributes and their values, as seen in the `create users` example. {project_name} composes the attributes into a JSON body and sends them to the server.
[NOTE]
====
The value in name=value pairs used in --set, -s options, are assumed to be JSON. If it cannot be parsed as valid JSON, then it will be sent to the server as a text value.
If the value is enclosed in quotes after shell processing, but is not valid JSON, the quotes will be stripped and the rest of the value will be sent as text. This behavior is deprecated, please consider specifying your value without qoutes or a valid JSON string literal with double quotes.
====
Several methods are available in {project_name} to update a resource using the `update` command. You can determine the current state of a resource and save it to a file, edit that file, and send it to the server for an update. Several methods are available in {project_name} to update a resource using the `update` command. You can determine the current state of a resource and save it to a file, edit that file, and send it to the server for an update.
For example: For example:

View file

@ -17,6 +17,7 @@
package org.keycloak.client.admin.cli.util; package org.keycloak.client.admin.cli.util;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
@ -37,6 +38,7 @@ public class OutputUtil {
static { static {
MAPPER.enable(SerializationFeature.INDENT_OUTPUT); MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
MAPPER.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
} }

View file

@ -18,9 +18,6 @@ package org.keycloak.client.admin.cli.util;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.DoubleNode;
import com.fasterxml.jackson.databind.node.LongNode;
import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.databind.node.TextNode;
@ -127,54 +124,21 @@ public class ReflectionUtil {
list.remove(index); list.remove(index);
} }
private static JsonNode valueToJsonNode(String val) { static JsonNode valueToJsonNode(String val) {
// try get value as JSON object // try get value as JSON
try { try {
return MAPPER.readValue(val, ObjectNode.class); return MAPPER.readTree(val);
} catch (Exception ignored) { } catch (Exception ignored) {
} }
// try get value as JSON array // legacy behavior, check for quoted / invalid json - to be removed
try { if (isQuoted(val)) {
return MAPPER.readValue(val, ArrayNode.class);
} catch (Exception ignored) {
}
if (isBoolean(val)) {
return BooleanNode.valueOf(Boolean.valueOf(val));
} else if (isInteger(val)) {
return LongNode.valueOf(Long.valueOf(val));
} else if (isNumber(val)) {
return DoubleNode.valueOf(Double.valueOf(val));
} else if (isQuoted(val)) {
return TextNode.valueOf(unquote(val)); return TextNode.valueOf(unquote(val));
} }
return TextNode.valueOf(val); return TextNode.valueOf(val);
} }
private static boolean isInteger(String val) {
try {
Long.valueOf(val);
return true;
} catch (Exception ignored) {
return false;
}
}
private static boolean isNumber(String val) {
try {
Double.valueOf(val);
return true;
} catch (Exception ignored) {
return false;
}
}
private static boolean isBoolean(String val) {
return "false".equals(val) || "true".equals(val);
}
private static boolean isQuoted(String val) { private static boolean isQuoted(String val) {
return val.startsWith("'") || val.startsWith("\""); return val.startsWith("'") || val.startsWith("\"");
} }

View file

@ -0,0 +1,47 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.client.admin.cli.util;
import org.junit.Test;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NumericNode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ReflectionUtilTest {
@Test
public void testValueToJsonNode() {
assertEquals("x", ReflectionUtil.valueToJsonNode("x").asText());
assertEquals("x", ReflectionUtil.valueToJsonNode("'x'").asText());
assertEquals("x", ReflectionUtil.valueToJsonNode("\"x\"").asText());
assertEquals("x\"y", ReflectionUtil.valueToJsonNode("\"x\"y\"").asText());
// should preserve the leading 0
assertEquals("0123", ReflectionUtil.valueToJsonNode("0123").asText());
JsonNode value = ReflectionUtil.valueToJsonNode("123");
assertTrue(value instanceof NumericNode);
assertEquals(123, value.asInt());
value = ReflectionUtil.valueToJsonNode("[\"x\",\"y\"]");
assertTrue(value instanceof ArrayNode);
assertEquals("y", value.get(1).textValue());
}
}