private ClrQuery ParseJson(Context context, ISchemaEntity?schema, Query <IJsonValue> query, ResolvedComponents components) { var queryModel = BuildQueryModel(context, schema, components); return(queryModel.Convert(query)); }
private static IJsonValue?ConvertComponents(IJsonValue?value, ValueConverter[] converters, ResolvedComponents components) { if (value is JsonArray array) { JsonArray?result = null; for (int i = 0, j = 0; i < array.Count; i++, j++) { var newValue = ConvertComponent(array[i], converters, components); if (newValue == null) { result ??= new JsonArray(array); result.RemoveAt(j); j--; } else if (!ReferenceEquals(newValue, array[i])) { result ??= new JsonArray(array); result[j] = newValue; } } return(result ?? array); } return(null); }
public static JsonSchema BuildJsonSchemaFlat(this Schema schema, PartitionResolver partitionResolver, ResolvedComponents components, JsonTypeFactory?factory = null, bool withHidden = false, bool withComponents = false) { Guard.NotNull(partitionResolver); Guard.NotNull(components); factory ??= DefaultFactory; var jsonSchema = JsonTypeBuilder.Object(); foreach (var field in schema.Fields.ForApi(withHidden)) { var property = JsonTypeVisitor.BuildProperty( field, components, schema, factory, withHidden, withComponents); // Property is null for UI fields. if (property != null) { property.SetRequired(false); property.SetDescription(field); jsonSchema.Properties.Add(field.Name, property); } } return(jsonSchema); }
public static FieldConverter ForValues(ResolvedComponents components, params ValueConverter[] converters) { return((data, field) => { ContentFieldData?newData = null; foreach (var(key, value) in data) { var newValue = ConvertByType(field, value, null, converters, components); if (newValue == null) { newData ??= new ContentFieldData(data); newData.Remove(key); } else if (!ReferenceEquals(newValue, value)) { newData ??= new ContentFieldData(data); newData[key] = newValue; } } return newData ?? data; }); }
private ClrQuery ParseJson(Context context, ISchemaEntity?schema, string json, ResolvedComponents components) { var queryModel = BuildQueryModel(context, schema, components); return(queryModel.Parse(json, jsonSerializer)); }
private static JsonSchema BuildJsonSchema(Schema schema, IAppEntity app, ResolvedComponents components, bool withHiddenFields) { var dataSchema = schema.BuildJsonSchema(app.PartitionResolver(), components, null, withHiddenFields); return(ContentJsonSchemaBuilder.BuildSchema(dataSchema, false, true)); }
private ClrQuery ParseJson(Context context, ISchemaEntity?schema, Query <IJsonValue> query, ResolvedComponents components) { var jsonSchema = BuildJsonSchema(context, schema, components); return(jsonSchema.Convert(query)); }
private ClrQuery ParseOData(Context context, ISchemaEntity?schema, string odata, ResolvedComponents components) { try { var model = BuildEdmModel(context, schema, components); return(model.ParseQuery(odata).ToQuery()); } catch (ValidationException) { throw; } catch (NotSupportedException) { throw new ValidationException(T.Get("common.odataNotSupported", new { odata })); } catch (ODataException ex) { var message = ex.Message; throw new ValidationException(T.Get("common.odataFailure", new { odata, message }), ex); } catch (Exception) { throw new ValidationException(T.Get("common.odataNotSupported", new { odata })); } }
private static IJsonValue ConvertNested <T>(FieldCollection <T> fields, JsonObject source, IArrayField?parent, ValueConverter[] converters, ResolvedComponents components) where T : IField { JsonObject?result = null; foreach (var(key, value) in source) { var newValue = value; if (fields.ByName.TryGetValue(key, out var field)) { newValue = ConvertByType(field, value, parent, converters, components); } else if (key != Component.Discriminator) { newValue = null; } if (newValue == null) { result ??= new JsonObject(source); result.Remove(key); } else if (!ReferenceEquals(newValue, value)) { result ??= new JsonObject(source); result[key] = newValue; } } return(result ?? source); }
public static JsonSchema BuildJsonSchema(this Schema schema, PartitionResolver partitionResolver, ResolvedComponents components, JsonTypeFactory?factory = null, bool withHidden = false, bool withComponents = false) { Guard.NotNull(partitionResolver); Guard.NotNull(components); factory ??= DefaultFactory; var jsonSchema = JsonTypeBuilder.Object(); foreach (var field in schema.Fields.ForApi(withHidden)) { var typeName = $"{schema.TypeName()}{field.Name.ToPascalCase()}PropertyDto"; // Create a reference to give it a nice name in code generation. var(reference, actual) = factory(typeName); if (actual != null) { var partitioning = partitionResolver(field.Partitioning); foreach (var partitionKey in partitioning.AllKeys) { var property = JsonTypeVisitor.BuildProperty( field, components, schema, factory, withHidden, withComponents); // Property is null for UI fields. if (property != null) { var isOptional = partitioning.IsOptional(partitionKey); var name = partitioning.GetName(partitionKey); // Required if property is required and language/partitioning is not optional. property.SetRequired(field.RawProperties.IsRequired && !isOptional); property.SetDescription(name); actual.Properties.Add(partitionKey, property); } } } var propertyReference = JsonTypeBuilder.ReferenceProperty(reference) .SetDescription(field) .SetRequired(field.RawProperties.IsRequired); jsonSchema.Properties.Add(field.Name, propertyReference); } return(jsonSchema); }
public static JsonSchemaProperty?BuildProperty(IField field, ResolvedComponents components, Schema schema, JsonTypeFactory factory, bool withHidden, bool withComponents) { var args = new Args(components, schema, factory, withHidden, withComponents); return(field.Accept(Instance, args)); }
private static IJsonValue?ConvertArrayItem(IArrayField field, IJsonValue?value, ValueConverter[] converters, ResolvedComponents components) { if (value is JsonObject obj) { return(ConvertNested(field.FieldCollection, obj, field, converters, components)); } return(null); }
public static FilterSchema BuildDataSchema(this Schema schema, PartitionResolver partitionResolver, ResolvedComponents components) { Guard.NotNull(partitionResolver); Guard.NotNull(components); var fields = new List <FilterField>(); var schemaName = schema.DisplayName(); foreach (var field in schema.Fields.ForApi(true)) { var fieldSchema = FilterVisitor.BuildProperty(field, components); if (fieldSchema == null) { continue; } var partitioning = partitionResolver(field.Partitioning); var partitionFields = new List <FilterField>(); foreach (var partitionKey in partitioning.AllKeys) { var partitionDescription = FieldPartitionDescription(field, partitioning.GetName(partitionKey) ?? partitionKey); var partitionField = new FilterField( fieldSchema, partitionKey, partitionDescription, true); partitionFields.Add(partitionField); } var filterable = new FilterField( new FilterSchema(FilterSchemaType.Object) { Fields = partitionFields.ToReadonlyList() }, field.Name, FieldDescription(schemaName, field)); fields.Add(filterable); } var dataSchema = new FilterSchema(FilterSchemaType.Object) { Fields = fields.ToReadonlyList() }; return(dataSchema); }
public ValidationContext( IJsonSerializer jsonSerializer, NamedId <DomainId> appId, NamedId <DomainId> schemaId, Schema schema, ResolvedComponents components, DomainId contentId) : base(appId, schemaId, schema) { JsonSerializer = jsonSerializer; Components = components; ContentId = contentId; }
private QueryModel BuildQueryModel(Context context, ISchemaEntity?schema, ResolvedComponents components) { var cacheKey = BuildJsonCacheKey(context.App, schema, context.IsFrontendClient); var result = cache.GetOrCreate(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; return(ContentQueryModel.Build(schema?.SchemaDef, context.App.PartitionResolver(), components)); }); return(result); }
private IEdmModel BuildEdmModel(Context context, ISchemaEntity?schema, ResolvedComponents components) { var cacheKey = BuildEmdCacheKey(context.App, schema, context.IsFrontendClient); var result = cache.GetOrCreate <IEdmModel>(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; return(BuildQueryModel(context, schema, components).ConvertToEdm("Contents", schema?.SchemaDef.Name ?? "Generic")); }); return(result); }
private IEnumerable <FieldConverter> GenerateConverters(Context context, ResolvedComponents components, ValueConverter?cleanReferences) { if (!context.IsFrontendClient) { yield return(excludedHiddenField); yield return(FieldConverters.ForValues(components, ValueConverters.ExcludeHidden)); } yield return(excludedChangedField); yield return(FieldConverters.ForValues(components, ValueConverters.ExcludeChangedTypes(jsonSerializer))); if (cleanReferences != null) { yield return(FieldConverters.ForValues(components, cleanReferences)); } yield return(FieldConverters.ResolveInvariant(context.App.Languages)); yield return(FieldConverters.ResolveLanguages(context.App.Languages)); if (!context.IsFrontendClient) { if (context.ShouldResolveLanguages()) { yield return(FieldConverters.ResolveFallbackLanguages(context.App.Languages)); } var languages = context.Languages(); if (languages.Any()) { yield return(FieldConverters.FilterLanguages(context.App.Languages, languages)); } var assetUrls = context.AssetUrls().ToList(); if (assetUrls.Count > 0) { var appId = context.App.NamedId(); var resolveAssetUrls = ValueConverters.ResolveAssetUrls(appId, assetUrls, urlGenerator); yield return(FieldConverters.ForValues(components, resolveAssetUrls)); } } }
private void ResolveAssetsUrls(ISchemaEntity schema, ResolvedComponents components, IGrouping <DomainId, ContentEntity> contents, ILookup <DomainId, IEnrichedAssetEntity> assets) { foreach (var field in schema.SchemaDef.ResolvingAssets()) { foreach (var content in contents) { content.ReferenceData ??= new ContentData(); var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData()) !; if (content.Data.TryGetValue(field.Name, out var fieldData) && fieldData != null) { foreach (var(partitionKey, partitionValue) in fieldData) { var referencedAsset = field.GetReferencedIds(partitionValue, components) .Select(x => assets[x]) .SelectMany(x => x) .FirstOrDefault(); if (referencedAsset != null) { IJsonValue array; if (IsImage(referencedAsset)) { var url = urlGenerator.AssetContent( referencedAsset.AppId, referencedAsset.Id.ToString()); array = JsonValue.Array(url, referencedAsset.FileName); } else { array = JsonValue.Array(referencedAsset.FileName); } requestCache.AddDependency(referencedAsset.UniqueId, referencedAsset.Version); fieldReference.AddLocalized(partitionKey, array); } } } } } }
public ContentConversionTests() { schema = new Schema("my-schema") .AddComponent(1, "component", Partitioning.Invariant) .AddComponents(2, "components", Partitioning.Invariant) .AddAssets(3, "assets1", Partitioning.Invariant) .AddAssets(4, "assets2", Partitioning.Invariant) .AddReferences(5, "references", Partitioning.Invariant) .AddArray(6, "array", Partitioning.Invariant, a => a .AddAssets(31, "nested")); components = new ResolvedComponents(new Dictionary <DomainId, Schema> { [DomainId.Empty] = schema }); }
private static IJsonValue?ConvertByType <T>(T field, IJsonValue?value, IArrayField?parent, ValueConverter[] converters, ResolvedComponents components) where T : IField { switch (field) { case IArrayField arrayField: return(ConvertArray(arrayField, value, converters, components)); case IField <ComponentFieldProperties> : return(ConvertComponent(value, converters, components)); case IField <ComponentsFieldProperties> : return(ConvertComponents(value, converters, components)); default: return(ConvertValue(field, value, parent, converters)); } }
private IEdmModel BuildEdmModel(Context context, ISchemaEntity?schema, ResolvedComponents components) { if (schema == null) { return(genericEdmModel); } var cacheKey = BuildEmdCacheKey(context.App, schema, context.IsFrontendClient); var result = cache.GetOrCreate <IEdmModel>(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; return(BuildEdmModel(schema.SchemaDef, context.App, components, context.IsFrontendClient)); }); return(result); }
private static IJsonValue?ConvertComponent(IJsonValue?value, ValueConverter[] converters, ResolvedComponents components) { if (value is JsonObject obj && obj.TryGetValue <JsonString>(Component.Discriminator, out var type)) { var id = DomainId.Create(type.Value); if (components.TryGetValue(id, out var schema)) { return(ConvertNested(schema.FieldCollection, obj, null, converters, components)); } else { return(obj); } } return(null); }
private (DomainId, RootField <ComponentFieldProperties>, ResolvedComponents) Field(ComponentFieldProperties properties, bool isRequired = false) { var schema = new Schema("my-component") .AddNumber(1, "componentField", Partitioning.Invariant, new NumberFieldProperties { IsRequired = isRequired }); var field = Fields.Component(1, "myComponent", Partitioning.Invariant, properties); var components = new ResolvedComponents(new Dictionary <DomainId, Schema> { [schemaId1] = schema, [schemaId2] = schema, }); return(schemaId1, field, components); }
public static EdmComplexType BuildEdmType(this Schema schema, bool withHidden, PartitionResolver partitionResolver, EdmTypeFactory factory, ResolvedComponents components) { Guard.NotNull(factory, nameof(factory)); Guard.NotNull(partitionResolver, nameof(partitionResolver)); var(edmType, _) = factory("Data"); foreach (var field in schema.FieldsByName.Values) { if (!field.IsForApi(withHidden)) { continue; } var fieldEdmType = EdmTypeVisitor.BuildType(field, factory, components); if (fieldEdmType == null) { continue; } var(partitionType, created) = factory($"Data.{field.Name.ToPascalCase()}"); if (created) { var partitioning = partitionResolver(field.Partitioning); foreach (var partitionKey in partitioning.AllKeys) { partitionType.AddStructuralProperty(partitionKey.EscapeEdmField(), fieldEdmType); } } edmType.AddStructuralProperty(field.Name.EscapeEdmField(), new EdmComplexTypeReference(partitionType, false)); } return(edmType); }
private static EdmModel BuildEdmModel(Schema schema, IAppEntity app, ResolvedComponents components, bool withHiddenFields) { var model = new EdmModel(); var pascalAppName = app.Name.ToPascalCase(); var pascalSchemaName = schema.Name.ToPascalCase(); var typeFactory = new EdmTypeFactory(name => { var finalName = pascalSchemaName; if (!string.IsNullOrWhiteSpace(name)) { finalName += "."; finalName += name; } var result = model.SchemaElements.OfType <EdmComplexType>().FirstOrDefault(x => x.Name == finalName); if (result != null) { return(result, false); } result = new EdmComplexType(pascalAppName, finalName); model.AddElement(result); return(result, true); }); var schemaType = schema.BuildEdmType(withHiddenFields, app.PartitionResolver(), typeFactory, components); return(BuildEdmModel(app.Name.ToPascalCase(), schema.Name, model, schemaType)); }
private static void AddAssetIds(HashSet <DomainId> ids, ISchemaEntity schema, ResolvedComponents components, IEnumerable <ContentEntity> contents) { foreach (var content in contents) { content.Data.AddReferencedIds(schema.SchemaDef.ResolvingAssets(), ids, components, 1); } }
public sealed record Args(ResolvedComponents Components, Schema Schema, JsonTypeFactory Factory, bool WithHidden, bool WithComponents, int Level = 0);
public OperationsBuilder Schema(Schema schema, PartitionResolver partitionResolver, ResolvedComponents components, bool flat) { var typeName = schema.TypeName(); var dataSchema = RegisterReference($"{typeName}DataDto", _ => { return(schema.BuildJsonSchemaDynamic(partitionResolver, components, CreateReference, false, true)); }); var flatDataSchema = RegisterReference($"{typeName}FlatDataDto", _ => { return(schema.BuildJsonSchemaFlat(partitionResolver, components, CreateReference, false, true)); }); var contentSchema = RegisterReference($"{typeName}ContentDto", _ => { return(ContentJsonSchemaBuilder.BuildSchema(flat ? flatDataSchema : dataSchema, true)); }); var contentsSchema = RegisterReference($"{typeName}ContentResultDto", _ => { return(BuildResult(contentSchema)); }); var path = $"/content/{AppName}/{schema.Name}"; var builder = new OperationsBuilder { ContentSchema = contentSchema, ContentsSchema = contentsSchema, DataSchema = dataSchema, Path = path, Parent = this, SchemaDisplayName = schema.DisplayName(), SchemaName = schema.Name, SchemaTypeName = typeName }; builder.AddTag("API endpoints for [schema] content items."); return(builder); }
public static (Schema Schema, ResolvedComponents) MixedSchema(SchemaType type = SchemaType.Default) { var componentId1 = DomainId.NewGuid(); var componentId2 = DomainId.NewGuid(); var componentIds = ReadonlyList.Create(componentId1, componentId2); var component1 = new Schema("component1") .Publish() .AddString(1, "unique1", Partitioning.Invariant) .AddString(2, "shared1", Partitioning.Invariant) .AddBoolean(3, "shared2", Partitioning.Invariant); var component2 = new Schema("component2") .Publish() .AddNumber(1, "unique1", Partitioning.Invariant) .AddNumber(2, "shared1", Partitioning.Invariant) .AddBoolean(3, "shared2", Partitioning.Invariant); var resolvedComponents = new ResolvedComponents(new Dictionary <DomainId, Schema> { [componentId1] = component1, [componentId2] = component2, }); var schema = new Schema("user", type: type) .Publish() .AddArray(101, "root-array", Partitioning.Language, f => f .AddAssets(201, "nested-assets", new AssetsFieldProperties()) .AddBoolean(202, "nested-boolean", new BooleanFieldProperties()) .AddDateTime(203, "nested-datetime", new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime }) .AddDateTime(204, "nested-date", new DateTimeFieldProperties { Editor = DateTimeFieldEditor.Date }) .AddGeolocation(205, "nested-geolocation", new GeolocationFieldProperties()) .AddJson(206, "nested-json", new JsonFieldProperties()) .AddJson(207, "nested-json2", new JsonFieldProperties()) .AddNumber(208, "nested-number", new NumberFieldProperties()) .AddReferences(209, "nested-references", new ReferencesFieldProperties()) .AddString(210, "nested-string", new StringFieldProperties()) .AddTags(211, "nested-tags", new TagsFieldProperties()) .AddUI(212, "nested-ui", new UIFieldProperties())) .AddAssets(102, "root-assets", Partitioning.Invariant, new AssetsFieldProperties()) .AddBoolean(103, "root-boolean", Partitioning.Invariant, new BooleanFieldProperties()) .AddDateTime(104, "root-datetime", Partitioning.Invariant, new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime }) .AddDateTime(105, "root-date", Partitioning.Invariant, new DateTimeFieldProperties { Editor = DateTimeFieldEditor.Date }) .AddGeolocation(106, "root-geolocation", Partitioning.Invariant, new GeolocationFieldProperties()) .AddJson(107, "root-json", Partitioning.Invariant, new JsonFieldProperties()) .AddNumber(108, "root-number", Partitioning.Invariant, new NumberFieldProperties { MinValue = 1, MaxValue = 10 }) .AddReferences(109, "root-references", Partitioning.Invariant, new ReferencesFieldProperties()) .AddString(110, "root-string1", Partitioning.Invariant, new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ReadonlyList.Create("a", "b") }) .AddString(111, "root-string2", Partitioning.Invariant, new StringFieldProperties { Hints = "My String1" }) .AddTags(112, "root-tags", Partitioning.Language, new TagsFieldProperties()) .AddUI(113, "root-ui", Partitioning.Language, new UIFieldProperties()) .AddComponent(114, "root-component", Partitioning.Language, new ComponentFieldProperties { SchemaIds = componentIds }) .AddComponents(115, "root-components", Partitioning.Language, new ComponentsFieldProperties { SchemaIds = componentIds }) .Update(new SchemaProperties { Hints = "The User" }) .HideField(104) .HideField(211, 101) .DisableField(109) .DisableField(212, 101) .LockField(105); return(schema, resolvedComponents); }
public static QueryModel Build(Schema?schema, PartitionResolver partitionResolver, ResolvedComponents components) { var fields = new List <FilterField> { new FilterField(FilterSchema.String, "id") { Description = FieldDescriptions.EntityId }, new FilterField(FilterSchema.Boolean, "isDeleted") { Description = FieldDescriptions.EntityIsDeleted }, new FilterField(FilterSchema.DateTime, "created") { Description = FieldDescriptions.EntityCreated }, new FilterField(SharedSchemas.User, "createdBy") { Description = FieldDescriptions.EntityCreatedBy }, new FilterField(FilterSchema.DateTime, "lastModified") { Description = FieldDescriptions.EntityLastModified }, new FilterField(SharedSchemas.User, "lastModifiedBy") { Description = FieldDescriptions.EntityLastModifiedBy }, new FilterField(FilterSchema.Number, "version") { Description = FieldDescriptions.EntityVersion }, new FilterField(SharedSchemas.Status, "status") { Description = FieldDescriptions.ContentStatus, }, new FilterField(SharedSchemas.Status, "newStatus", IsNullable: true) { Description = FieldDescriptions.ContentNewStatus } }; if (schema != null) { var dataSchema = schema.BuildDataSchema(partitionResolver, components); fields.Add(new FilterField(dataSchema, "data") { Description = FieldDescriptions.ContentData }); } var filterSchema = new FilterSchema(FilterSchemaType.Object) { Fields = fields.ToReadonlyList() }; return(new QueryModel { Schema = filterSchema }); }