Skip to content

Commit ae329e2

Browse files
Merge pull request #165 from microsoftgraph/incorpApiDoctor
Incorp api doctor into TypeWriter
2 parents e3a1d22 + 3dfdc88 commit ae329e2

File tree

5 files changed

+175
-31
lines changed

5 files changed

+175
-31
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using ApiDoctor.Publishing.CSDL;
2+
using ApiDoctor.Validation;
3+
using ApiDoctor.Validation.Error;
4+
using ApiDoctor.Validation.OData;
5+
using ApiDoctor.Validation.OData.Transformation;
6+
using NLog;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.Linq;
10+
using System.Threading.Tasks;
11+
12+
namespace Typewriter
13+
{
14+
/// <summary>
15+
/// Creates a CSDL file with documentation annotations sourced from documentation.
16+
/// </summary>
17+
internal class DocAnnotationWriter : ApiDoctor.Publishing.CSDL.CsdlWriter
18+
{
19+
private static Logger Logger => LogManager.GetLogger("DocAnnotationWriter");
20+
21+
private readonly CsdlWriterOptions options;
22+
23+
internal DocAnnotationWriter(DocSet docSet, CsdlWriterOptions options, string csdl) : base(docSet, options)
24+
{
25+
this.options = options; // Can change the base access modifier so we could use it.
26+
}
27+
28+
public async Task<string> PublishToStringAsync(IssueLogger issues)
29+
{
30+
string outputFilenameSuffix = "";
31+
32+
Logger.Info("Begin creating metadata file with documentation annotations.");
33+
34+
// Step 1: Generate an EntityFramework OM from the documentation and/or template file
35+
EntityFramework framework = CreateEntityFrameworkFromDocs(issues);
36+
if (null == framework)
37+
return string.Empty;
38+
39+
// Step 1a: Apply an transformations that may be defined in the documentation
40+
if (!string.IsNullOrEmpty(options.TransformOutput))
41+
{
42+
PublishSchemaChangesConfigFile transformations = DocSet.TryLoadConfigurationFiles<PublishSchemaChangesConfigFile>(options.DocumentationSetPath).Where(x => x.SchemaChanges.TransformationName == options.TransformOutput).FirstOrDefault();
43+
if (null == transformations)
44+
{
45+
throw new KeyNotFoundException($"Unable to locate a transformation set named {options.TransformOutput}. Aborting.");
46+
}
47+
48+
string[] versionsToPublish = options.Version?.Split(new char[] { ',', ' ' });
49+
framework.ApplyTransformation(transformations.SchemaChanges, versionsToPublish);
50+
if (!string.IsNullOrEmpty(options.Version))
51+
{
52+
outputFilenameSuffix += $"-{options.Version}";
53+
}
54+
}
55+
56+
if (options.Sort)
57+
{
58+
// Sorts the objects in collections, so that we have consistent output regardless of input
59+
framework.SortObjectGraph();
60+
}
61+
62+
if (options.ValidateSchema)
63+
{
64+
framework.ValidateSchemaTypes();
65+
}
66+
67+
// Step 2: Generate XML representation of EDMX
68+
string xmlData = ODataParser.Serialize<EntityFramework>(framework, options.AttributesOnNewLines);
69+
70+
Logger.Info("Finish creating metadata file with documentation annotations.");
71+
72+
return xmlData;
73+
}
74+
}
75+
76+
internal static class AnnotationHelper
77+
{
78+
private static Logger Logger => LogManager.GetLogger("AnnotationHelper");
79+
80+
internal async static Task<string> ApplyAnnotationsToCsdl(string csdl, Options options)
81+
{
82+
// Get DocSet
83+
DocSet docs = GetDocSet(options, new IssueLogger());
84+
85+
var csdlWriterOptions = new CsdlWriterOptions()
86+
{
87+
DocumentationSetPath = options.DocsRoot + "\\api-reference\\v1.0\\",
88+
Annotations = AnnotationOptions.Properties,
89+
SourceMetadataPath = options.Metadata,
90+
SkipMetadataGeneration = true,
91+
Formats = MetadataFormat.EdmxInput
92+
};
93+
94+
DocAnnotationWriter docWriter = new DocAnnotationWriter(docs, csdlWriterOptions, csdl);
95+
96+
return await docWriter.PublishToStringAsync(new IssueLogger());
97+
}
98+
99+
100+
private static DocSet GetDocSet(Options options, IssueLogger issues)
101+
{
102+
Logger.Info("Opening documentation from {0}", options.DocsRoot);
103+
DocSet docSet = null;
104+
105+
try
106+
{
107+
docSet = new DocSet(options.DocsRoot + "\\api-reference\\v1.0\\");
108+
}
109+
catch (System.IO.FileNotFoundException ex)
110+
{
111+
Logger.Error(ex.Message);
112+
return null;
113+
}
114+
115+
Logger.Info("Parsing documentation files");
116+
var stopwatch = new Stopwatch();
117+
stopwatch.Start();
118+
docSet.ScanDocumentation(string.Empty, issues);
119+
stopwatch.Stop();
120+
Logger.Info($"Took {stopwatch.Elapsed} to parse {docSet.Files.Length} source files.");
121+
122+
return docSet;
123+
}
124+
}
125+
}

