You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by dh...@apache.org on 2017/08/03 08:35:50 UTC

[2/8] camel git commit: CAMEL-10744: Added utility APIs in Salesforce JsonUtils to generate JSON schema from SObjectDescription, added required modules to Karaf camel-salesforce feature

CAMEL-10744: Added utility APIs in Salesforce JsonUtils to generate JSON schema from SObjectDescription, added required modules to Karaf camel-salesforce feature


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/e0fe1df7
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/e0fe1df7
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/e0fe1df7

Branch: refs/heads/camel-2.19.x
Commit: e0fe1df75a383ebea78a40125d0d55654ce4f31e
Parents: 6f9ed73
Author: Dhiraj Bokde <dh...@yahoo.com>
Authored: Thu Aug 3 00:12:19 2017 -0700
Committer: Dhiraj Bokde <dh...@yahoo.com>
Committed: Thu Aug 3 01:13:01 2017 -0700

----------------------------------------------------------------------
 .../camel-salesforce-component/pom.xml          |  15 ++
 .../salesforce/api/utils/JsonUtils.java         | 225 +++++++++++++++++++
 .../salesforce/api/utils/JsonUtilsTest.java     |  56 +++++
 parent/pom.xml                                  |   6 +
 .../features/src/main/resources/features.xml    |   2 +
 5 files changed, 304 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/e0fe1df7/components/camel-salesforce/camel-salesforce-component/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/pom.xml b/components/camel-salesforce/camel-salesforce-component/pom.xml
index b3b0214..f258da5 100644
--- a/components/camel-salesforce/camel-salesforce-component/pom.xml
+++ b/components/camel-salesforce/camel-salesforce-component/pom.xml
@@ -88,6 +88,15 @@
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
     </dependency>
+    <!-- json schema -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-jsonSchema</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.thoughtworks.xstream</groupId>
       <artifactId>xstream</artifactId>
