Repository: camel Updated Branches: refs/heads/master 34a7afbf6 -> 4d6e8f187
CAMEL-11667: Support Metadata component extension This simplifies and unifies the two modes of metadata gathering. Now if `sObjectName` is specified a (very simple) JSON schema is returned rather than custom JSON object. Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/4d6e8f18 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/4d6e8f18 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/4d6e8f18 Branch: refs/heads/master Commit: 4d6e8f187110a44558df06774453c8f4441590b0 Parents: 34a7afb Author: Zoran Regvart <zregv...@apache.org> Authored: Fri Sep 8 17:33:11 2017 +0200 Committer: Zoran Regvart <zregv...@apache.org> Committed: Fri Sep 8 17:33:32 2017 +0200 ---------------------------------------------------------------------- .../salesforce/SalesforceMetaDataExtension.java | 127 ++++++------------- .../salesforce/api/utils/JsonUtils.java | 17 +++ .../SalesforceMetaDataExtensionTest.java | 61 +++++---- 3 files changed, 93 insertions(+), 112 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/4d6e8f18/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtension.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtension.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtension.java index abf5201..8c46e14 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtension.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtension.java @@ -18,119 +18,77 @@ package org.apache.camel.component.salesforce; import java.io.IOException; import java.io.InputStream; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.function.BiFunction; import java.util.function.Consumer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; -import org.apache.camel.CamelContext; import org.apache.camel.component.extension.metadata.AbstractMetaDataExtension; import org.apache.camel.component.extension.metadata.MetaDataBuilder; +import org.apache.camel.component.salesforce.api.dto.GlobalObjects; import org.apache.camel.component.salesforce.api.dto.SObjectDescription; import org.apache.camel.component.salesforce.api.utils.JsonUtils; import org.apache.camel.component.salesforce.internal.client.RestClient; import org.apache.camel.component.salesforce.internal.client.RestClient.ResponseCallback; import org.apache.camel.util.ObjectHelper; -import static org.apache.camel.component.salesforce.SalesforceClientTemplate.invoke; - public class SalesforceMetaDataExtension extends AbstractMetaDataExtension { - static final String OBJECT_TYPE = SalesforceEndpointConfig.SOBJECT_NAME; - - public enum Scope implements MetaDataOperation { - OBJECT( - (client, params) -> callback -> client.getDescription(stringParam(params, OBJECT_TYPE).get(), callback)) { - @Override - public MetaData map(final CamelContext camelContext, final InputStream stream) throws IOException { - final SObjectDescription description = MAPPER.readerFor(SObjectDescription.class).readValue(stream); - - final JsonSchema payload = JsonUtils.getSObjectJsonSchemaAsSchema(description, true); - - return MetaDataBuilder.on(camelContext)// - .withAttribute(MetaData.CONTENT_TYPE, "application/schema+json")// - .withAttribute(MetaData.JAVA_TYPE, JsonNode.class)// - .withAttribute("scope", "object").withPayload(payload).build(); - } - }, - OBJECT_TYPES((client, params) -> callback -> client.getGlobalObjects(callback)) { - @Override - public MetaData map(final CamelContext camelContext, final InputStream stream) throws IOException { - final JsonNode rawResponse = MAPPER.readTree(stream); - final JsonNode payload = rawResponse.get("sobjects"); - - return MetaDataBuilder.on(camelContext)// - .withAttribute(MetaData.CONTENT_TYPE, "application/json")// - .withAttribute(MetaData.JAVA_TYPE, JsonNode.class)// - .withAttribute("scope", "object_types").withPayload(payload).build(); - } - }; - - private final BiFunction<RestClient, Map<String, Object>, Consumer<ResponseCallback>> method; - - private Scope(final BiFunction<RestClient, Map<String, Object>, Consumer<ResponseCallback>> method) { - this.method = method; - } - - public static Scope valueOf(final Object given) { - if (given instanceof Scope) { - return (Scope) given; - } - - if (given instanceof String) { - return Scope.valueOf((String) given); - } - - return Scope.valueOf(String.valueOf(given)); - } - } - - interface MetaDataOperation { - - MetaData map(CamelContext camelContext, InputStream stream) throws IOException; + @FunctionalInterface + interface SchemaMapper { + JsonSchema map(InputStream stream) throws IOException; } private static final ObjectMapper MAPPER = JsonUtils.createObjectMapper(); @Override public Optional<MetaData> meta(final Map<String, Object> parameters) { - final Optional<String> objectType = stringParam(parameters, OBJECT_TYPE); + final JsonSchema schema = schemaFor(parameters); - final Scope scope = objectType.map(v -> Scope.OBJECT).orElse(Scope.OBJECT_TYPES); - - final MetaData metaData = metaInternal(scope, parameters); + final MetaData metaData = MetaDataBuilder.on(getCamelContext())// + .withAttribute(MetaData.CONTENT_TYPE, "application/schema+json")// + .withAttribute(MetaData.JAVA_TYPE, JsonNode.class)// + .withPayload(schema).build(); return Optional.ofNullable(metaData); } - MetaData metaInternal(final Scope scope, final Map<String, Object> parameters) { - final CamelContext camelContext = getCamelContext(); + JsonSchema allObjectsSchema(final Map<String, Object> parameters) throws Exception { + return SalesforceClientTemplate.invoke(getCamelContext(), parameters, client -> fetchAllObjectsSchema(client)); + } + JsonSchema schemaFor(final Map<String, Object> parameters) { try { - return invoke(camelContext, parameters, - client -> fetchMetadata(camelContext, scope.method.apply(client, parameters), scope::map)); + if (parameters.containsKey(SalesforceEndpointConfig.SOBJECT_NAME)) { + return singleObjectSchema(parameters); + } + + return allObjectsSchema(parameters); } catch (final Exception e) { throw ObjectHelper.wrapRuntimeCamelException(e); } + } - static MetaData fetchMetadata(final CamelContext camelContext, final Consumer<ResponseCallback> restMethod, - final MetaDataOperation callback) { - final CompletableFuture<MetaData> ret = new CompletableFuture<>(); + JsonSchema singleObjectSchema(final Map<String, Object> parameters) throws Exception { + return SalesforceClientTemplate.invoke(getCamelContext(), parameters, + client -> fetchSingleObjectSchema(client, (String) parameters.get(SalesforceEndpointConfig.SOBJECT_NAME))); + } + + static JsonSchema fetch(final Consumer<ResponseCallback> restMethod, final SchemaMapper callback) { + final CompletableFuture<JsonSchema> ret = new CompletableFuture<>(); restMethod.accept((response, exception) -> { if (exception != null) { ret.completeExceptionally(exception); } else { - try { - ret.complete(callback.map(camelContext, response)); + try (final InputStream is = response) { + ret.complete(callback.map(is)); } catch (final IOException e) { ret.completeExceptionally(e); } @@ -144,26 +102,25 @@ public class SalesforceMetaDataExtension extends AbstractMetaDataExtension { } } - static Map<String, Object> mapOf(final String k1, final Object v1, final String k2, final Object v2) { - final Map<String, Object> ret = new HashMap<>(2); - - ret.put(k1, v1); - ret.put(k2, v2); + static JsonSchema fetchAllObjectsSchema(final RestClient client) { + return fetch(callback -> client.getGlobalObjects(callback), SalesforceMetaDataExtension::mapAllObjectsSchema); + } - return ret; + static JsonSchema fetchSingleObjectSchema(final RestClient client, final String objectName) { + return fetch(callback -> client.getDescription(objectName, callback), + SalesforceMetaDataExtension::mapSingleObjectSchema); } - static Optional<String> stringParam(final Map<String, Object> params, final String name) { - final Object value = params.get(name); + static JsonSchema mapAllObjectsSchema(final InputStream stream) throws IOException { + final GlobalObjects globalObjects = MAPPER.readerFor(GlobalObjects.class).readValue(stream); - if (value == null) { - return Optional.empty(); - } + return JsonUtils.getGlobalObjectsJsonSchemaAsSchema(globalObjects); + } - if (value instanceof String) { - return Optional.of((String) value); - } + static JsonSchema mapSingleObjectSchema(final InputStream stream) throws IOException { + final SObjectDescription description = MAPPER.readerFor(SObjectDescription.class).readValue(stream); - throw new IllegalArgumentException("Expecting parameter `" + name + "` to be of String type, got: " + value); + return JsonUtils.getSObjectJsonSchemaAsSchema(description, true); } + } http://git-wip-us.apache.org/repos/asf/camel/blob/4d6e8f18/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 cc3f7c1..415bb61 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 @@ -46,7 +46,9 @@ 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.GlobalObjects; import org.apache.camel.component.salesforce.api.dto.PickListValue; +import org.apache.camel.component.salesforce.api.dto.SObject; import org.apache.camel.component.salesforce.api.dto.SObjectDescription; import org.apache.camel.component.salesforce.api.dto.SObjectField; import org.apache.camel.impl.DefaultPackageScanClassResolver; @@ -291,4 +293,19 @@ public abstract class JsonUtils { return new JsonSchemaGenerator(objectMapper).generateSchema(type).asObjectSchema(); } + public static JsonSchema getGlobalObjectsJsonSchemaAsSchema(final GlobalObjects globalObjects) { + final Set<JsonSchema> allSchemas = new HashSet<>(); + + for (SObject sobject : globalObjects.getSobjects()) { + // generate SObject schema from description + ObjectSchema sobjectSchema = new ObjectSchema(); + sobjectSchema.setId(DEFAULT_ID_PREFIX + ":" + sobject.getName()); + sobjectSchema.setTitle(sobject.getLabel()); + + allSchemas.add(sobjectSchema); + } + + return getJsonSchemaAsSchema(allSchemas, DEFAULT_ID_PREFIX + ":GlobalObjects"); + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/4d6e8f18/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtensionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtensionTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtensionTest.java index e19f7cf..b0a86b8 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtensionTest.java +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/SalesforceMetaDataExtensionTest.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema; import org.apache.camel.component.extension.MetaDataExtension; import org.apache.camel.component.extension.MetaDataExtension.MetaData; +import org.apache.camel.component.salesforce.api.utils.JsonUtils; import org.apache.camel.component.salesforce.internal.client.RestClient; import org.apache.camel.component.salesforce.internal.client.RestClient.ResponseCallback; import org.apache.camel.impl.DefaultCamelContext; @@ -68,7 +69,7 @@ public class SalesforceMetaDataExtensionTest { try (InputStream stream = resource("/objectDescription.json")) { doAnswer(provideStreamToCallback(stream)).when(restClient).getDescription(eq("Account"), any(ResponseCallback.class)); - maybeMeta = metadata.meta(Collections.singletonMap(SalesforceMetaDataExtension.OBJECT_TYPE, "Account")); + maybeMeta = metadata.meta(Collections.singletonMap(SalesforceEndpointConfig.SOBJECT_NAME, "Account")); } assertThat(maybeMeta).isPresent(); @@ -80,15 +81,8 @@ public class SalesforceMetaDataExtensionTest { final ObjectSchema payload = meta.getPayload(ObjectSchema.class); assertThat(payload).isNotNull(); - @SuppressWarnings({"unchecked", "rawtypes"}) - final Set<JsonSchema> oneOf = (Set) payload.getOneOf(); - final Optional<JsonSchema> merchandiseSchema = StreamSupport.stream(oneOf.spliterator(), false) - .filter(idMatches("urn:jsonschema:org:apache:camel:component:salesforce:dto:Merchandise__c")).findAny(); - final Optional<JsonSchema> merchandiseQuerySchema = StreamSupport.stream(oneOf.spliterator(), false) - .filter(idMatches("urn:jsonschema:org:apache:camel:component:salesforce:dto:QueryRecordsMerchandise__c")) - .findAny(); - assertThat(merchandiseSchema).isPresent(); - assertThat(merchandiseQuerySchema).isPresent(); + assertThat(schemaFor(payload, "Merchandise__c")).isPresent(); + assertThat(schemaFor(payload, "QueryRecordsMerchandise__c")).isPresent(); } @Test @@ -103,26 +97,23 @@ public class SalesforceMetaDataExtensionTest { final MetaData meta = maybeMeta.get(); assertThat(meta.getAttribute(MetaDataExtension.MetaData.JAVA_TYPE)).isEqualTo(JsonNode.class); - assertThat(meta.getAttribute(MetaDataExtension.MetaData.CONTENT_TYPE)).isEqualTo("application/json"); + assertThat(meta.getAttribute(MetaDataExtension.MetaData.CONTENT_TYPE)).isEqualTo("application/schema+json"); - final JsonNode payload = meta.getPayload(JsonNode.class); + final ObjectSchema payload = meta.getPayload(ObjectSchema.class); assertThat(payload).isNotNull(); - assertThat(valueAt(payload, 0, "name")).isEqualTo("AcceptedEventRelation"); - assertThat(valueAt(payload, 0, "label")).isEqualTo("Accepted Event Relation"); - assertThat(valueAt(payload, 1, "name")).isEqualTo("Account"); - assertThat(valueAt(payload, 1, "label")).isEqualTo("Account"); - assertThat(valueAt(payload, 2, "name")).isEqualTo("AccountCleanInfo"); - assertThat(valueAt(payload, 2, "label")).isEqualTo("Account Clean Info"); - assertThat(valueAt(payload, 3, "name")).isEqualTo("AccountContactRole"); - assertThat(valueAt(payload, 3, "label")).isEqualTo("Account Contact Role"); - } - static InputStream resource(final String path) { - return SalesforceMetaDataExtensionTest.class.getResourceAsStream(path); - } - - static String valueAt(final JsonNode payload, final int idx, final String name) { - return payload.get(idx).get(name).asText(); + @SuppressWarnings({"unchecked", "rawtypes"}) + final Set<JsonSchema> oneOf = (Set) payload.getOneOf(); + assertThat(oneOf).hasSize(4); + + assertThat(schemaFor(payload, "AcceptedEventRelation")).isPresent() + .hasValueSatisfying(schema -> assertThat(schema.getTitle()).isEqualTo("Accepted Event Relation")); + assertThat(schemaFor(payload, "Account")).isPresent() + .hasValueSatisfying(schema -> assertThat(schema.getTitle()).isEqualTo("Account")); + assertThat(schemaFor(payload, "AccountCleanInfo")).isPresent() + .hasValueSatisfying(schema -> assertThat(schema.getTitle()).isEqualTo("Account Clean Info")); + assertThat(schemaFor(payload, "AccountContactRole")).isPresent() + .hasValueSatisfying(schema -> assertThat(schema.getTitle()).isEqualTo("Account Contact Role")); } static Answer<Void> provideStreamToCallback(final InputStream stream) { @@ -135,6 +126,22 @@ public class SalesforceMetaDataExtensionTest { }; } + static InputStream resource(final String path) { + return SalesforceMetaDataExtensionTest.class.getResourceAsStream(path); + } + + static Optional<ObjectSchema> schemaFor(final ObjectSchema schema, final String sObjectName) { + @SuppressWarnings({"unchecked", "rawtypes"}) + final Set<ObjectSchema> oneOf = (Set) schema.getOneOf(); + + return StreamSupport.stream(oneOf.spliterator(), false) + .filter(idMatches(JsonUtils.DEFAULT_ID_PREFIX + ":" + sObjectName)).findAny(); + } + + static String valueAt(final JsonNode payload, final int idx, final String name) { + return payload.get(idx).get(name).asText(); + } + private static Predicate<JsonSchema> idMatches(final String wantedId) { return schema -> wantedId.equals(schema.getId()); }