Changes to actually load resources from definitions loaded from Certification metadata report

This commit is contained in:
michaelpede 2021-08-19 13:02:42 -07:00
parent 132411cf25
commit f1cbd7ddba
4 changed files with 412 additions and 125 deletions

View File

@ -47,24 +47,29 @@ public class FieldDefinition extends ResourceInfo
return FieldDefinition.fieldList;
}
ArrayList<FieldInfo> list = new ArrayList<FieldInfo>();
ArrayList<FieldInfo> list = new ArrayList<>();
FieldDefinition.fieldList = list;
FieldInfo fieldInfo = null;
fieldInfo = new FieldInfo("FieldKey", EdmPrimitiveTypeKind.String.getFullQualifiedName());
fieldInfo.addAnnotation("Field Key Field", "RESO.OData.Metadata.DisplayName");
fieldInfo.addAnnotation("The key used to uniquely identify the Field.", "Core.Description");
list.add(fieldInfo);
fieldInfo = new FieldInfo("ResourceName", EdmPrimitiveTypeKind.String.getFullQualifiedName());
fieldInfo.addAnnotation("The name of the resource the field belongs to. This will be a RESO Standard Name, when applicable, but may also be a local resource name.", "Core.Description");
list.add(fieldInfo);
fieldInfo = new FieldInfo("FieldName", EdmPrimitiveTypeKind.String.getFullQualifiedName());
fieldInfo.addAnnotation("The name of the field as expressed in the payload. For OData APIs, this field MUST meet certain naming requirements and should be consistent with whats advertised in the OData XML metadata (to be verified in certification). ", "Core.Description");
list.add(fieldInfo);
fieldInfo = new FieldInfo("DisplayName", EdmPrimitiveTypeKind.String.getFullQualifiedName());
fieldInfo.addAnnotation("The display name for the field. SHOULD be provided in all cases where the use of display names is needed, even if the display name is the same as the underlying field name. The DisplayName MAY be a RESO Standard Display Name or a local one. ", "Core.Description");
list.add(fieldInfo);
fieldInfo = new FieldInfo("ModificationTimestamp", EdmPrimitiveTypeKind.DateTimeOffset.getFullQualifiedName());
fieldInfo.addAnnotation("The timestamp when the field metadata item was last modified. This is used to help rebuild caches when metadata items change so consumers don't have to re-pull and reprocess the entire set of metadata when only a small number of changes have been made.", "Core.Description");
list.add(fieldInfo);
return FieldDefinition.fieldList;
@ -74,64 +79,7 @@ public class FieldDefinition extends ResourceInfo
public Entity getData(EdmEntitySet edmEntitySet, List<UriParameter> keyPredicates)
{
ArrayList<FieldInfo> fields = this.getFieldList();
Entity product = null;
Map<String, String> properties = System.getenv();
try {
String sqlCriteria = null;
// Statements allow to issue SQL queries to the database
Statement statement = null;
// Result set get the result of the SQL query
String queryString = null;
for (final UriParameter key : keyPredicates)
{
// key
String keyName = key.getName(); // .toLowerCase();
String keyValue = key.getText();
if (sqlCriteria==null)
{
sqlCriteria = keyName + " = " + keyValue;
}
else
{
sqlCriteria = sqlCriteria + " and " + keyName + " = " + keyValue;
}
}
queryString = "select * from " + this.getTableName();
if (null!=sqlCriteria && sqlCriteria.length()>0)
{
queryString = queryString + " WHERE " + sqlCriteria;
}
LOG.info("SQL Query: "+queryString);
ResultSet resultSet = statement.executeQuery(queryString);
String primaryFieldName = this.getPrimaryKeyName();
// add the lookups from the database.
while (resultSet.next())
{
Entity ent = CommonDataProcessing.getEntityFromRow(resultSet, this, null);
product = ent;
}
statement.close();
} catch (Exception e) {
LOG.error("Server Error occurred in reading "+this.getResourceName(), e);
return product;
}
return product;
return null;
}
public EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo, boolean isCount) throws ODataApplicationException
@ -173,7 +121,7 @@ public class FieldDefinition extends ResourceInfo
for (FieldInfo field: resourceFieldList)
{
HashMap<String,Object> entityValues = new HashMap<>();
entityValues.put("FieldKey", field.getFieldName());
entityValues.put("FieldKey", resourceName.toLowerCase()+'_'+field.getFieldName().toLowerCase() );
entityValues.put("FieldName", field.getFieldName());
entityValues.put("ResourceName", resourceName);
entityValues.put("DisplayName", field.getFieldName());

View File

@ -1,6 +1,8 @@
package org.reso.service.data.meta;
import com.google.gson.stream.JsonReader;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import java.io.FileNotFoundException;
import java.io.FileReader;
@ -9,38 +11,28 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class AnnotationObject
class GenericGSONobject<SubType>
{
private static Map<String, Object> FIELD_PROPERTIES = Stream.of(
new AbstractMap.SimpleEntry<>("term", String.class),
new AbstractMap.SimpleEntry<>("value", String.class) )
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
protected static String subArrayName = "annotations";
class FieldObject
{
private static Map<String, Object> FIELD_PROPERTIES = Stream.of(
new AbstractMap.SimpleEntry<>("resourceName", String.class),
new AbstractMap.SimpleEntry<>("fieldName", String.class),
new AbstractMap.SimpleEntry<>("type", String.class),
new AbstractMap.SimpleEntry<>("nullable", Boolean.class),
new AbstractMap.SimpleEntry<>("scale", Number.class),
new AbstractMap.SimpleEntry<>("precision", Number.class),
new AbstractMap.SimpleEntry<>("isCollection", Boolean.class),
new AbstractMap.SimpleEntry<>("unicode", Boolean.class) )
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
protected JsonReader reader;
protected HashMap<String,Object> properties = new HashMap<>();
protected ArrayList<SubType> subArrayList = new ArrayList<>();
private JsonReader reader;
private HashMap<String,Object> properties = new HashMap<>();
public FieldObject(JsonReader reader)
public GenericGSONobject(JsonReader reader)
{
this.reader = reader;
this.readObject();
}
public Map<String, Object> getPropertiesMeta()
{
return null;
}
private void readObject()
{
Map<String, Object> PROPERTIES_META = this.getPropertiesMeta();
try
{
reader.beginObject();
@ -49,9 +41,9 @@ class FieldObject
String name = reader.nextName();
if (FIELD_PROPERTIES.containsKey(name))
if (PROPERTIES_META.containsKey(name))
{
Object classType = FIELD_PROPERTIES.get(name);
Object classType = PROPERTIES_META.get(name);
if (classType.equals(String.class))
{
@ -61,17 +53,19 @@ class FieldObject
{
properties.put(name,reader.nextBoolean() );
}
else if (classType.equals(Number.class))
else if (classType.equals(Integer.class))
{
properties.put(name,reader.nextLong() );
properties.put(name,reader.nextInt() );
}
} else if (name.equals("annotations")) {
} else if (name.equals(subArrayName)) {
// read array
reader.beginArray();
while (reader.hasNext()) {
reader.skipValue();
//fields.add( this.readField() );
GenericGSONobject subArrayItem = this.createSubType();
subArrayList.add((SubType)subArrayItem);
}
reader.endArray();
@ -88,20 +82,145 @@ class FieldObject
}
}
public Object getProperty(String name)
{
return properties.get(name);
}
protected GenericGSONobject createSubType() // must also be of type GenericGSONobject
{
return null;
}
/**
* Get the annotations for this field.
* @return the sub-array for this generic object
*/
public ArrayList<SubType> getSubArrayList()
{
return subArrayList;
}
}
class FieldObject extends GenericGSONobject<AnnotationObject>
{
private static Map<String, Object> PROPERTIES_META = Stream.of(
new AbstractMap.SimpleEntry<>("resourceName", String.class),
new AbstractMap.SimpleEntry<>("fieldName", String.class),
new AbstractMap.SimpleEntry<>("type", String.class),
new AbstractMap.SimpleEntry<>("nullable", Boolean.class),
new AbstractMap.SimpleEntry<>("maxLength", Integer.class),
new AbstractMap.SimpleEntry<>("scale", Integer.class),
new AbstractMap.SimpleEntry<>("precision", Integer.class),
new AbstractMap.SimpleEntry<>("isCollection", Boolean.class),
new AbstractMap.SimpleEntry<>("unicode", Boolean.class) )
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
protected static String subArrayName = "annotations";
public FieldObject(JsonReader reader)
{
super(reader);
}
public Map<String, Object> getPropertiesMeta()
{
return PROPERTIES_META;
}
protected GenericGSONobject createSubType() // must also be of type GenericGSONobject
{
return new AnnotationObject(reader);
}
/**
* Get the annotations for this field.
* @return
*/
public ArrayList<AnnotationObject> getAnnotations()
{
return getSubArrayList();
}
}
class LookupObject extends GenericGSONobject<AnnotationObject>
{
private static Map<String, Object> PROPERTIES_META = Stream.of(
new AbstractMap.SimpleEntry<>("lookupName", String.class),
new AbstractMap.SimpleEntry<>("lookupValue", String.class),
new AbstractMap.SimpleEntry<>("type", String.class) )
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
protected static String subArrayName = "annotations";
public LookupObject(JsonReader reader)
{
super(reader);
}
public Map<String, Object> getPropertiesMeta()
{
return PROPERTIES_META;
}
protected GenericGSONobject createSubType() // must also be of type GenericGSONobject
{
return new AnnotationObject(reader);
}
/**
* Get the annotations for this field.
* @return
*/
public ArrayList<AnnotationObject> getAnnotations()
{
return getSubArrayList();
}
}
class AnnotationObject extends GenericGSONobject<Object>
{
private static Map<String, Object> PROPERTIES_META = Stream.of(
new AbstractMap.SimpleEntry<>("term", String.class),
new AbstractMap.SimpleEntry<>("value", String.class) )
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
public AnnotationObject(JsonReader reader)
{
super(reader);
}
public Map<String, Object> getPropertiesMeta()
{
return PROPERTIES_META;
}
}
public class DefinitionBuilder
{
// Constants
private static String EDM_ENUM = "org.reso.metadata.enums";
private static Map<String, FullQualifiedName> EDM_MAP = Stream.of(
new AbstractMap.SimpleEntry<>("Edm.String", EdmPrimitiveTypeKind.String.getFullQualifiedName() ),
new AbstractMap.SimpleEntry<>("Edm.Boolean", EdmPrimitiveTypeKind.Boolean.getFullQualifiedName() ),
new AbstractMap.SimpleEntry<>("Edm.Decimal", EdmPrimitiveTypeKind.Int64.getFullQualifiedName() ))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
private static Map<String, Boolean> HEADER_FIELDS = Stream.of(
new AbstractMap.SimpleEntry<>("description", true),
new AbstractMap.SimpleEntry<>("generatedOn", true),
new AbstractMap.SimpleEntry<>("version", true))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// Internals
private String fileName;
private JsonReader reader;
// Constructor
public DefinitionBuilder(String fileName)
{
this.fileName = fileName;
@ -122,14 +241,186 @@ public class DefinitionBuilder
private FieldObject readField()
{
FieldObject fo = new FieldObject(reader);
return fo;
return new FieldObject(reader);
}
private LookupObject readLookup()
{
return new LookupObject(reader);
}
private HashMap<String,ArrayList<GenericGSONobject>> createHashFromKey(ArrayList<GenericGSONobject> allObjects, String keyName)
{
HashMap<String,ArrayList<GenericGSONobject>> lookup = new HashMap<>();
for (GenericGSONobject obj: allObjects)
{
String keyValue = obj.getProperty(keyName).toString();
ArrayList<GenericGSONobject> commonList = lookup.get(keyValue);
if (commonList==null)
{
commonList = new ArrayList<>();
lookup.put(keyValue, commonList);
}
commonList.add(obj);
}
return lookup;
}
// Function to convert camel case
// string to snake case string
public static String camelToSnake(String str)
{
// Empty String
String result = "";
// Append first character(in lower case)
// to result string
char c = str.charAt(0);
result = result + Character.toLowerCase(c);
// Traverse the string from
// ist index to last index
for (int i = 1; i < str.length(); i++) {
char ch = str.charAt(i);
// Check if the character is upper case
// then append '_' and such character
// (in lower case) to result string
if (Character.isUpperCase(ch)) {
result = result + '_';
result
= result
+ Character.toLowerCase(ch);
}
// If the character is lower case then
// add such character into result string
else {
result = result + ch;
}
}
// return the result
return result;
}
private List<ResourceInfo> createResources(ArrayList<GenericGSONobject> fields, ArrayList<GenericGSONobject> lookups)
{
HashMap<String, ArrayList<GenericGSONobject>> lookupMap = createHashFromKey(lookups, "lookupName");
HashMap<String, ArrayList<GenericGSONobject>> fieldMap = createHashFromKey(fields, "resourceName");
List<ResourceInfo> resources = new ArrayList<>();
for (String resourceName: fieldMap.keySet() )
{
ArrayList<GenericGSONobject> resourceFields = fieldMap.get(resourceName);
String tableName = resourceName.toLowerCase();
if (!"ouid".equals(tableName))
{
tableName = camelToSnake(resourceName); // @ToDo: This is NOT Guaranteed for all users
}
ResourceInfo resource = new GenericResourceInfo(resourceName, tableName);
resources.add(resource);
ArrayList<FieldInfo> fieldList = resource.getFieldList();
for (GenericGSONobject field : resourceFields)
{
FieldInfo newField = null;
String fieldName = (String) field.getProperty("fieldName");
String fieldType = (String) field.getProperty("type");
Boolean nullable = (Boolean) field.getProperty("nullable");
Boolean isCollection = (Boolean) field.getProperty("isCollection");
Integer maxLength = (Integer) field.getProperty("maxLength");
Integer scale = (Integer) field.getProperty("scale");
Integer precision = (Integer) field.getProperty("precision");
FullQualifiedName fqn = EDM_MAP.get(fieldType);
if (fqn != null)
{
newField = new FieldInfo(fieldName, fqn);
}
else
if (fieldType.startsWith(EDM_ENUM))
{
String lookupName = fieldType.substring(EDM_ENUM.length()+1 );
EnumFieldInfo enumFieldInfo = new EnumFieldInfo(fieldName, EdmPrimitiveTypeKind.Int64.getFullQualifiedName());
enumFieldInfo.setLookupName(lookupName);
newField = enumFieldInfo;
ArrayList<GenericGSONobject> lookupList = lookupMap.get(fieldType);
for (GenericGSONobject lookupItem: lookupList)
{
EnumValueInfo enumValue = new EnumValueInfo((String)lookupItem.getProperty("lookupValue"));
ArrayList<AnnotationObject> annotations = null;
if (lookupItem.getClass().equals(LookupObject.class))
{
annotations = ((LookupObject)lookupItem).getAnnotations();
}
if (annotations!=null)
{
for (AnnotationObject annotation : annotations)
{
enumValue.addAnnotation((String) annotation.getProperty("value"), (String) annotation.getProperty("term"));
}
}
enumFieldInfo.addValue(enumValue);
}
}
if (newField != null)
{
if (maxLength != null)
{
newField.setMaxLength(maxLength);
}
if (scale != null)
{
newField.setScale(scale);
}
if (precision != null)
{
newField.setPrecision(precision);
}
ArrayList<AnnotationObject> annotations = null;
if (field.getClass().equals(FieldObject.class))
{
annotations = ((FieldObject)field).getAnnotations();
}
if (annotations!=null)
{
for (AnnotationObject annotation : annotations)
{
newField.addAnnotation((String) annotation.getProperty("value"), (String) annotation.getProperty("term"));
}
}
fieldList.add(newField);
}
}
}
return resources;
}
public List<ResourceInfo> readResources()
{
List<ResourceInfo> resources = new ArrayList<>();
ArrayList<FieldObject> fields = new ArrayList();
ArrayList<GenericGSONobject> fields = new ArrayList();
ArrayList<GenericGSONobject> lookups = new ArrayList();
try
{
@ -153,9 +444,19 @@ public class DefinitionBuilder
reader.endArray();
} else if (name.equals("lookups")) {
// read array
reader.beginArray();
while (reader.hasNext()) {
lookups.add( this.readLookup() );
}
reader.endArray();
} else {
reader.skipValue(); //avoid some unhandle events
}
}
reader.endObject();
@ -165,6 +466,6 @@ public class DefinitionBuilder
e.printStackTrace();
}
return resources;
return createResources(fields, lookups);
}
}

View File

@ -0,0 +1,23 @@
package org.reso.service.data.meta;
import java.util.ArrayList;
public class GenericResourceInfo extends ResourceInfo
{
ArrayList<FieldInfo> fieldList = new ArrayList<>();
public GenericResourceInfo(String resourceName, String tableName)
{
this.resourceName = resourceName;
this.resourcesName = resourceName;
this.tableName = tableName;
}
// Accessor
public ArrayList<FieldInfo> getFieldList()
{
return fieldList;
}
}

View File

@ -117,12 +117,25 @@ public class RESOservlet extends HttpServlet
// If there is a Certification metadata report file, import it for class definitions.
if (definitionFile!=null && false)
if (definitionFile!=null)
{
DefinitionBuilder definitionBuilder = new DefinitionBuilder(definitionFile);
List<ResourceInfo> loadedResources = definitionBuilder.readResources();
for (ResourceInfo resource: loadedResources)
{
try
{
resource.findPrimaryKey(this.connect);
resources.add(resource);
}
catch (SQLException e)
{
LOG.error("Error with: "+resource.getResourceName()+" - "+e.getMessage());
}
}
}
else
{
// Get all classes with constructors with 0 parameters. LookupDefinition should not work.
try
{
@ -158,6 +171,8 @@ public class RESOservlet extends HttpServlet
{
LOG.error(e.getMessage());
}
}
ResourceInfo defn = new LookupDefinition();
try