@@ -169,6 +178,12 @@
       <version>2.2</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <version>${squareup-okhttp3-version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/camel/blob/e0fe1df7/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
index 2c3a04f..959e00b 100644
--- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
+++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/utils/JsonUtils.java
@@ -16,13 +16,50 @@
  */
 package org.apache.camel.component.salesforce.api.utils;
 
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.joining;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
+import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
+import com.fasterxml.jackson.module.jsonSchema.types.BooleanSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.IntegerSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.NullSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.SimpleTypeSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.StringSchema;
+
+import org.apache.camel.component.salesforce.api.dto.AbstractDTOBase;
+import org.apache.camel.component.salesforce.api.dto.AbstractQueryRecordsBase;
+import org.apache.camel.component.salesforce.api.dto.Address;
+import org.apache.camel.component.salesforce.api.dto.GeoLocation;
+import org.apache.camel.component.salesforce.api.dto.PickListValue;
+import org.apache.camel.component.salesforce.api.dto.SObjectDescription;
+import org.apache.camel.component.salesforce.api.dto.SObjectField;
+import org.apache.camel.impl.DefaultPackageScanClassResolver;
 
 /**
  * Factory class for creating {@linkplain com.fasterxml.jackson.databind.ObjectMapper}
  */
 public abstract class JsonUtils {
+
+    public static final String SCHEMA4 = "http://json-schema.org/draft-04/schema#";
+    public static final String DEFAULT_ID_PREFIX = "urn:jsonschema:org:apache:camel:component:salesforce:dto";
+
+    private static final String API_DTO_ID = "org:urn:jsonschema:org:apache:camel:component:salesforce:api:dto";
+
     public static ObjectMapper createObjectMapper() {
         // enable date time support including Java 1.8 ZonedDateTime
         ObjectMapper objectMapper = new ObjectMapper();
@@ -31,4 +68,192 @@ public abstract class JsonUtils {
         return objectMapper;
     }
 
+    public static String getBasicApiJsonSchema() throws JsonProcessingException {
+        ObjectMapper mapper = createSchemaObjectMapper();
+        JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper);
+
+        DefaultPackageScanClassResolver packageScanClassResolver = new DefaultPackageScanClassResolver();
+
+        Set<Class<?>> schemaClasses = new HashSet<>();
+
+        // get non-abstract extensions of AbstractDTOBase
+        schemaClasses.addAll(packageScanClassResolver.findByFilter(
+            type -> !Modifier.isAbstract(type.getModifiers())
+                    && AbstractDTOBase.class.isAssignableFrom(type),
+                "org.apache.camel.component.salesforce.api.dto"));
+
+        // get non-abstract extensions of AbstractDTOBase
+        schemaClasses.addAll(packageScanClassResolver.findByFilter(
+            type -> !Modifier.isAbstract(type.getModifiers())
+                    && AbstractDTOBase.class.isAssignableFrom(type),
+                "org.apache.camel.component.salesforce.api.dto"));
+
+        Set<Object> allSchemas = new HashSet<>();
+        for (Class<?> aClass : schemaClasses) {
+            JsonSchema jsonSchema = schemaGen.generateSchema(aClass);
+            allSchemas.add(jsonSchema);
+        }
+
+        return getJsonSchemaString(mapper, allSchemas, API_DTO_ID);
+    }
+
+    public static String getJsonSchemaString(ObjectMapper mapper, Set<Object> allSchemas, String id) throws JsonProcessingException {
+        ObjectSchema rootSchema = new ObjectSchema();
+        rootSchema.set$schema(SCHEMA4);
+        rootSchema.setId(id);
+        rootSchema.setOneOf(allSchemas);
+
+        return mapper.writeValueAsString(rootSchema);
+    }
+
+    public static String getSObjectJsonSchema(SObjectDescription description) throws JsonProcessingException {
+        return getSObjectJsonSchema(description, true);
+    }
+
+    public static String getSObjectJsonSchema(SObjectDescription description, boolean addQuerySchema) throws JsonProcessingException {
+        ObjectMapper schemaObjectMapper = createSchemaObjectMapper();
+        return getJsonSchemaString(schemaObjectMapper, getSObjectJsonSchema(schemaObjectMapper, description, DEFAULT_ID_PREFIX, addQuerySchema), DEFAULT_ID_PREFIX);
+    }
+
+    public static Set<Object> getSObjectJsonSchema(ObjectMapper objectMapper, SObjectDescription description, String idPrefix, boolean addQuerySchema) throws JsonProcessingException {
+        Set<Object> allSchemas = new HashSet<>();
+
+        // generate SObject schema from description
+        ObjectSchema sobjectSchema = new ObjectSchema();
+        sobjectSchema.setId(idPrefix + ":" + description.getName());
+        sobjectSchema.setTitle(description.getLabel());
+
+        SimpleTypeSchema addressSchema = null;
+        SimpleTypeSchema geoLocationSchema = null;
+
+        for (SObjectField field : description.getFields()) {
+
+            SimpleTypeSchema fieldSchema = new NullSchema();
+            String soapType = field.getSoapType();
+
+            switch (soapType.substring(soapType.indexOf(':') + 1)) {
+            case "ID": // mapping for tns:ID SOAP type
+            case "string":
+            case "base64Binary":
+                // Salesforce maps any types like string, picklist, reference, etc. to string
+            case "anyType":
+                fieldSchema = new StringSchema();
+                break;
+
+            case "integer":
+            case "int":
+            case "long":
+            case "short":
+            case "byte":
+            case "unsignedInt":
+            case "unsignedShort":
+            case "unsignedByte":
+                fieldSchema = new IntegerSchema();
+                break;
+
+            case "decimal":
+            case "float":
+            case "double":
+                fieldSchema = new NumberSchema();
+                break;
+
+            case "boolean":
+                fieldSchema = new BooleanSchema();
+                break;
+
+            case "dateTime":
+            case "time":
+            case "date":
+            case "g":
+                fieldSchema = new StringSchema();
+                ((StringSchema) fieldSchema).setFormat(JsonValueFormat.DATE_TIME);
+                break;
+
+            case "address":
+                if (addressSchema == null) {
+                    addressSchema = getSchemaFromClass(objectMapper, Address.class);
+                }
+                fieldSchema = addressSchema;
+                break;
+
+            case "location":
+                if (geoLocationSchema == null) {
+                    geoLocationSchema = getSchemaFromClass(objectMapper, GeoLocation.class);
+                }
+                fieldSchema = geoLocationSchema;
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unsupported type " + soapType);
+            }
+
+            List<PickListValue> picklistValues = field.getPicklistValues();
+            switch (field.getType()) {
+            case "picklist":
+                fieldSchema.asStringSchema().setEnums(
+                        picklistValues == null ? Collections.EMPTY_SET : picklistValues.stream()
+                                .map(PickListValue::getValue)
+                                .distinct()
+                                .collect(Collectors.toSet()));
+                break;
+
+            case "multipicklist":
+                // TODO regex needs more work to not allow values not separated by ','
+                fieldSchema.asStringSchema().setPattern(picklistValues == null ? "" : picklistValues.stream()
+                        .map(val -> "(,?(" + val.getValue() + "))")
+                        .distinct()
+                        .collect(joining("|", "(", ")")));
+                break;
+
+            default:
+                // nothing to fix
+            }
+
+            // additional field properties
+            fieldSchema.setTitle(field.getLabel());
+            fieldSchema.setDefault(field.getDefaultValue());
+            if (field.isUpdateable() != null) {
+                fieldSchema.setReadonly(!field.isUpdateable());
+            }
+
+            // add property to sobject schema
+            if (field.isNillable()) {
+                sobjectSchema.putOptionalProperty(field.getName(), fieldSchema);
+            } else {
+                sobjectSchema.putProperty(field.getName(), fieldSchema);
+            }
+        }
+
+        // add sobject schema to root schema
+        allSchemas.add(sobjectSchema);
+
+        if (addQuerySchema) {
+            // add a simple query schema for this sobject for lookups, etc.
+            ObjectSchema queryRecords = getSchemaFromClass(objectMapper, AbstractQueryRecordsBase.class);
+            queryRecords.setId(idPrefix + ":QueryRecords" + description.getName());
+
+            ObjectSchema refSchema = new ObjectSchema();
+            refSchema.set$ref(sobjectSchema.getId());
+
+            ArraySchema recordsProperty = new ArraySchema();
+            recordsProperty.setItems(new ArraySchema.SingleItems(refSchema));
+            queryRecords.putProperty("records", recordsProperty);
+
+            allSchemas.add(queryRecords);
+        }
+
+        return allSchemas;
+    }
+
+    public static ObjectMapper createSchemaObjectMapper() {
+        ObjectMapper objectMapper = createObjectMapper();
+        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
+        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+        return objectMapper;
+    }
+
+    private static ObjectSchema getSchemaFromClass(ObjectMapper objectMapper, Class<?> type) throws JsonMappingException {
+        return new JsonSchemaGenerator(objectMapper).generateSchema(type).asObjectSchema();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/e0fe1df7/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/api/utils/JsonUtilsTest.java
----------------------------------------------------------------------
diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/api/utils/JsonUtilsTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/api/utils/JsonUtilsTest.java
new file mode 100644
index 0000000..cd59a9e
--- /dev/null
+++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/api/utils/JsonUtilsTest.java
@@ -0,0 +1,56 @@
+package org.apache.camel.component.salesforce.api.utils;
+
+import org.apache.camel.component.salesforce.api.dto.SObjectDescription;
+import org.apache.camel.component.salesforce.dto.generated.Account;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit test for {@link JsonUtils}
+ */
+public class JsonUtilsTest {
+
+    public static final Logger LOG = LoggerFactory.getLogger(JsonUtilsTest.class);
+
+    @Test
+    public void getBasicApiJsonSchema() throws Exception {
+
+        // create basic api dto schema
+        LOG.info("Basic Api Schema...");
+        String basicApiJsonSchema = JsonUtils.getBasicApiJsonSchema();
+        LOG.info(basicApiJsonSchema);
+
+        // parse schema to validate
+        ObjectMapper objectMapper = JsonUtils.createObjectMapper();
+        JsonSchema jsonSchema = objectMapper.readValue(basicApiJsonSchema, JsonSchema.class);
+        assertTrue(jsonSchema.isObjectSchema());
+        assertFalse(((ObjectSchema)jsonSchema).getOneOf().isEmpty());
+    }
+
+    @Test
+    public void getSObjectJsonSchema() throws Exception {
+
+        // create sobject dto schema
+        SObjectDescription description = new Account().description();
+
+        LOG.info("SObject Schema...");
+        String sObjectJsonSchema = JsonUtils.getSObjectJsonSchema(description);
+        LOG.info(sObjectJsonSchema);
+
+        // parse schema to validate
+        ObjectMapper objectMapper = JsonUtils.createObjectMapper();
+        JsonSchema jsonSchema = objectMapper.readValue(sObjectJsonSchema, JsonSchema.class);
+        assertTrue(jsonSchema.isObjectSchema());
+        assertEquals(2, ((ObjectSchema)jsonSchema).getOneOf().size());
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/camel/blob/e0fe1df7/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 6714d0d..cfbc5e4 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -622,6 +622,7 @@
     <spymemcached-bundle-version>2.5_2</spymemcached-bundle-version> <!-- FIXME cmueller: not in sync! -->
     <spymemcached-version>2.12.0</spymemcached-version>
     <squareup-okhttp-version>2.7.5</squareup-okhttp-version>
+    <squareup-okhttp3-version>3.8.1</squareup-okhttp3-version>
     <squareup-okhttp-bundle-version>2.7.5_1</squareup-okhttp-bundle-version>
     <squareup-okio-version>1.11.0</squareup-okio-version>
     <squareup-okio-bundle-version>1.11.0_1</squareup-okio-bundle-version>
@@ -3457,6 +3458,11 @@
         <artifactId>jackson-module-jaxb-annotations</artifactId>
         <version>${jackson2-version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.fasterxml.jackson.module</groupId>
+        <artifactId>jackson-module-jsonSchema</artifactId>
+        <version>${jackson2-version}</version>
+      </dependency>
 
       <dependency>
         <groupId>io.netty</groupId>

http://git-wip-us.apache.org/repos/asf/camel/blob/e0fe1df7/platforms/karaf/features/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/platforms/karaf/features/src/main/resources/features.xml b/platforms/karaf/features/src/main/resources/features.xml
index a6e2213..1f23fda 100644
--- a/platforms/karaf/features/src/main/resources/features.xml
+++ b/platforms/karaf/features/src/main/resources/features.xml
@@ -1574,6 +1574,8 @@
     <bundle dependency='true'>mvn:com.fasterxml.jackson.core/jackson-core/${jackson2-version}</bundle>
     <bundle dependency='true'>mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson2-version}</bundle>
     <bundle dependency='true'>mvn:com.fasterxml.jackson.core/jackson-databind/${jackson2-version}</bundle>
+    <bundle dependency='true'>mvn:com.fasterxml.jackson.core/jackson-jaxrs-json-provider/${jackson2-version}</bundle>
+    <bundle dependency='true'>mvn:com.fasterxml.jackson.core/jackson-module-jsonSchema/${jackson2-version}</bundle>
     <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xstream/${xstream-bundle-version}</bundle>
     <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xpp3/${xpp3-bundle-version}</bundle>
     <bundle dependency='true'>mvn:javax.servlet/javax.servlet-api/${javax.servlet-api-version}</bundle>