result = new ArrayList<>(oDataResponse.getHeader(key));
-
- if (result.size() > 0) {
- return result.get(0);
- } else {
- return null;
- }
-
+ return oDataResponse.getHeader(key).stream().reduce(String::concat).orElse(null);
}
/**
@@ -768,42 +759,39 @@ public static void assertDateTimeOffset(String parameterFieldName, String op, Ti
/**
* Asserts that metadata in the given container are valid. Fetches metadata if not present in the container.
+ *
* @param container a test container with a valid config that metadata can be fetched into
*/
- public static void assertValidXMLMetadata(WebAPITestContainer container) {
+ public static void assertValidXMLMetadata(WebAPITestContainer container, Scenario scenario) {
try {
if (!container.getHaveMetadataBeenRequested()) {
//will lazy-load metadata from the server if not yet requested
container.fetchXMLMetadata();
}
container.validateMetadata();
- assertTrue("XML Metadata at the given service root is not valid! " + container.getServiceRoot(),
- container.getIsValidXMLMetadata());
+ if (!container.getIsValidXMLMetadata()) {
+ failAndExitWithErrorMessage("Invalid XML Metadata! Service root: " + container.getServiceRoot(), scenario);
+ }
} catch (Exception ex) {
- fail(getDefaultErrorMessage(ex));
+ failAndExitWithErrorMessage(getDefaultErrorMessage(ex), scenario);
}
}
/**
* Asserts that the given container has XML Metadata that contains an Entity Data Model (Edm)
+ *
* @param container the container with XML metadata to validate
*/
- public static void assertXmlMetadataContainsEdm(WebAPITestContainer container) {
+ public static void assertXmlMetadataContainsEdm(WebAPITestContainer container, Scenario scenario) {
container.setEdm(Commander.deserializeEdm(container.getXMLResponseData(), container.getCommander().getClient()));
- assertNotNull(getDefaultErrorMessage("Edm de-serialized to an empty object!"), container.getEdm());
- }
-
- /**
- * Asserts that the Edm in the given container are valid
- * @param container the container with the XML Metadata to check
- */
- public static void assertValidEdm(WebAPITestContainer container) {
- assertTrue("Edm Metadata at the given service root is not valid! " + container.getServiceRoot(),
- container.getIsValidEdm());
+ if (container.getEdm() == null) {
+ failAndExitWithErrorMessage(getDefaultErrorMessage("Edm de-serialized to an empty object!"), scenario);
+ }
}
/**
* Asserts that XML Metadata are retrieved from the server
+ *
* @param container the container to retrieve metadata with
*/
public static void assertXMLMetadataAreRequestedFromTheServer(WebAPITestContainer container, Scenario scenario) {
@@ -833,35 +821,28 @@ public static void assertXMLMetadataAreRequestedFromTheServer(WebAPITestContaine
}
}
- /**
- * Asserts that the XML Response in the given container is valid XML
- * @param container the container with the XML response to validate
- */
- public static void assertXMLResponseIsValidXML(WebAPITestContainer container) {
- assertNotNull(getDefaultErrorMessage("no XML Response data were found!"), container.getXMLResponseData());
- container.validateXMLMetadataXML();
- assertTrue(getDefaultErrorMessage("invalid XML response!"), container.getIsValidXMLMetadataXML());
- }
-
/**
* Asserts that the XML metadata in the given container has a valid service document
+ *
* @param container the container with XML Metadata to validate
*/
- public static void assertXMLMetadataHasValidServiceDocument(WebAPITestContainer container) {
+ public static void assertXMLMetadataHasValidServiceDocument(WebAPITestContainer container, Scenario scenario) {
try {
- assertNotNull("ERROR: could not find default entity container for given service root: " +
- container.getServiceRoot(), container.getEdm().getEntityContainer());
+ if (container == null || container.getEdm() == null || container.getEdm().getEntityContainer() == null) {
+ failAndExitWithErrorMessage("Could not find default entity container for given service root: " + container.getServiceRoot(), scenario);
+ }
LOG.info("Found Default Entity Container: '" + container.getEdm().getEntityContainer().getNamespace() + "'");
} catch (ODataClientErrorException cex) {
container.setResponseCode(cex.getStatusLine().getStatusCode());
- fail(cex.toString());
+ failAndExitWithErrorMessage(cex.toString(), scenario);
} catch (Exception ex) {
- fail(getDefaultErrorMessage(ex));
+ failAndExitWithErrorMessage(getDefaultErrorMessage(ex), scenario);
}
}
/**
* Asserts that valid Metadata have been retrieved. Fetches metadata if not present.
+ *
* @param container a test container to validate
*/
public static void assertValidMetadataHaveBeenRetrieved(WebAPITestContainer container) {
@@ -880,6 +861,7 @@ public static void assertValidMetadataHaveBeenRetrieved(WebAPITestContainer cont
/**
* Validates that the given response data have a valid OData count
+ *
* @param responseData the data to check for a count against
* @return true if the there is a count present and it's greater than or equal to the number of results
*/
@@ -902,21 +884,21 @@ public static boolean validateODataCount(String responseData) {
* Contains the list of supported operators for use in query expressions.
*/
public static class Operators {
- public static final String
- AND = "and",
- OR = "or",
- NE = "ne",
- EQ = "eq",
- GREATER_THAN = "gt",
- GREATER_THAN_OR_EQUAL = "ge",
- HAS = "has",
- LESS_THAN = "lt",
- LESS_THAN_OR_EQUAL = "le",
- CONTAINS = "contains",
- ENDS_WITH = "endswith",
- STARTS_WITH = "startswith",
- TO_LOWER = "tolower",
- TO_UPPER = "toupper";
+ public static final String
+ AND = "and",
+ OR = "or",
+ NE = "ne",
+ EQ = "eq",
+ GREATER_THAN = "gt",
+ GREATER_THAN_OR_EQUAL = "ge",
+ HAS = "has",
+ LESS_THAN = "lt",
+ LESS_THAN_OR_EQUAL = "le",
+ CONTAINS = "contains",
+ ENDS_WITH = "endswith",
+ STARTS_WITH = "startswith",
+ TO_LOWER = "tolower",
+ TO_UPPER = "toupper";
}
public static final class DateParts {
@@ -965,5 +947,19 @@ public static void failAndExitWithErrorMessage(String msg, Scenario scenario) {
}
System.exit(NOT_OK);
}
+
+ /**
+ * Builds a Data Dictionary Cache
+ *
+ * @return a DDProcessor Cache object
+ */
+ public static DDCacheProcessor buildDataDictionaryCache() {
+ LOG.info("Creating standard field cache...");
+ final DDCacheProcessor cache = new DDCacheProcessor();
+ DataDictionaryCodeGenerator generator = new DataDictionaryCodeGenerator(cache);
+ generator.processWorksheets();
+ LOG.info("Standard field cache created!");
+ return cache;
+ }
}
diff --git a/src/main/java/org/reso/commander/common/Utils.java b/src/main/java/org/reso/commander/common/Utils.java
index f6224255..199544f1 100644
--- a/src/main/java/org/reso/commander/common/Utils.java
+++ b/src/main/java/org/reso/commander/common/Utils.java
@@ -1,18 +1,34 @@
package org.reso.commander.common;
+import com.google.common.base.Functions;
+import com.google.gson.*;
+import io.cucumber.gherkin.internal.com.eclipsesource.json.Json;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.domain.ClientEntity;
+import org.apache.olingo.client.api.serialization.ODataSerializerException;
+import org.apache.olingo.client.core.edm.xml.ClientCsdlAnnotation;
+import org.apache.olingo.client.core.serialization.JsonSerializer;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmAnnotation;
+import org.apache.olingo.commons.api.edm.EdmElement;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.core.edm.EdmAnnotationImpl;
import java.io.File;
import java.io.FileWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Objects;
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
public class Utils {
private static final Logger LOG = LogManager.getLogger(Utils.class);
@@ -30,6 +46,7 @@ public static String getTimestamp(Date date) {
/**
* Gets the current timestamp
+ *
* @return the current timestamp returned as a string
*/
public static String getTimestamp() {
@@ -38,9 +55,10 @@ public static String getTimestamp() {
/**
* Creates a file in the given directory with the given content
+ *
* @param directoryName the directory name to create the file in
- * @param fileName the name of the file to create
- * @param content the content to write to the file
+ * @param fileName the name of the file to create
+ * @param content the content to write to the file
*/
public static File createFile(String directoryName, String fileName, String content) {
if (directoryName == null || fileName == null) return null;
@@ -66,6 +84,7 @@ public static File createFile(String directoryName, String fileName, String cont
/**
* Creates a file in the given directory with the given content
+ *
* @param content the content to write to the file
*/
public static File createFile(String outputPath, String content) {
@@ -88,9 +107,10 @@ public static File createFile(String outputPath, String content) {
/**
* Removes a directory at the given pathToDirectory.
- *
+ *
* If current user has write access then directory creation will result in True being returned.
* Otherwise will return false if the directory couldn't be created for some reason.
+ *
* @param pathToDirectory
* @return
*/
@@ -143,4 +163,17 @@ public static String getIsoTimestamp(OffsetDateTime fromDate) {
return OffsetDateTime.from(fromDate.toInstant()).format(DateTimeFormatter.ISO_INSTANT);
}
+ /**
+ * Gets the difference of two generic sets.
+ * @param a the minuend set
+ * @param b the subtrahend set
+ * @param the type of set
+ * @return Set of type T that contains A \ B
+ */
+ public static Set getDifference(Set a, Set b) {
+ return a.parallelStream()
+ .filter(item -> !b.contains(item))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ }
}
diff --git a/src/main/java/org/reso/commander/jsonSerializers/FieldJson.java b/src/main/java/org/reso/commander/jsonSerializers/FieldJson.java
new file mode 100644
index 00000000..4d92f6bf
--- /dev/null
+++ b/src/main/java/org/reso/commander/jsonSerializers/FieldJson.java
@@ -0,0 +1,177 @@
+package org.reso.commander.jsonSerializers;
+
+import com.google.gson.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.olingo.commons.api.edm.EdmAnnotation;
+import org.apache.olingo.commons.api.edm.EdmElement;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.reso.commander.common.ODataUtils;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Optional;
+
+import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
+
+/**
+ * FieldJson uses a JSON payload with the following structure:
+ *
+ * {
+ * "resourceName": "Property",
+ * "fieldName": "AboveGradeFinishedArea",
+ * "type": "Edm.Decimal"
+ * }
+ */
+public final class FieldJson implements JsonSerializer {
+ private static final Logger LOG = LogManager.getLogger(FieldJson.class);
+
+ static final String
+ RESOURCE_NAME_KEY = "resourceName",
+ FIELD_NAME_KEY = "fieldName",
+ NULLABLE_KEY = "nullable",
+ MAX_LENGTH_KEY = "maxLength",
+ PRECISION_KEY = "precision",
+ SCALE_KEY = "scale",
+ IS_COLLECTION_KEY = "isCollection",
+ DEFAULT_VALUE_KEY = "defaultValue",
+ UNICODE_KEY = "unicode",
+ TYPE_KEY = "type",
+ TERM_KEY = "term",
+ VALUE_KEY = "value",
+ ANNOTATIONS_KEY = "annotations",
+ FIELDS_KEY = "fields";
+
+ String resourceName;
+ EdmElement edmElement;
+
+ /**
+ * Constructor which takes an edmElement and reads the type from it, then
+ * uses it as the resource name.
+ * @param edmElement edmElement to create FieldJson for
+ */
+ public FieldJson(EdmElement edmElement) {
+ Optional element = Optional.ofNullable(edmElement);
+ assert element.isPresent() : "EdmElement cannot be null!";
+ this.edmElement = edmElement;
+
+ Optional resourceName = Optional.ofNullable(edmElement.getType().getName());
+ assert resourceName.isPresent() : "Could not read name from edmElement type!";
+ this.resourceName = resourceName.get();
+ }
+
+ /**
+ * Constructor which takes an edmElement and reads the type from it, then
+ * uses it as the resource name.
+ * @param resourceName the resourceName the element belongs to
+ * @param edmElement edmElement to create FieldJson for
+ */
+ public FieldJson(String resourceName, EdmElement edmElement) {
+ this.resourceName = resourceName;
+ this.edmElement = edmElement;
+ }
+
+ /**
+ * Metadata Pretty Printer
+ * @param metadataReport the metadata report
+ * @return a human-friendly string version of the metadata report
+ */
+ public static String buildReportString(JsonElement metadataReport) {
+ StringBuilder reportBuilder = new StringBuilder();
+ metadataReport.getAsJsonObject().get(FIELDS_KEY).getAsJsonArray().forEach(field -> {
+ reportBuilder.append("\nResource: ");
+ reportBuilder.append(field.getAsJsonObject().get(RESOURCE_NAME_KEY));
+ reportBuilder.append("\nField: ");
+ reportBuilder.append(field.getAsJsonObject().get(FIELD_NAME_KEY));
+ reportBuilder.append("\nType: ");
+ reportBuilder.append(field.getAsJsonObject().get(TYPE_KEY));
+
+ if (field.getAsJsonObject().get(ANNOTATIONS_KEY) != null) {
+ JsonArray annotations = field.getAsJsonObject().get(ANNOTATIONS_KEY).getAsJsonArray();
+ if (annotations != null && annotations.size() > 0) {
+ reportBuilder.append("\n");
+ reportBuilder.append("Annotations:");
+ annotations.forEach(annotation -> {
+ if (annotation.getAsJsonObject().get(TERM_KEY) != null) {
+ reportBuilder.append("\n\tTerm: ");
+ reportBuilder.append(annotation.getAsJsonObject().get(TERM_KEY));
+ }
+
+ if (annotation.getAsJsonObject().get(VALUE_KEY) != null) {
+ reportBuilder.append("\n\tValue: ");
+ reportBuilder.append(annotation.getAsJsonObject().get(VALUE_KEY));
+ }
+ });
+ }
+ }
+ reportBuilder.append("\n");
+ });
+ return reportBuilder.toString();
+ }
+
+ @Override
+ public JsonElement serialize(FieldJson src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject field = new JsonObject();
+
+
+ field.addProperty(RESOURCE_NAME_KEY, src.resourceName);
+ field.addProperty(FIELD_NAME_KEY, src.edmElement.getName());
+
+ String typeName = null;
+ try {
+ typeName = src.edmElement.getType().getFullQualifiedName().getFullQualifiedNameAsString();
+ field.addProperty(TYPE_KEY, typeName);
+ } catch (Exception ex) {
+ LOG.error(getDefaultErrorMessage("Field Name:", src.edmElement.getName(), ex.toString()));
+ field.addProperty(TYPE_KEY, "UNDEFINED");
+ }
+
+ field.addProperty(NULLABLE_KEY, ((EdmProperty) src.edmElement).isNullable());
+ field.addProperty(MAX_LENGTH_KEY, ((EdmProperty) src.edmElement).getMaxLength());
+ field.addProperty(SCALE_KEY, ((EdmProperty) src.edmElement).getScale());
+ field.addProperty(PRECISION_KEY, ((EdmProperty) src.edmElement).getPrecision());
+ field.addProperty(DEFAULT_VALUE_KEY, ((EdmProperty) src.edmElement).getDefaultValue());
+ field.addProperty(IS_COLLECTION_KEY, src.edmElement.isCollection());
+ field.addProperty(UNICODE_KEY, ((EdmProperty) src.edmElement).isUnicode());
+
+ //TODO: report issue to Apache
+ // Can only get the annotation term using ((ClientCsdlAnnotation) ((EdmAnnotationImpl)edmAnnotation).annotatable).term
+ // which a private member and cannot be accessed
+ List annotations = ((EdmProperty) src.edmElement).getAnnotations();
+ if (annotations != null && annotations.size() > 0) {
+ JsonArray annotationsJsonArray = new JsonArray();
+ annotations.forEach(edmAnnotation -> {
+ if (edmAnnotation.getExpression() != null) {
+ if (edmAnnotation.getExpression().isConstant()) {
+ JsonObject annotation = new JsonObject();
+ if (edmAnnotation.getTerm() != null) {
+ annotation.addProperty(TERM_KEY, edmAnnotation.getTerm().getFullQualifiedName().getFullQualifiedNameAsString());
+ } else {
+ ODataUtils.SneakyAnnotationReader sneakyAnnotationReader = new ODataUtils.SneakyAnnotationReader(edmAnnotation);
+ annotation.addProperty(TERM_KEY, sneakyAnnotationReader.getTerm());
+ }
+ annotation.addProperty(VALUE_KEY, edmAnnotation.getExpression().asConstant().getValueAsString());
+ annotationsJsonArray.add(annotation);
+ } else if (edmAnnotation.getExpression().isDynamic()) {
+ if (edmAnnotation.getExpression().asDynamic().isCollection()) {
+ edmAnnotation.getExpression().asDynamic().asCollection().getItems().forEach(edmExpression -> {
+ //OData Allowed Values come across as Records, in which case their key is "Value"
+ if (edmExpression.asDynamic().isRecord()) {
+ JsonObject annotation = new JsonObject();
+ edmExpression.asDynamic().asRecord().getPropertyValues().forEach(edmPropertyValue -> {
+ annotation.addProperty(TERM_KEY, edmPropertyValue.getProperty());
+ annotation.addProperty(VALUE_KEY, edmPropertyValue.getValue().asConstant().getValueAsString());
+ annotationsJsonArray.add(annotation);
+ });
+ }
+ });
+ }
+ }
+ }
+ });
+ if (annotationsJsonArray.size() > 0) field.add(ANNOTATIONS_KEY, annotationsJsonArray);
+ }
+ return field;
+ }
+}
+
diff --git a/src/main/java/org/reso/commander/jsonSerializers/LookupJson.java b/src/main/java/org/reso/commander/jsonSerializers/LookupJson.java
new file mode 100644
index 00000000..8a85b556
--- /dev/null
+++ b/src/main/java/org/reso/commander/jsonSerializers/LookupJson.java
@@ -0,0 +1,101 @@
+package org.reso.commander.jsonSerializers;
+
+import com.google.gson.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.olingo.commons.api.edm.EdmEnumType;
+import org.reso.commander.common.ODataUtils;
+
+import java.lang.reflect.Type;
+
+/**
+ * LookupJson uses a JSON payload with the following structure:
+ *
+ * {
+ * "lookupName": "org.reso.metadata.enums.CommunityFeatures",
+ * "lookupValue": "Stables",
+ * "type": "Edm.Int32"
+ * }
+ */
+public final class LookupJson implements JsonSerializer {
+ private static final Logger LOG = LogManager.getLogger(LookupJson.class);
+
+ public static final String
+ LOOKUP_NAME_KEY = "lookupName", LOOKUP_VALUE_KEY = "lookupValue",
+ TYPE_KEY = "type", VALUE_KEY = "value", ANNOTATIONS_KEY = "annotations",
+ LOOKUPS_KEY = "lookups", TERM_KEY = "term";
+
+ EdmEnumType edmEnumType;
+ String memberName;
+
+ public LookupJson(String memberName, EdmEnumType edmEnumType) {
+ this.edmEnumType = edmEnumType;
+ this.memberName = memberName;
+ }
+
+ /**
+ * Metadata Pretty Printer
+ * @param metadataReport the metadata report
+ * @return a human-friendly string version of the metadata report
+ */
+ public static String buildReportString(JsonElement metadataReport) {
+ StringBuilder reportBuilder = new StringBuilder();
+ metadataReport.getAsJsonObject().get(LOOKUPS_KEY).getAsJsonArray().forEach(field -> {
+ reportBuilder.append("\nLookup Name: ");
+ reportBuilder.append(field.getAsJsonObject().get(LOOKUP_NAME_KEY));
+ reportBuilder.append("\nLookup Value: ");
+ reportBuilder.append(field.getAsJsonObject().get(LOOKUP_VALUE_KEY));
+ reportBuilder.append("\nType: ");
+ reportBuilder.append(field.getAsJsonObject().get(TYPE_KEY));
+
+ if (field.getAsJsonObject().get(ANNOTATIONS_KEY) != null) {
+ JsonArray annotations = field.getAsJsonObject().get(ANNOTATIONS_KEY).getAsJsonArray();
+ if (annotations != null && annotations.size() > 0) {
+ reportBuilder.append("\n");
+ reportBuilder.append("Annotations:");
+ annotations.forEach(annotation -> {
+ if (annotation.getAsJsonObject().get(TERM_KEY) != null) {
+ reportBuilder.append("\n\tTerm: ");
+ reportBuilder.append(annotation.getAsJsonObject().get(TERM_KEY));
+ }
+
+ if (annotation.getAsJsonObject().get(VALUE_KEY) != null) {
+ reportBuilder.append("\n\tValue: ");
+ reportBuilder.append(annotation.getAsJsonObject().get(VALUE_KEY));
+ }
+ });
+ }
+ }
+ reportBuilder.append("\n");
+ });
+ return reportBuilder.toString();
+ }
+
+ @Override
+ public JsonElement serialize(LookupJson src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject membersJsonObject = new JsonObject();
+ membersJsonObject.addProperty(LOOKUP_NAME_KEY, src.edmEnumType.getFullQualifiedName().toString());
+ membersJsonObject.addProperty(LOOKUP_VALUE_KEY, src.memberName);
+ membersJsonObject.addProperty(TYPE_KEY, src.edmEnumType.getUnderlyingType().getFullQualifiedName().getFullQualifiedNameAsString());
+
+ if (src.edmEnumType.getMember(memberName).getAnnotations().size() > 0) {
+ JsonArray annotations = new JsonArray();
+ src.edmEnumType.getMember(memberName).getAnnotations().forEach(edmAnnotation -> {
+ JsonObject annotation = new JsonObject();
+ if (edmAnnotation.getTerm() != null) {
+ annotation.addProperty(TERM_KEY, edmAnnotation.getTerm().getFullQualifiedName().getFullQualifiedNameAsString());
+ } else {
+ ODataUtils.SneakyAnnotationReader sneakyAnnotationReader = new ODataUtils.SneakyAnnotationReader(edmAnnotation);
+ annotation.addProperty(TERM_KEY, sneakyAnnotationReader.getTerm());
+ }
+
+ if (edmAnnotation.getExpression() != null) {
+ annotation.addProperty(VALUE_KEY, edmAnnotation.getExpression().asConstant().getValueAsString());
+ }
+ annotations.add(annotation);
+ });
+ membersJsonObject.add(ANNOTATIONS_KEY, annotations);
+ }
+ return membersJsonObject;
+ }
+}
diff --git a/src/main/java/org/reso/commander/jsonSerializers/MetadataReport.java b/src/main/java/org/reso/commander/jsonSerializers/MetadataReport.java
new file mode 100644
index 00000000..45466442
--- /dev/null
+++ b/src/main/java/org/reso/commander/jsonSerializers/MetadataReport.java
@@ -0,0 +1,83 @@
+package org.reso.commander.jsonSerializers;
+
+import com.google.gson.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.olingo.commons.api.edm.*;
+import org.reso.commander.common.Utils;
+
+import java.lang.reflect.Type;
+import java.util.Date;
+
+import static org.reso.commander.Commander.REPORT_DIVIDER;
+import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
+
+public class MetadataReport implements JsonSerializer {
+ private static final Logger LOG = LogManager.getLogger(MetadataReport.class);
+
+ private Edm metadata;
+
+ private MetadataReport() {
+ //private default constructor
+ }
+
+ public MetadataReport(Edm metadata) {
+ this.metadata = metadata;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder reportBuilder = new StringBuilder();
+
+ reportBuilder
+ .append("\n\n" + REPORT_DIVIDER)
+ .append("\nRESO Metadata Report")
+ .append("\n").append(new Date())
+ .append("\n" + REPORT_DIVIDER);
+
+ JsonElement metadataReport = serialize(this, MetadataReport.class, null);
+ reportBuilder.append(FieldJson.buildReportString(metadataReport));
+ reportBuilder.append(LookupJson.buildReportString(metadataReport));
+
+ return reportBuilder.toString();
+ }
+
+ @Override
+ public JsonElement serialize(MetadataReport src, Type typeOfSrc, JsonSerializationContext context) {
+ final String
+ DESCRIPTION_KEY = "description", DESCRIPTION = "RESO Data Dictionary Metadata Report",
+ VERSION_KEY = "version", VERSION = "1.7",
+ GENERATED_ON_KEY = "generatedOn",
+ FIELDS_KEY = "fields",
+ LOOKUPS_KEY = "lookups";
+
+ JsonArray fields = new JsonArray();
+ JsonArray lookups = new JsonArray();
+
+ src.metadata.getSchemas().forEach(edmSchema -> {
+ //serialize entities (resources) and members (fields)
+ edmSchema.getEntityTypes().forEach(edmEntityType -> {
+ edmEntityType.getPropertyNames().forEach(propertyName -> {
+ FieldJson fieldJson = new FieldJson(edmEntityType.getName(), edmEntityType.getProperty(propertyName));
+ fields.add(fieldJson.serialize(fieldJson, FieldJson.class, null));
+ });
+ });
+
+ //serialize enum types
+ edmSchema.getEnumTypes().forEach(edmEnumType -> {
+ edmEnumType.getMemberNames().forEach(memberName -> {
+ LookupJson lookupJson = new LookupJson(memberName, edmEnumType);
+ lookups.add(lookupJson.serialize(lookupJson, LookupJson.class, null));
+ });
+ });
+ });
+
+ JsonObject metadataReport = new JsonObject();
+ metadataReport.addProperty(DESCRIPTION_KEY, DESCRIPTION);
+ metadataReport.addProperty(VERSION_KEY, VERSION);
+ metadataReport.addProperty(GENERATED_ON_KEY, Utils.getIsoTimestamp());
+ metadataReport.add(FIELDS_KEY, fields);
+ metadataReport.add(LOOKUPS_KEY, lookups);
+ return metadataReport;
+ }
+}
diff --git a/src/main/java/org/reso/models/LookupResourceItem.java b/src/main/java/org/reso/models/LookupResourceItem.java
new file mode 100644
index 00000000..1e8ed859
--- /dev/null
+++ b/src/main/java/org/reso/models/LookupResourceItem.java
@@ -0,0 +1,46 @@
+package org.reso.models;
+
+import java.util.Date;
+
+public class LookupResourceItem {
+ private final String lookupKey;
+ private final String lookupName;
+ private final String lookupValue;
+ private final String lookupStandardName;
+ private final String legacyODataValue;
+ private final Date modificationTimestamp;
+
+ public LookupResourceItem(String lookupKey, String lookupName, String lookupValue,
+ String lookupStandardName, String legacyODataValue, Date modificationTimestamp) {
+ this.lookupKey = lookupKey;
+ this.lookupName = lookupName;
+ this.lookupValue = lookupValue;
+ this.lookupStandardName = lookupStandardName;
+ this.legacyODataValue = legacyODataValue;
+ this.modificationTimestamp = modificationTimestamp;
+ }
+
+ public String getLookupKey() {
+ return lookupKey;
+ }
+
+ public String getLookupName() {
+ return lookupName;
+ }
+
+ public String getLookupValue() {
+ return lookupValue;
+ }
+
+ public String getLookupStandardName() {
+ return lookupStandardName;
+ }
+
+ public String getLegacyODataValue() {
+ return legacyODataValue;
+ }
+
+ public Date getModificationTimestamp() {
+ return modificationTimestamp;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/reso/models/MetadataReport.java b/src/main/java/org/reso/models/MetadataReport.java
deleted file mode 100644
index 2b2d8c05..00000000
--- a/src/main/java/org/reso/models/MetadataReport.java
+++ /dev/null
@@ -1,336 +0,0 @@
-package org.reso.models;
-
-import com.google.gson.*;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.olingo.client.core.edm.xml.ClientCsdlAnnotation;
-import org.apache.olingo.commons.api.edm.*;
-import org.apache.olingo.commons.core.edm.EdmAnnotationImpl;
-import org.reso.commander.common.Utils;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Type;
-import java.util.Date;
-import java.util.List;
-
-import static org.reso.commander.Commander.REPORT_DIVIDER;
-import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage;
-
-public class MetadataReport implements JsonSerializer {
- private static final Logger LOG = LogManager.getLogger(MetadataReport.class);
-
- private Edm metadata;
-
- private MetadataReport() {
- //private default constructor
- }
-
- public MetadataReport(Edm metadata) {
- this.metadata = metadata;
- }
-
- @Override
- public String toString() {
- StringBuilder reportBuilder = new StringBuilder();
-
- reportBuilder
- .append("\n\n" + REPORT_DIVIDER)
- .append("\nRESO Metadata Report")
- .append("\n").append(new Date())
- .append("\n" + REPORT_DIVIDER);
-
- JsonElement metadataReport = serialize(this, MetadataReport.class, null);
- reportBuilder.append(FieldJson.buildReportString(metadataReport));
- reportBuilder.append(LookupJson.buildReportString(metadataReport));
-
- return reportBuilder.toString();
- }
-
- /**
- * FieldJson uses a JSON payload with the following structure:
- *
- * {
- * "resourceName": "Property",
- * "fieldName": "AboveGradeFinishedArea",
- * "type": "Edm.Decimal"
- * }
- */
- private static final class FieldJson implements JsonSerializer {
- static final String
- RESOURCE_NAME_KEY = "resourceName",
- FIELD_NAME_KEY = "fieldName",
- NULLABLE_KEY = "nullable",
- MAX_LENGTH_KEY = "maxLength",
- PRECISION_KEY = "precision",
- SCALE_KEY = "scale",
- IS_COLLECTION_KEY = "isCollection",
- DEFAULT_VALUE_KEY = "defaultValue",
- UNICODE_KEY = "unicode",
- TYPE_KEY = "type",
- TERM_KEY = "term",
- VALUE_KEY = "value",
- ANNOTATIONS_KEY = "annotations",
- FIELDS_KEY = "fields";
-
- String resourceName;
- EdmElement edmElement;
-
- public FieldJson(String resourceName, EdmElement edmElement) {
- this.resourceName = resourceName;
- this.edmElement = edmElement;
- }
-
- public static String buildReportString(JsonElement metadataReport) {
- StringBuilder reportBuilder = new StringBuilder();
- metadataReport.getAsJsonObject().get(FIELDS_KEY).getAsJsonArray().forEach(field -> {
- reportBuilder.append("\nResource: ");
- reportBuilder.append(field.getAsJsonObject().get(RESOURCE_NAME_KEY));
- reportBuilder.append("\nField: ");
- reportBuilder.append(field.getAsJsonObject().get(FIELD_NAME_KEY));
- reportBuilder.append("\nType: ");
- reportBuilder.append(field.getAsJsonObject().get(TYPE_KEY));
-
- if (field.getAsJsonObject().get(ANNOTATIONS_KEY) != null) {
- JsonArray annotations = field.getAsJsonObject().get(ANNOTATIONS_KEY).getAsJsonArray();
- if (annotations != null && annotations.size() > 0) {
- reportBuilder.append("\n");
- reportBuilder.append("Annotations:");
- annotations.forEach(annotation -> {
- if (annotation.getAsJsonObject().get(TERM_KEY) != null) {
- reportBuilder.append("\n\tTerm: ");
- reportBuilder.append(annotation.getAsJsonObject().get(TERM_KEY));
- }
-
- if (annotation.getAsJsonObject().get(VALUE_KEY) != null) {
- reportBuilder.append("\n\tValue: ");
- reportBuilder.append(annotation.getAsJsonObject().get(VALUE_KEY));
- }
- });
- }
- }
- reportBuilder.append("\n");
- });
- return reportBuilder.toString();
- }
-
- @Override
- public JsonElement serialize(FieldJson src, Type typeOfSrc, JsonSerializationContext context) {
- JsonObject field = new JsonObject();
-
-
- field.addProperty(RESOURCE_NAME_KEY, src.resourceName);
- field.addProperty(FIELD_NAME_KEY, src.edmElement.getName());
-
- String typeName = null;
- try {
- typeName = src.edmElement.getType().getFullQualifiedName().getFullQualifiedNameAsString();
- field.addProperty(TYPE_KEY, typeName);
- } catch (Exception ex) {
- LOG.error(getDefaultErrorMessage("Field Name:", src.edmElement.getName(), ex.toString()));
- field.addProperty(TYPE_KEY, "UNDEFINED");
- }
-
- field.addProperty(NULLABLE_KEY, ((EdmProperty) src.edmElement).isNullable());
- field.addProperty(MAX_LENGTH_KEY, ((EdmProperty) src.edmElement).getMaxLength());
- field.addProperty(SCALE_KEY, ((EdmProperty) src.edmElement).getScale());
- field.addProperty(PRECISION_KEY, ((EdmProperty) src.edmElement).getPrecision());
- field.addProperty(DEFAULT_VALUE_KEY, ((EdmProperty) src.edmElement).getDefaultValue());
- field.addProperty(IS_COLLECTION_KEY, src.edmElement.isCollection());
- field.addProperty(UNICODE_KEY, ((EdmProperty) src.edmElement).isUnicode());
-
- //TODO: report issue to Apache
- // Can only get the annotation term using ((ClientCsdlAnnotation) ((EdmAnnotationImpl)edmAnnotation).annotatable).term
- // which a private member and cannot be accessed
- List annotations = ((EdmProperty) src.edmElement).getAnnotations();
- if (annotations != null && annotations.size() > 0) {
- JsonArray annotationsJsonArray = new JsonArray();
- annotations.forEach(edmAnnotation -> {
- if (edmAnnotation.getExpression() != null) {
- if (edmAnnotation.getExpression().isConstant()) {
- JsonObject annotation = new JsonObject();
- if (edmAnnotation.getTerm() != null) {
- annotation.addProperty(TERM_KEY, edmAnnotation.getTerm().getFullQualifiedName().getFullQualifiedNameAsString());
- } else {
- SneakyAnnotationReader sneakyAnnotationReader = new SneakyAnnotationReader(edmAnnotation);
- annotation.addProperty(TERM_KEY, sneakyAnnotationReader.getTerm());
- }
- annotation.addProperty(VALUE_KEY, edmAnnotation.getExpression().asConstant().getValueAsString());
- annotationsJsonArray.add(annotation);
- } else if (edmAnnotation.getExpression().isDynamic()) {
- if (edmAnnotation.getExpression().asDynamic().isCollection()) {
- edmAnnotation.getExpression().asDynamic().asCollection().getItems().forEach(edmExpression -> {
- //OData Allowed Values come across as Records, in which case their key is "Value"
- if (edmExpression.asDynamic().isRecord()) {
- JsonObject annotation = new JsonObject();
- edmExpression.asDynamic().asRecord().getPropertyValues().forEach(edmPropertyValue -> {
- annotation.addProperty(TERM_KEY, edmPropertyValue.getProperty());
- annotation.addProperty(VALUE_KEY, edmPropertyValue.getValue().asConstant().getValueAsString());
- annotationsJsonArray.add(annotation);
- });
- }
- });
- }
- }
- }
- });
- if (annotationsJsonArray.size() > 0) field.add(ANNOTATIONS_KEY, annotationsJsonArray);
- }
- return field;
- }
- }
-
- static class SneakyAnnotationReader {
- Class extends EdmAnnotationImpl> object;
- Field field;
- EdmAnnotationImpl edmAnnotationImpl;
- ClientCsdlAnnotation clientCsdlAnnotation;
-
- public SneakyAnnotationReader(EdmAnnotation edmAnnotation) {
- try {
- edmAnnotationImpl = ((EdmAnnotationImpl) edmAnnotation);
-
- // create an object of the class named Class
- object = edmAnnotationImpl.getClass();
-
- // access the private variable
- field = object.getDeclaredField("annotation");
- // make private field accessible
- field.setAccessible(true);
-
- clientCsdlAnnotation = (ClientCsdlAnnotation) field.get(edmAnnotationImpl);
-
- } catch (Exception ex) {
- LOG.error(ex);
- ex.printStackTrace();
- }
- }
-
- public String getTerm() {
- return clientCsdlAnnotation.getTerm();
- }
- }
-
- /**
- * LookupJson uses a JSON payload with the following structure:
- *
- * {
- * "lookupName": "org.reso.metadata.enums.CommunityFeatures",
- * "lookupValue": "Stables",
- * "type": "Edm.Int32"
- * }
- */
- private static final class LookupJson implements JsonSerializer {
- static final String
- LOOKUP_NAME_KEY = "lookupName", LOOKUP_VALUE_KEY = "lookupValue",
- TYPE_KEY = "type", VALUE_KEY = "value", ANNOTATIONS_KEY = "annotations",
- LOOKUPS_KEY = "lookups";
-
- EdmEnumType edmEnumType;
- String memberName;
-
- public LookupJson(String memberName, EdmEnumType edmEnumType) {
- this.edmEnumType = edmEnumType;
- this.memberName = memberName;
- }
-
- public static String buildReportString(JsonElement metadataReport) {
- StringBuilder reportBuilder = new StringBuilder();
- metadataReport.getAsJsonObject().get(LOOKUPS_KEY).getAsJsonArray().forEach(field -> {
- reportBuilder.append("\nLookup Name: ");
- reportBuilder.append(field.getAsJsonObject().get(LOOKUP_NAME_KEY));
- reportBuilder.append("\nLookup Value: ");
- reportBuilder.append(field.getAsJsonObject().get(LOOKUP_VALUE_KEY));
- reportBuilder.append("\nType: ");
- reportBuilder.append(field.getAsJsonObject().get(TYPE_KEY));
-
- if (field.getAsJsonObject().get(ANNOTATIONS_KEY) != null) {
- JsonArray annotations = field.getAsJsonObject().get(ANNOTATIONS_KEY).getAsJsonArray();
- if (annotations != null && annotations.size() > 0) {
- reportBuilder.append("\n");
- reportBuilder.append("Annotations:");
- annotations.forEach(annotation -> {
- if (annotation.getAsJsonObject().get(FieldJson.TERM_KEY) != null) {
- reportBuilder.append("\n\tTerm: ");
- reportBuilder.append(annotation.getAsJsonObject().get(FieldJson.TERM_KEY));
- }
-
- if (annotation.getAsJsonObject().get(VALUE_KEY) != null) {
- reportBuilder.append("\n\tValue: ");
- reportBuilder.append(annotation.getAsJsonObject().get(VALUE_KEY));
- }
- });
- }
- }
- reportBuilder.append("\n");
- });
- return reportBuilder.toString();
- }
-
- @Override
- public JsonElement serialize(LookupJson src, Type typeOfSrc, JsonSerializationContext context) {
- JsonObject membersJsonObject = new JsonObject();
- membersJsonObject.addProperty(LOOKUP_NAME_KEY, src.edmEnumType.getFullQualifiedName().toString());
- membersJsonObject.addProperty(LOOKUP_VALUE_KEY, src.memberName);
- membersJsonObject.addProperty(TYPE_KEY, src.edmEnumType.getUnderlyingType().getFullQualifiedName().getFullQualifiedNameAsString());
-
- if (src.edmEnumType.getMember(memberName).getAnnotations().size() > 0) {
- JsonArray annotations = new JsonArray();
- src.edmEnumType.getMember(memberName).getAnnotations().forEach(edmAnnotation -> {
- JsonObject annotation = new JsonObject();
- if (edmAnnotation.getTerm() != null) {
- annotation.addProperty(FieldJson.TERM_KEY, edmAnnotation.getTerm().getFullQualifiedName().getFullQualifiedNameAsString());
- } else {
- SneakyAnnotationReader sneakyAnnotationReader = new SneakyAnnotationReader(edmAnnotation);
- annotation.addProperty(FieldJson.TERM_KEY, sneakyAnnotationReader.getTerm());
- }
-
- if (edmAnnotation.getExpression() != null) {
- annotation.addProperty(VALUE_KEY, edmAnnotation.getExpression().asConstant().getValueAsString());
- }
- annotations.add(annotation);
- });
- membersJsonObject.add(ANNOTATIONS_KEY, annotations);
- }
- return membersJsonObject;
- }
- }
-
- @Override
- public JsonElement serialize(MetadataReport src, Type typeOfSrc, JsonSerializationContext context) {
- final String
- DESCRIPTION_KEY = "description", DESCRIPTION = "RESO Data Dictionary Metadata Report",
- VERSION_KEY = "version", VERSION = "1.7",
- GENERATED_ON_KEY = "generatedOn",
- FIELDS_KEY = "fields",
- LOOKUPS_KEY = "lookups";
-
- JsonArray fields = new JsonArray();
- JsonArray lookups = new JsonArray();
-
- src.metadata.getSchemas().forEach(edmSchema -> {
- //serialize entities (resources) and members (fields)
- edmSchema.getEntityTypes().forEach(edmEntityType -> {
- edmEntityType.getPropertyNames().forEach(propertyName -> {
- FieldJson fieldJson = new FieldJson(edmEntityType.getName(), edmEntityType.getProperty(propertyName));
- fields.add(fieldJson.serialize(fieldJson, FieldJson.class, null));
- });
- });
-
- //serialize enum types
- edmSchema.getEnumTypes().forEach(edmEnumType -> {
- edmEnumType.getMemberNames().forEach(memberName -> {
- LookupJson lookupJson = new LookupJson(memberName, edmEnumType);
- lookups.add(lookupJson.serialize(lookupJson, LookupJson.class, null));
- });
- });
- });
-
- JsonObject metadataReport = new JsonObject();
- metadataReport.addProperty(DESCRIPTION_KEY, DESCRIPTION);
- metadataReport.addProperty(VERSION_KEY, VERSION);
- metadataReport.addProperty(GENERATED_ON_KEY, Utils.getIsoTimestamp());
- metadataReport.add(FIELDS_KEY, fields);
- metadataReport.add(LOOKUPS_KEY, lookups);
- return metadataReport;
- }
-}
diff --git a/src/main/java/org/reso/models/ReferenceStandardField.java b/src/main/java/org/reso/models/ReferenceStandardField.java
index 7992ccf1..f37dfb35 100644
--- a/src/main/java/org/reso/models/ReferenceStandardField.java
+++ b/src/main/java/org/reso/models/ReferenceStandardField.java
@@ -102,8 +102,14 @@ public String getLookup() {
return lookup;
}
- public String getLookupStandardName() {
- return getLookup().replace("Lookups", "").trim();
+ public String getLookupName() {
+ String lookupName = getLookup()
+ .replace("", "")
+ .replace("Lookups", "").trim();
+
+ if (lookupName.length() == 0) return null;
+
+ return lookupName;
}
public String getCollection() {
diff --git a/src/test/java/org/reso/commander/test/features/test-web-api-core-test-container.feature b/src/test/java/org/reso/commander/test/features/test-web-api-core-test-container.feature
index 17ba9610..f30ede7a 100644
--- a/src/test/java/org/reso/commander/test/features/test-web-api-core-test-container.feature
+++ b/src/test/java/org/reso/commander/test/features/test-web-api-core-test-container.feature
@@ -1,7 +1,7 @@
Feature: Web API Container Tests
Background:
- Given a Web API test container was created using the RESOScript "mock.web-api-server.core.1.0.2.resoscript"
+ Given a Web API test container was created using the RESOScript "mock.web-api-server.core.2.0.0.resoscript"
And a Commander instance exists within the test container
####################################
diff --git a/src/test/resources/mock.web-api-server.core.1.0.2.resoscript b/src/test/resources/mock.web-api-server.core.2.0.0.resoscript
similarity index 97%
rename from src/test/resources/mock.web-api-server.core.1.0.2.resoscript
rename to src/test/resources/mock.web-api-server.core.2.0.0.resoscript
index a7d469cd..3107e17d 100644
--- a/src/test/resources/mock.web-api-server.core.1.0.2.resoscript
+++ b/src/test/resources/mock.web-api-server.core.2.0.0.resoscript
@@ -1,7 +1,7 @@