internal static ItemStorageConfig CreateStorageConfig(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
var typeWrapper = TypeFactory.GetTypeInfo(type);
ItemStorageConfig config = new ItemStorageConfig(type);
DynamoDBTableAttribute tableAttribute = Utils.GetTableAttribute(type);
if (tableAttribute == null || string.IsNullOrEmpty(tableAttribute.TableName))
{
throw new InvalidOperationException("No DynamoDBTableAttribute on type");
}
if (string.IsNullOrEmpty(tableAttribute.TableName))
{
throw new InvalidOperationException("DynamoDBTableAttribute.Table is empty or null");
}
config.TableName = tableAttribute.TableName;
config.LowerCamelCaseProperties = tableAttribute.LowerCamelCaseProperties;
foreach (var member in typeWrapper.GetMembers())
{
// filter out non-fields and non-properties
if (!(member is FieldInfo || member is PropertyInfo))
{
continue;
}
// filter out properties that aren't both read and write
if (!Utils.IsReadWrite(member))
{
continue;
}
// prepare basic info
Type memberType = Utils.GetType(member);
string attributeName = GetAccurateCase(config, member.Name);
string propertyName = member.Name;
PropertyStorage propertyStorage = new PropertyStorage(member, memberType);
// run through all DDB attributes
bool ignorePresent = false;
var allAttributes = Utils.GetAttributes(member);
foreach (var attribute in allAttributes)
{
// filter out ignored properties
if (attribute is DynamoDBIgnoreAttribute)
{
ignorePresent |= true;
}
// if ignore attribute is present, ignore other attributes
if (ignorePresent)
{
continue;
}
if (attribute is DynamoDBVersionAttribute)
{
Utils.ValidateVersionType(memberType); // no conversion is possible, so type must be a nullable primitive
propertyStorage.IsVersion = true;
}
DynamoDBPropertyAttribute propertyAttribute = attribute as DynamoDBPropertyAttribute;
if (propertyAttribute != null)
{
if (!string.IsNullOrEmpty(propertyAttribute.AttributeName))
{
attributeName = GetAccurateCase(config, propertyAttribute.AttributeName);
}
if (propertyAttribute is DynamoDBHashKeyAttribute)
{
if (propertyAttribute.Converter == null && !Utils.IsPrimitive(memberType))
{
throw new InvalidOperationException("Hash key " + propertyName + " must be of primitive type");
}
var gsiHashAttribute = propertyAttribute as DynamoDBGlobalSecondaryIndexHashKeyAttribute;
if (gsiHashAttribute != null)
{
propertyStorage.IsGSIHashKey = true;
if (gsiHashAttribute.IndexNames == null || gsiHashAttribute.IndexNames.Length == 0)
{
throw new InvalidOperationException("Global Secondary Index must not be null or empty");
}
propertyStorage.Indexes.AddRange(gsiHashAttribute.IndexNames);
AddGSIConfigs(config, gsiHashAttribute.IndexNames, propertyName, true);
}
else
{
propertyStorage.IsHashKey = true;
}
}
if (propertyAttribute is DynamoDBRangeKeyAttribute)
{
if (propertyAttribute.Converter == null && !Utils.IsPrimitive(memberType))
{
throw new InvalidOperationException("Range key " + propertyName + " must be of primitive type");
}
var gsiRangeAttribute = propertyAttribute as DynamoDBGlobalSecondaryIndexRangeKeyAttribute;
if (gsiRangeAttribute != null)
{
propertyStorage.IsGSIRangeKey = true;
if (gsiRangeAttribute.IndexNames == null || gsiRangeAttribute.IndexNames.Length == 0)
{
throw new InvalidOperationException("Global Secondary Index must not be null or empty");
}
propertyStorage.Indexes.AddRange(gsiRangeAttribute.IndexNames);
AddGSIConfigs(config, gsiRangeAttribute.IndexNames, propertyName, false);
}
else
{
propertyStorage.IsRangeKey = true;
}
}
DynamoDBLocalSecondaryIndexRangeKeyAttribute lsiRangeKeyAttribute = propertyAttribute as DynamoDBLocalSecondaryIndexRangeKeyAttribute;
if (lsiRangeKeyAttribute != null)
{
if (lsiRangeKeyAttribute.IndexNames == null || lsiRangeKeyAttribute.IndexNames.Length == 0)
{
throw new InvalidOperationException("Local Secondary Index must not be null or empty");
}
propertyStorage.Indexes.AddRange(lsiRangeKeyAttribute.IndexNames);
propertyStorage.IsLSIRangeKey = true;
foreach (var index in lsiRangeKeyAttribute.IndexNames)
{
List <string> properties;
if (!config.IndexNameToLSIRangePropertiesMapping.TryGetValue(index, out properties))
{
properties = new List <string>();
config.IndexNameToLSIRangePropertiesMapping[index] = properties;
}
properties.Add(propertyName);
}
}
if (propertyAttribute.Converter != null)
{
if (!Utils.CanInstantiate(propertyAttribute.Converter) || !Utils.ImplementsInterface(propertyAttribute.Converter, typeof(IPropertyConverter)))
{
throw new InvalidOperationException("Converter for " + propertyName + " must be instantiable with no parameters and must implement IPropertyConverter");
}
propertyStorage.Converter = Utils.Instantiate(propertyAttribute.Converter) as IPropertyConverter;
}
}
}
propertyStorage.PropertyName = propertyName;
propertyStorage.AttributeName = attributeName;
// only add property storage if no ignore attribute was present
if (!ignorePresent)
{
config.AddPropertyStorage(propertyStorage);
}
}
if (config.HashKeyPropertyNames.Count == 0)
{
throw new InvalidOperationException("No hash key configured for type " + type.FullName);
}
return(config);
}