public static DocumentSchema Load(TextReader reader, string title) { using (var json = new JsonTextReader(reader)) { DocumentSchema schema; try { schema = DefaultSerializer.Value.Deserialize <DocumentSchema>(json); } catch (Exception e) { throw new InvalidSchemaException($"Not a valid schema: {e.Message}", e); } SchemaValidator.Validate(schema); if (string.IsNullOrWhiteSpace(schema.Title)) { if (string.IsNullOrWhiteSpace(title)) { throw new InvalidSchemaException($"Title of schema must be specified."); } schema.Title = title; } if (schema.Type != JSchemaType.Object) { throw new InvalidSchemaException("Type for the root schema object must be object"); } if (!JsonPointer.TryCreate(schema.Metadata, out var pointer)) { throw new InvalidJsonPointerException($"Metadata's json pointer {schema.Metadata} is invalid."); } var metadataSchema = pointer.FindSchema(schema); if (metadataSchema != null && metadataSchema.Type != JSchemaType.Object) { throw new InvalidJsonPointerException($"The referenced object is in type: {metadataSchema.Type}, only object can be a metadata reference"); } schema.MetadataReference = pointer; return(schema); } }
public static DocumentSchema Load(TextReader reader, string title) { DocumentSchema schema; using var jtr = new JsonTextReader(reader); JSchema jSchema; JObject jObject; try { jObject = JObject.Load(jtr); jSchema = JSchema.Load(jObject.CreateReader()); } catch (Exception e) when(e is JSchemaException || e is JsonException) { var message = ($"{title} is not a valid schema: {e.Message}"); Logger.LogError(message, code: ErrorCodes.Build.ViolateSchema); throw new InvalidSchemaException(message, e); } var validator = new SchemaValidator(jObject, jSchema); // validate schema here validator.ValidateMetaSchema(); try { schema = LoadSchema <DocumentSchema>(jSchema, new Dictionary <JSchema, BaseSchema>()); schema.SchemaVersion = jSchema.SchemaVersion; schema.Id = jSchema.Id; schema.Version = GetValueFromJSchemaExtensionData <string>(jSchema, "version"); schema.Metadata = GetValueFromJSchemaExtensionData <string>(jSchema, "metadata"); schema.Validator = validator; } catch (Exception e) { var message = $"{title} is not a valid schema: {e.Message}"; Logger.LogError(message, code: ErrorCodes.Build.ViolateSchema); throw new InvalidSchemaException(message, e); } if (string.IsNullOrWhiteSpace(schema.Title)) { if (string.IsNullOrWhiteSpace(title)) { var message = "Title of schema must be specified."; Logger.LogError(message, code: ErrorCodes.Build.ViolateSchema); throw new InvalidSchemaException(message); } schema.Title = title; } if (schema.Type != JSchemaType.Object) { var message = "Type for the root schema object must be object"; Logger.LogError(message, code: ErrorCodes.Build.ViolateSchema); throw new InvalidSchemaException(message); } if (!JsonPointer.TryCreate(schema.Metadata, out var pointer)) { var message = $"Metadata's json pointer {schema.Metadata} is invalid."; Logger.LogError(message, code: ErrorCodes.Build.ViolateSchema); throw new InvalidSchemaException(message); } var metadataSchema = pointer.FindSchema(schema); if (metadataSchema != null && metadataSchema.Type != JSchemaType.Object) { throw new InvalidJsonPointerException($"The referenced object is in type: {metadataSchema.Type}, only object can be a metadata reference"); } schema.MetadataReference = pointer; schema.AllowOverwrite = CheckOverwriteAbility(schema); schema.Hash = JsonUtility.Serialize(jObject).GetMd5String(); return(schema); }
private void ApplyOverwriteToModel(OverwriteApplier overwriteApplier, string uid, IHostService host) { var ms = host.LookupByUid(uid); var ods = ms.Where(m => m.Type == DocumentType.Overwrite).ToList(); var articles = ms.Except(ods).ToList(); if (articles.Count == 0 || ods.Count == 0) { return; } if (articles.Count > 1) { throw new DocumentException($"{uid} is defined in multiple articles {articles.Select(s => s.LocalPathFromRoot).ToDelimitedString()}"); } var model = articles[0]; var schema = model.Properties.Schema as DocumentSchema; using (new LoggerFileScope(model.LocalPathFromRoot)) { var uidDefiniton = model.Uids.Where(s => s.Name == uid).ToList(); if (uidDefiniton.Count == 0) { throw new DocfxException($"Unable to find UidDefinition for Uid {uid}"); } try { foreach (var ud in uidDefiniton) { var jsonPointer = new JsonPointer(ud.Path).GetParentPointer(); var schemaForCurrentUid = jsonPointer.FindSchema(schema); var source = jsonPointer.GetValue(model.Content); foreach (var od in ods) { using (new LoggerFileScope(od.LocalPathFromRoot)) { foreach (var fm in ((IEnumerable <OverwriteDocumentModel>)od.Content).Where(s => s.Uid == uid)) { // Suppose that BuildOverwriteWithSchema do the validation of the overwrite object var overwriteObject = overwriteApplier.BuildOverwriteWithSchema(od, fm, schemaForCurrentUid); overwriteApplier.MergeContentWithOverwrite(ref source, overwriteObject, ud.Name, string.Empty, schemaForCurrentUid); model.LinkToUids = model.LinkToUids.Union(od.LinkToUids); model.LinkToFiles = model.LinkToFiles.Union(od.LinkToFiles); model.FileLinkSources = model.FileLinkSources.Merge(od.FileLinkSources); model.UidLinkSources = model.UidLinkSources.Merge(od.UidLinkSources); } } } } // 1. Validate schema after the merge // TODO: Issue exists - however unable to identify which overwrite document the issue is from ((SchemaDrivenDocumentProcessor)host.Processor).SchemaValidator.Validate(model.Content); // 2. Re-export xrefspec after the merge overwriteApplier.UpdateXrefSpec(model, schema); } catch (DocumentException e) { // Log error here to preserve file info Logger.LogError(e.Message); throw; } } }
public override void Postbuild(ImmutableList<FileModel> models, IHostService host) { foreach (var uid in host.GetAllUids()) { var ms = host.LookupByUid(uid); var ods = ms.Where(m => m.Type == DocumentType.Overwrite).ToList(); var articles = ms.Except(ods).ToList(); if (articles.Count == 0 || ods.Count == 0) { continue; } if (articles.Count > 1) { throw new DocumentException($"{uid} is defined in multiple articles {articles.Select(s => s.LocalPathFromRoot).ToDelimitedString()}"); } var model = articles[0]; var schema = model.Properties.Schema as DocumentSchema; using (new LoggerFileScope(model.LocalPathFromRoot)) { var uidDefiniton = model.Uids.Where(s => s.Name == uid).ToList(); if (uidDefiniton.Count == 0) { throw new DocfxException($"Unable to find UidDefinition for Uid {uid}"); } foreach (var ud in uidDefiniton) { var jsonPointer = new JsonPointer(ud.Path).GetParentPointer(); var schemaForCurrentUid = jsonPointer.FindSchema(schema); var source = jsonPointer.GetValue(model.Content); foreach (var od in ods) { using (new LoggerFileScope(od.LocalPathFromRoot)) { foreach (var fm in ((IEnumerable<OverwriteDocumentModel>)od.Content).Where(s => s.Uid == uid)) { // Suppose that BuildOverwriteWithSchema do the validation of the overwrite object var overwriteObject = BuildOverwriteWithSchema(od, fm, host, schemaForCurrentUid); _merger.Merge(ref source, overwriteObject, ud.Name, string.Empty, schemaForCurrentUid); model.LinkToUids = model.LinkToUids.Union(od.LinkToUids); model.LinkToFiles = model.LinkToFiles.Union(od.LinkToFiles); model.FileLinkSources = model.FileLinkSources.Merge(od.FileLinkSources); model.UidLinkSources = model.UidLinkSources.Merge(od.UidLinkSources); } } } } // 1. Validate schema after the merge ((SchemaDrivenDocumentProcessor)host.Processor).SchemaValidator.Validate(model.Content); // 2. Re-export xrefspec after the merge var context = new ProcessContext(host, model); _xrefSpecUpdater.Process(model.Content, schema, context); UpdateXRefSpecs((List<XRefSpec>)model.Properties.XRefSpecs, context.XRefSpecs); UpdateXRefSpecs((List<XRefSpec>)model.Properties.ExternalXRefSpecs, context.ExternalXRefSpecs); } } }