public static bool IsFieldExposedToDatabase(FieldDefinition f) { return(Annotations.HasAnnotations(f, WellKnownAnnotations.ValueAnnotation, WellKnownAnnotations.ValueListAnnotation)); }
public string Generate(TypeDescription type) { var exposedFields = type.Fields.Where(Types.IsFieldExposedToDatabase).ToList(); if (!exposedFields.Any()) { return(string.Empty); } var profileIdFields = type.Fields.Where(f => Annotations.HasAnnotations(f, ProfileIdAnnotation)).ToArray(); if (!profileIdFields.Any()) { throw new InvalidOperationException($"{type.QualifiedName} is missing a string field annotated with {ProfileIdAnnotation}"); } if (profileIdFields.Length != 1) { throw new InvalidOperationException($"{type.QualifiedName} has multiple fields annotated with {ProfileIdAnnotation}. Only one is allowed."); } var profileIdField = profileIdFields[0]; if (!profileIdField.IsSingular() || !profileIdField.HasPrimitive(PrimitiveType.String)) { throw new InvalidOperationException($"{profileIdField.Name} is annotated with {ProfileIdAnnotation}, which requires it to be a string type."); } var profileFieldName = SnakeCaseToPascalCase(profileIdField.Name); var sb = new StringBuilder(); // Provide shortcuts foreach (var field in exposedFields) { sb.AppendLine($"public const string {DatabaseSyncFieldName(field)} = \"{type.QualifiedName}.{field.Name}\";"); sb.AppendLine($"public const string {PathFieldName(field)} = \"{type.ComponentId}_{field.FieldId}\";"); } // Use the static Linq methods below to avoid needing to import System.Linq. const string enumerable = "global::System.Linq.Enumerable"; const string isImmediateChild = "global::Improbable.DatabaseSync.DatabaseSync.IsImmediateChild"; const string insertStatement = "insert into {databaseName} (path, name, count)"; var valueFields = exposedFields.WithAnnotation(ValueAnnotation).ToList(); var listFields = exposedFields.WithAnnotation(ValueListAnnotation).ToList(); sb.AppendLine($@"[global::Improbable.DatabaseSync.Hydrate({type.ComponentId})] public static {SchemaComponentUpdate} Hydrate({GenericIEnumerable}<{DatabaseSyncItemClass}> items, string profilePath) {{ var update = new Update(); foreach(var item in items) {{ // Update count and list fields. switch(item.Name) {{ {string.Join("\n", valueFields.Select(field => Indent(3, $"case {DatabaseSyncFieldName(field)}: update.Set{FieldName(field)}(item.Count); break;")))} {string.Join("\n", listFields.Select(field => Indent(3, $@"case {DatabaseSyncFieldName(field)}: update.Set{FieldName(field)}({enumerable}.ToArray({enumerable}.Where(items, i => {isImmediateChild}(i.Path, item.Path)))); break;")))} }} }} return update.ToSchemaUpdate(); }} [global::Improbable.DatabaseSync.ProfileIdFromSchemaData] public static string GetProfileIdFromComponentData(global::Improbable.Worker.CInterop.SchemaObject fields) {{ return fields.GetString({profileIdField.FieldId}); }} public static string ComponentToDatabase(string databaseName, in {type.Fqn()} item) {{ if (string.IsNullOrEmpty(item.{profileFieldName})) {{ throw new global::System.ArgumentNullException(nameof(item.{profileFieldName})); }} return $@""{string.Join("\n", valueFields.Select(field => $"{insertStatement} values('{{item.{profileFieldName}}}.{{{PathFieldName(field)}}}', '{{{DatabaseSyncFieldName(field)}}}', {{item.{FieldName(field)}}});"))} {string.Join("\n", listFields.Select(field => $"{insertStatement} values('{{item.{profileFieldName}}}.{{{PathFieldName(field)}}}', '{{{DatabaseSyncFieldName(field)}}}', 0);"))}""; }} [global::Improbable.DatabaseSync.ProfileId] public string DatabaseSyncProfileId => {profileFieldName}; { string.Join("\n", exposedFields.Select(field => $@"public string {FieldName(field)}Path() {{ return {profileFieldName} + ""."" + {PathFieldName(field)}; }} ")) }"); return(sb.ToString().TrimEnd()); }