src/Typewriter/MetadataPreprocessor.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -129,24 +129,31 @@ internal static void RemoveCapabilityAnnotations()
129129
/// </summary>
130130
internal static void AddLongDescriptionToThumbnail()
131131
{
132-
// Thumbnail hack - add LongDescription annotation
133-
XElement thumbnailComplexType = xMetadata.Descendants()
134-
.Where(x => (string)x.Name.LocalName == "ComplexType")
135-
.Where(x => x.Attribute("Name").Value == "thumbnail")
136-
.First();
137-
138-
if (thumbnailComplexType != null)
132+
try
139133
{
140-
// need to specify namespace so default xmlns="" isn't added that breaks VIPR
141-
XElement thumbnailAnnotation = new XElement(thumbnailComplexType.Name.Namespace + "Annotation");
134+
// Thumbnail hack - add LongDescription annotation
135+
XElement thumbnailComplexType = xMetadata.Descendants()
136+
.Where(x => (string)x.Name.LocalName == "ComplexType")
137+
.Where(x => x.Attribute("Name").Value == "thumbnail")
138+
.First();
139+
140+
if (thumbnailComplexType != null)
141+
{
142+
// need to specify namespace so default xmlns="" isn't added that breaks VIPR
143+
XElement thumbnailAnnotation = new XElement(thumbnailComplexType.Name.Namespace + "Annotation");
142144

143-
thumbnailAnnotation.Add(new XAttribute("Term", "Org.OData.Core.V1.LongDescription"));
144-
thumbnailAnnotation.Add(new XAttribute("String", "navigable"));
145-
thumbnailComplexType.Add(thumbnailAnnotation);
145+
thumbnailAnnotation.Add(new XAttribute("Term", "Org.OData.Core.V1.LongDescription"));
146+
thumbnailAnnotation.Add(new XAttribute("String", "navigable"));
147+
thumbnailComplexType.Add(thumbnailAnnotation);
146148

147-
Logger.Info("AddLongDescriptionToThumbnail rule was applied to the thumbnail complex type.");
149+
Logger.Info("AddLongDescriptionToThumbnail rule was applied to the thumbnail complex type.");
150+
}
151+
else
152+
{
153+
Logger.Error("AddLongDescriptionToThumbnail rule was not applied to the thumbnail complex type because the type wasn't found.");
154+
}
148155
}
149-
else
156+
catch (InvalidOperationException)
150157
{
151158
Logger.Error("AddLongDescriptionToThumbnail rule was not applied to the thumbnail complex type because the type wasn't found.");
152159
}

src/Typewriter/Options.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
6-
using CommandLine;
1+
using CommandLine;
72

83
namespace Typewriter
94
{
@@ -28,5 +23,8 @@ class Options
2823

2924
[Option('o', "output", Default= ".", HelpText = "Path to output folder")]
3025
public string Output { get; set; }
26+
27+
[Option('d', "docs", Default = ".", HelpText = "Path to the root of the documentation repo folder")]
28+
public string DocsRoot { get; set; }
3129
}
32-
}
30+
}

src/Typewriter/Program.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Diagnostics;
4-
using System.Linq;
5-
using System.Text;
6-
using System.Threading.Tasks;
7-
using CommandLine;
1+
using CommandLine;
82
using Microsoft.Graph.ODataTemplateWriter.TemplateProcessor;
93
using NLog;
104
using NLog.Config;
115
using NLog.Targets;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
129
using Vipr.Core;
1310
using Vipr.Reader.OData.v4;
1411

@@ -32,12 +29,16 @@ private static void GenerateSDK(Options options)
3229

3330
SetupLogging(options.Verbosity);
3431

35-
var csdlContents = MetadataResolver.GetMetadata(options.Metadata);
32+
string csdlContents = MetadataResolver.GetMetadata(options.Metadata);
3633

3734
// Clean up EDMX to work with the generators assumptions.
38-
var processCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
35+
string processedCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
3936

40-
var files = MetadataToClientSource(processCsdlContents, options.Language);
37+
// Inject documentation annotations into the CSDL using ApiDoctor.
38+
string csdlWithDocAnnotations = AnnotationHelper.ApplyAnnotationsToCsdl(processedCsdlContents, options).Result;
39+
40+
// Create code files from the CSDL with annotations for the target platform and write those files to disk.
41+
var files = MetadataToClientSource(csdlWithDocAnnotations, options.Language);
4142
FileWriter.WriteAsync(files, options.Output);
4243

4344
stopwatch.Stop();
@@ -84,8 +85,17 @@ private static void HandleError(IEnumerable<Error> errors)
8485
}
8586
}
8687

88+
/// <summary>
89+
/// Generates code files from an edmx file.
90+
/// </summary>
91+
/// <param name="edmxString">The EDMX file as a string.</param>
92+
/// <param name="targetLanguage">Specifies the target language. Possible values are csharp, php, etc.</param>
93+
/// <returns></returns>
8794
static private IEnumerable<TextFile> MetadataToClientSource(string edmxString, string targetLanguage)
8895
{
96+
if (String.IsNullOrEmpty(edmxString))
97+
throw new ArgumentNullException("edmxString", "The EDMX file string contains no content.");
98+
8999
var reader = new OdcmReader();
90100
var writer = new TemplateWriter(targetLanguage);
91101
writer.SetConfigurationProvider(new ConfigurationProvider());

src/Typewriter/Typewriter.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
</ItemGroup>
4444
<ItemGroup>
4545
<Compile Include="ConfigurationProvider.cs" />
46+
<Compile Include="DocAnnotationWriter.cs" />
4647
<Compile Include="FileWriter.cs" />
4748
<Compile Include="MetadataPreprocessor.cs" />
4849
<Compile Include="MetadataResolver.cs" />
@@ -68,6 +69,9 @@
6869
</ProjectReference>
6970
</ItemGroup>
7071
<ItemGroup>
72+
<PackageReference Include="ApiDoctor.Publishing">
73+
<Version>1.0.0-CI-20181030-211105</Version>
74+
</PackageReference>
7175
<PackageReference Include="CommandLineParser">
7276
<Version>2.2.1</Version>
7377
</PackageReference>

0 commit comments

Comments
 (0)