From 2edd397ececabf19e6f9b62c5e49418222119a7f Mon Sep 17 00:00:00 2001 From: Jeff Cantrill Date: Wed, 2 Mar 2016 15:37:03 -0500 Subject: [PATCH] [OSJC-205] Access raw JSON as object map --- .../capability/CapabilityInitializer.java | 3 + .../resources/PropertyAccessCapability.java | 133 ++++++++++++++++++ .../resources/IPropertyAccessCapability.java | 65 +++++++++ .../PropertyAccessCapabilityTest.java | 80 +++++++++++ 4 files changed, 281 insertions(+) create mode 100644 src/main/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapability.java create mode 100644 src/main/java/com/openshift/restclient/capability/resources/IPropertyAccessCapability.java create mode 100644 src/test/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapabilityTest.java diff --git a/src/main/java/com/openshift/internal/restclient/capability/CapabilityInitializer.java b/src/main/java/com/openshift/internal/restclient/capability/CapabilityInitializer.java index 11c16a9d..8fbb0326 100644 --- a/src/main/java/com/openshift/internal/restclient/capability/CapabilityInitializer.java +++ b/src/main/java/com/openshift/internal/restclient/capability/CapabilityInitializer.java @@ -22,6 +22,7 @@ import com.openshift.internal.restclient.capability.resources.OpenShiftBinaryRSync; import com.openshift.internal.restclient.capability.resources.ProjectTemplateListCapability; import com.openshift.internal.restclient.capability.resources.ProjectTemplateProcessing; +import com.openshift.internal.restclient.capability.resources.PropertyAccessCapability; import com.openshift.internal.restclient.capability.resources.TagCapability; import com.openshift.internal.restclient.capability.resources.TemplateTraceability; import com.openshift.internal.restclient.capability.resources.UpdateableCapability; @@ -40,6 +41,7 @@ import com.openshift.restclient.capability.resources.IPortForwardable; import com.openshift.restclient.capability.resources.IProjectTemplateList; import com.openshift.restclient.capability.resources.IProjectTemplateProcessing; +import com.openshift.restclient.capability.resources.IPropertyAccessCapability; import com.openshift.restclient.capability.resources.IRSyncable; import com.openshift.restclient.capability.resources.ITags; import com.openshift.restclient.capability.resources.ITemplateTraceability; @@ -127,6 +129,7 @@ public static void initializeCapabilities(Map, ICap initializeCapability(capabilities, ITags.class, new TagCapability(resource)); initializeCapability(capabilities, IClientCapability.class, new ClientCapability(client)); initializeCapability(capabilities, IUpdatable.class, new UpdateableCapability(resource)); + initializeCapability(capabilities, IPropertyAccessCapability.class, new PropertyAccessCapability(resource)); } public static void initializeClientCapabilities(Map, ICapability> capabilities, IClient client){ diff --git a/src/main/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapability.java b/src/main/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapability.java new file mode 100644 index 00000000..d05eecda --- /dev/null +++ b/src/main/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapability.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.openshift.internal.restclient.capability.resources; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.openshift.internal.util.JBossDmrExtentions.*; + +import org.jboss.dmr.ModelNode; + +import com.openshift.internal.restclient.model.KubernetesResource; +import com.openshift.restclient.capability.resources.IPropertyAccessCapability; +import com.openshift.restclient.model.IResource; + +public class PropertyAccessCapability implements IPropertyAccessCapability { + + private KubernetesResource resource; + + public PropertyAccessCapability(IResource resource) { + if(resource instanceof KubernetesResource) { + this.resource = (KubernetesResource) resource; + } + } + + @Override + public String asString(String path) { + ModelNode node = get(resource.getNode(), null, path); + if(!node.isDefined()) { + throw new UnresolvablePathException(); + } + return node.asString(); + } + + @Override + public Map asMap(String path) { + return asMap(get(resource.getNode(), null, path)); + } + + private Map asMap(ModelNode node) { + if(!node.isDefined()) { + throw new UnresolvablePathException(); + } + Map result = new HashMap<>(); + for (String key : node.keys()) { + ModelNode value = node.get(key); + switch(value.getType()) { + case OBJECT: + result.put(key, asMap(value)); + break; + case LIST: + result.put(key, asList(value)); + case STRING: + result.put(key, value.asString()); + break; + case INT: + result.put(key, value.asInt()); + break; + case BIG_INTEGER: + result.put(key, value.asBigInteger()); + break; + case BIG_DECIMAL: + result.put(key, value.asBigDecimal()); + break; + case LONG: + result.put(key, value.asBigDecimal()); + break; + case BOOLEAN: + result.put(key, value.asBoolean()); + break; + default: + result.put(key, value.asString()); + } + } + return result; + } + + private List asList(ModelNode node) { + List list = new ArrayList<>(); + for (ModelNode entry : node.asList()) { + switch(entry.getType()) { + case OBJECT: + list.add(asMap(entry)); + break; + case LIST: + list.add(asList(entry)); + case STRING: + list.add(entry.asString()); + break; + case INT: + list.add(entry.asInt()); + break; + case BIG_INTEGER: + list.add(entry.asBigInteger()); + break; + case BIG_DECIMAL: + list.add(entry.asBigDecimal()); + break; + case LONG: + list.add(entry.asBigDecimal()); + break; + case BOOLEAN: + list.add(entry.asBoolean()); + break; + default: + list.add(entry.asString()); + } + + } + return list; + } + + @Override + public boolean isSupported() { + return resource != null; + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + +} diff --git a/src/main/java/com/openshift/restclient/capability/resources/IPropertyAccessCapability.java b/src/main/java/com/openshift/restclient/capability/resources/IPropertyAccessCapability.java new file mode 100644 index 00000000..7acada7c --- /dev/null +++ b/src/main/java/com/openshift/restclient/capability/resources/IPropertyAccessCapability.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.openshift.restclient.capability.resources; + +import java.util.Map; + +import com.openshift.restclient.capability.ICapability; + +/** + * A mechanism to access the underlying content of a json + * structure + * + * Relies on a dot delimited path to indentify the root of the entity + * being returned (e.g. 'metadata.labels'). This does not provide + * a mechanism to retrieve properties across an array kind. + * + * @author jeff.cantrill + * + */ +public interface IPropertyAccessCapability extends ICapability { + + /** + * + * @param path + * @return + * + * @throws @{@link UnresolvablePathException} when the path + * does not resolve to an existing node + */ + Map asMap(String path); + + /** + * Return the string value to the path + * @param path + * @return + * @throws @{@link UnresolvablePathException} when the path + * does not resolve to an existing node + */ + String asString(String path); + + /** + * The exception thrown when a path given to the capability is + * unresolvable + * + * @author jeff.cantrill + * + */ + public static class UnresolvablePathException extends RuntimeException{ + + /** + * + */ + private static final long serialVersionUID = 2422016683166925224L; + + + } +} diff --git a/src/test/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapabilityTest.java b/src/test/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapabilityTest.java new file mode 100644 index 00000000..7c1b0e75 --- /dev/null +++ b/src/test/java/com/openshift/internal/restclient/capability/resources/PropertyAccessCapabilityTest.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.openshift.internal.restclient.capability.resources; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.dmr.ModelNode; +import org.junit.Before; +import org.junit.Test; + +import com.openshift.internal.restclient.model.BuildConfig; +import com.openshift.restclient.IClient; +import com.openshift.restclient.capability.resources.IPropertyAccessCapability; +import com.openshift.restclient.capability.resources.IPropertyAccessCapability.UnresolvablePathException; +import com.openshift.restclient.utils.Samples; + +public class PropertyAccessCapabilityTest { + + private ModelNode node; + private IPropertyAccessCapability cap; + + @Before + public void setup() { + IClient client = mock(IClient.class); + node = ModelNode.fromJSONString(Samples.V1_BUILD_CONFIG.getContentAsString()); + node.get(new String[] {"spec", "strategy","sourceStrategy", "xyz"}).set(1986); + BuildConfig config = new BuildConfig(node, client, new HashMap<>()); + cap = new PropertyAccessCapability(config); + } + @Test(expected=UnresolvablePathException.class) + public void testAsMapWhenPathIsNotFound() { + cap.asMap("foo.strategy.sourceStrategy.from"); + } + + @Test + public void testAsString() { + assertEquals("1986", cap.asString("spec.strategy.sourceStrategy.xyz")); + } + + @Test(expected=UnresolvablePathException.class) + public void testAsWhenPathIsNotFound() { + cap.asString("spec.strategy.sourceStrategy.xyzzz"); + } + + @Test + public void testAsMap() { + Map from = new HashMap<>(); + from.put("kind","ImageStreamTag"); + from.put("name","ruby-20-centos7:latest"); + Map exp = new HashMap<>(); + exp.put("from", from); + exp.put("incremental", true); + exp.put("scripts", "aLocation"); + exp.put("xyz", 1986); + + // @TODO Why doesnt this validate? +// Map envEntry = new HashMap<>(); +// envEntry.put("name", "foo"); +// envEntry.put("value", "bar"); +// List env = new ArrayList<>(); +// env.add(envEntry); +// +// exp.put("env", env); + Map act = cap.asMap("spec.strategy.sourceStrategy.from"); + assertEquals(from, act); + } + +}