Skip to content

Commit 324f250

Browse files
Merge pull request #166 from microsoftgraph/generatorOptions
Add generation options
2 parents ae329e2 + 54a24e0 commit 324f250

File tree

11 files changed

+245
-42
lines changed

11 files changed

+245
-42
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Source code writers for [VIPR][vipr-source-repo] utilizing T4 templates. The Gra
99
Currently the following target languages are supported by this writer:
1010
- Android
1111
- CSharp
12+
- Java
1213
- Objective-C
1314
- Python
1415
- TypeScript
@@ -35,6 +36,41 @@ Once setup is complete, you can work with the GraphODataTemplateWriter solution
3536

3637
For more information on submodules read [this chapter](http://git-scm.com/book/en/v2/Git-Tools-Submodules) from the Git book and search the Web.
3738

39+
## Using Typewriter
40+
41+
Typewriter is a new solution for generating code files using the GraphODataTemplateWriter and VIPR. It is an executable that is intended to simplify the generation of code files. Build the solution to find the typewriter executable in `\MSGraph-SDK-Code-Generator\src\Typewriter\bin\Release`. The typewriter run options are:
42+
43+
* **-l**, **-language**: The target language for the generated code files. The values can be: `Android`, `Java`, `ObjC`, `CSharp`, `PHP`, `Python`, `TypeScript`, or `GraphEndpointList`. The default value is `CSharp`. This is not applicable when only generating clean and annotated metadata as specified by the `-generationmode Metadata` option.
44+
* **-m**, **-metadata**: The local file path or URL to the target input metadata. The default value is `https://graph.microsoft.com/v1.0/$metadata`. This value is required.
45+
* **-v**, **-verbosity**: The log verbosity level. The values can be: `Minimal`, `Info`, `Debug`, or `Trace`. The default value is `Minimal`.
46+
* **-o**, **-output**: Specifies the path to the output folder. The default value is the directory that contains typewriter.exe. The structure and contents of the output directory will be different based on the `-generationmode` and `-language` options.
47+
* **-d**, **-docs**: Specifies the path to the local root of the [microsoft-graph-docs](https://github.com/microsoftgraph/microsoft-graph-docs) repo. The default value is the directory that contains typewriter.exe. The documentation is parsed to provide documentation annotations to the metadata which is then used to add doc comments in the generated code files. This option is required when using `-generationmode` values of `Metadata` or `Full`.
48+
* **-g**, **-generationmode**: Specifies the generation mode. The values can be: `Full`, `Metadata`, or `Files`. `Full` (default) generation mode produces the output code files by cleaning the input metadata, parsing the documentation, and adding annotations before generating the output files. `Metadata` generation mode produces an output metadata file by cleaning metadata, documentation parsing, and adding documentation annotations. `Files` generation mode produces code files from an input metadata and bypasses the cleaning, documentation parsing, and adding documentation annotations.
49+
* **-f**, **-outputMetadataFileName**: The base output metadata filename. Only applicable for `-generationmode Metadata`. The default value is `cleanMetadataWithDescriptions` which is used with the value of the `-endpointVersion` to generate a metadata file named `cleanMetadataWithDescriptionsv1.0.xml`.
50+
* **-e**, **-endpointVersion**: The endpoint version used when naming a metadata file. Expected values are `v1.0` and `beta`. Only applicable for `-generationmode Metadata`.
51+
52+
### Example typewriter usage
53+
54+
#### Generate TypeScript typings from a CSDL (metadata) file without cleaning or annotating the CSDL.
55+
56+
The output will go in to the `outputTypeScript` directory.
57+
58+
`.\typewriter.exe -v Info -m D:\cleanMetadataWithDescriptions_v10.xml -o outputTypeScript -l TypeScript -g Files`
59+
60+
#### Clean and annotate a metadata file with documentation annotations sourced from the documentation repo
61+
62+
The output metadata file will go in to the `output2` directory. The output metadata file will be named `cleanMetadataWithDescriptionsv1.0.xml` based on the default values.
63+
64+
`.\typewriter.exe -v Info -m D:\v1.0_2018_10_23_source.xml -o output2 -d D:\repos\microsoft-graph-docs -g Metadata`
65+
66+
#### Generate C# code files from the metadata that will be cleaned and annotated with documentation annotations sourced from the documentation repo
67+
68+
The output C# code files will go in to the `output` directory.
69+
70+
`.\typewriter.exe -v Info -m D:\v1.0_2018_10_23_source.xml -o output -l CSharp -d D:\repos\microsoft-graph-docs -g Full`
71+
72+
73+
3874
## Using Vipr with this Writer
3975

4076
1. Build the solution in Visual Studio.

src/Typewriter/DocAnnotationWriter.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal class DocAnnotationWriter : ApiDoctor.Publishing.CSDL.CsdlWriter
2020

2121
private readonly CsdlWriterOptions options;
2222

23-
internal DocAnnotationWriter(DocSet docSet, CsdlWriterOptions options, string csdl) : base(docSet, options)
23+
internal DocAnnotationWriter(DocSet docSet, CsdlWriterOptions options) : base(docSet, options)
2424
{
2525
this.options = options; // Can change the base access modifier so we could use it.
2626
}
@@ -77,21 +77,31 @@ internal static class AnnotationHelper
7777
{
7878
private static Logger Logger => LogManager.GetLogger("AnnotationHelper");
7979

80-
internal async static Task<string> ApplyAnnotationsToCsdl(string csdl, Options options)
80+
/// <summary>
81+
/// Applies annotations to CSDL file.
82+
/// </summary>
83+
/// <param name="options">The typewriter input options.</param>
84+
/// <param name="pathToCleanMetadata">Optional. Contains the path to a clean metadata to use when applying annotations. Overrides Option.Metadata.</param>
85+
/// <returns>An annotated metadata file.</returns>
86+
internal async static Task<string> ApplyAnnotationsToCsdl(Options options, string pathToCleanMetadata = null)
8187
{
82-
// Get DocSet
8388
DocSet docs = GetDocSet(options, new IssueLogger());
8489

8590
var csdlWriterOptions = new CsdlWriterOptions()
8691
{
8792
DocumentationSetPath = options.DocsRoot + "\\api-reference\\v1.0\\",
8893
Annotations = AnnotationOptions.Properties,
89-
SourceMetadataPath = options.Metadata,
9094
SkipMetadataGeneration = true,
9195
Formats = MetadataFormat.EdmxInput
9296
};
9397

94-
DocAnnotationWriter docWriter = new DocAnnotationWriter(docs, csdlWriterOptions, csdl);
98+
// We only intend to use the source metadata when we don't pass in a CSDL.
99+
if (string.IsNullOrEmpty(pathToCleanMetadata))
100+
csdlWriterOptions.SourceMetadataPath = options.Metadata;
101+
else
102+
csdlWriterOptions.SourceMetadataPath = pathToCleanMetadata;
103+
104+
DocAnnotationWriter docWriter = new DocAnnotationWriter(docs, csdlWriterOptions);
95105

96106
return await docWriter.PublishToStringAsync(new IssueLogger());
97107
}

src/Typewriter/FileWriter.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@ internal static class FileWriter
1717
private static ConcurrentDictionary<string, Lazy<AsyncLock>> lockDictionary = new ConcurrentDictionary<string, Lazy<AsyncLock>>();
1818
internal static Logger Logger => LogManager.GetLogger("FileWriter");
1919

20+
/// <summary>
21+
/// Writes a metadata file to disk.
22+
/// </summary>
23+
/// <param name="metadata">The metadata to write.</param>
24+
/// <param name="fileName">The output metadata filename.</param>
25+
/// <param name="outputDirectoryPath">Metadata write location.</param>
26+
public static string WriteMetadata(string metadata, string fileName, string outputDirectoryPath = null)
27+
{
28+
if (!string.IsNullOrWhiteSpace(outputDirectoryPath) && !Directory.Exists(outputDirectoryPath))
29+
Directory.CreateDirectory(outputDirectoryPath);
30+
if (string.IsNullOrWhiteSpace(outputDirectoryPath))
31+
outputDirectoryPath = Environment.CurrentDirectory;
32+
33+
var fullFileName = string.Concat(outputDirectoryPath, "\\", fileName);
34+
35+
File.WriteAllText(fullFileName, metadata);
36+
Logger.Info($"Metadata written to {fullFileName}");
37+
38+
return fullFileName;
39+
}
40+
2041
/// <summary>
2142
/// Write all generated files to disk
2243
/// </summary>

src/Typewriter/Generator.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using Microsoft.Graph.ODataTemplateWriter.TemplateProcessor;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Runtime.CompilerServices;
5+
using Vipr.Core;
6+
using Vipr.Reader.OData.v4;
7+
8+
[assembly: InternalsVisibleTo("GraphODataTemplateWriter.Test")]
9+
10+
namespace Typewriter
11+
{
12+
internal static class Generator
13+
{
14+
/// <summary>
15+
/// Generate code files from the input metadata and do not preprocess.
16+
/// </summary>
17+
/// <param name="csdlContents">Metadata to process</param>
18+
/// <param name="options">The options bag</param>
19+
static internal void GenerateFiles(string csdlContents, Options options)
20+
{
21+
var filesToWrite = MetadataToClientSource(csdlContents, options.Language);
22+
FileWriter.WriteAsync(filesToWrite, options.Output);
23+
}
24+
25+
/// <summary>
26+
/// Generate a metadata file that has been cleaned and doc annotations added to it.
27+
/// </summary>
28+
/// <param name="csdlContents">Metadata to process</param>
29+
/// <param name="options">The options bag</param>
30+
static internal void WriteCleanAnnotatedMetadata(string csdlContents, Options options)
31+
{
32+
string csdlWithDocAnnotations = CleanMetadata(csdlContents, options);
33+
string metadataFileName = string.Concat(options.OutputMetadataFileName, options.EndpointVersion, ".xml");
34+
FileWriter.WriteMetadata(csdlWithDocAnnotations, metadataFileName, options.Output);
35+
}
36+
37+
/// <summary>
38+
/// Clean and annotate the input metadata and then generate code files.
39+
/// </summary>
40+
/// <param name="csdlContents">Metadata to process</param>
41+
/// <param name="options">The options bag</param>
42+
static internal void GenerateFilesFromCleanMetadata(string csdlContents, Options options)
43+
{
44+
string csdlWithDocAnnotations = CleanMetadata(csdlContents, options);
45+
46+
// Create code files from the CSDL with annotations for the target platform and write those files to disk.
47+
GenerateFiles(csdlWithDocAnnotations, options);
48+
}
49+
50+
static private string CleanMetadata(string csdlContents, Options options)
51+
{
52+
// Clean up EDMX to work with the generators assumptions.
53+
string processedCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
54+
55+
// Create clean metadata and provide a path to it.
56+
string pathToCleanMetadata = FileWriter.WriteMetadata(processedCsdlContents, "cleanMetadata.xml");
57+
58+
// Inject documentation annotations into the CSDL using ApiDoctor and get back the file as a string.
59+
return AnnotationHelper.ApplyAnnotationsToCsdl(options, pathToCleanMetadata).Result;
60+
}
61+
62+
/// <summary>
63+
/// Generates code files from an edmx file.
64+
/// </summary>
65+
/// <param name="edmxString">The EDMX file as a string.</param>
66+
/// <param name="targetLanguage">Specifies the target language. Possible values are csharp, php, etc.</param>
67+
/// <returns></returns>
68+
static private IEnumerable<TextFile> MetadataToClientSource(string edmxString, string targetLanguage)
69+
{
70+
if (String.IsNullOrEmpty(edmxString))
71+
throw new ArgumentNullException("edmxString", "The EDMX file string contains no content.");
72+
73+
var reader = new OdcmReader();
74+
var writer = new TemplateWriter(targetLanguage);
75+
writer.SetConfigurationProvider(new ConfigurationProvider());
76+
77+
var model = reader.GenerateOdcmModel(new List<TextFile> { new TextFile("$metadata", edmxString) });
78+
79+
return writer.GenerateProxy(model);
80+
}
81+
}
82+
}

src/Typewriter/Options.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using CommandLine;
2+
using System.Runtime.CompilerServices;
3+
4+
[assembly: InternalsVisibleTo("GraphODataTemplateWriter.Test")]
25

36
namespace Typewriter
47
{
@@ -10,7 +13,26 @@ public enum VerbosityLevel
1013
Trace
1114
}
1215

13-
class Options
16+
/// <summary>
17+
/// Specifies how Typewriter will processes the input metadata and what type of outputs it produces.
18+
/// </summary>
19+
public enum GenerationMode
20+
{
21+
/// <summary>
22+
/// (default) Produces the output code files by cleaning the input metadata, parsing the docs, and adding annotations before generating the output files.
23+
/// </summary>
24+
Full,
25+
/// <summary>
26+
/// Produces an output metadata file by cleaning metadata, documentation parsing, and adding doc annotations.
27+
/// </summary>
28+
Metadata,
29+
/// <summary>
30+
/// Uses the input metadata and only generates code files for the target platform. It bypasses the cleaning, doc parsing, and adding doc annotations.
31+
/// </summary>
32+
Files
33+
}
34+
35+
public class Options
1436
{
1537
[Option('l', "language", Default = "CSharp", HelpText = "The target language for the generated code files. The values can be: Android, Java, ObjC, CSharp, PHP, Python, TypeScript, or GraphEndpointList")]
1638
public string Language { get; set; }
@@ -26,5 +48,17 @@ class Options
2648

2749
[Option('d', "docs", Default = ".", HelpText = "Path to the root of the documentation repo folder")]
2850
public string DocsRoot { get; set; }
51+
52+
[Option('g', "generationmode", Default = GenerationMode.Full, HelpText = "Specifies the generation mode. The values can be: Full, Metadata, or Files. Full generation mode produces " +
53+
"the output code files by cleaning the input metadata, parsing the documentation, and adding annotations before generating the output files. Metadata generation mode" +
54+
"produces an output metadata file by cleaning metadata, documentation parsing, and adding documentation annotations. Files generation mode produces code files from" +
55+
"an input metadata and bypasses the cleaning, documentation parsing, and adding documentation annotations.")]
56+
public GenerationMode GenerationMode { get; set; }
57+
58+
[Option('f', "outputMetadataFileName", Default = "cleanMetadataWithDescriptions", HelpText = "The output metadata filename. Only applicable for GenerationMode.Metadata.")]
59+
public string OutputMetadataFileName { get; set; }
60+
61+
[Option('e', "endpointVersion", Default = "v1.0", HelpText = "The endpoint version. Expected values are 'v1.0' and 'beta'. Only applicable for GenerationMode.Metadata.")]
62+
public string EndpointVersion { get; set; }
2963
}
3064
}

src/Typewriter/Program.cs

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,19 @@ private static void GenerateSDK(Options options)
3131

3232
string csdlContents = MetadataResolver.GetMetadata(options.Metadata);
3333

34-
// Clean up EDMX to work with the generators assumptions.
35-
string processedCsdlContents = MetadataPreprocessor.CleanMetadata(csdlContents);
36-
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);
42-
FileWriter.WriteAsync(files, options.Output);
34+
switch (options.GenerationMode)
35+
{
36+
case GenerationMode.Files:
37+
Generator.GenerateFiles(csdlContents, options);
38+
break;
39+
case GenerationMode.Metadata:
40+
Generator.WriteCleanAnnotatedMetadata(csdlContents, options);
41+
break;
42+
case GenerationMode.Full:
43+
default:
44+
Generator.GenerateFilesFromCleanMetadata(csdlContents, options);
45+
break;
46+
}
4347

4448
stopwatch.Stop();
4549
Logger.Info($"Generation time: {stopwatch.Elapsed } seconds.");
@@ -84,25 +88,5 @@ private static void HandleError(IEnumerable<Error> errors)
8488
Console.Write(item.ToString());
8589
}
8690
}
87-
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>
94-
static private IEnumerable<TextFile> MetadataToClientSource(string edmxString, string targetLanguage)
95-
{
96-
if (String.IsNullOrEmpty(edmxString))
97-
throw new ArgumentNullException("edmxString", "The EDMX file string contains no content.");
98-
99-
var reader = new OdcmReader();
100-
var writer = new TemplateWriter(targetLanguage);
101-
writer.SetConfigurationProvider(new ConfigurationProvider());
102-
103-
var model = reader.GenerateOdcmModel(new List<TextFile> { new TextFile("$metadata", edmxString) });
104-
105-
return writer.GenerateProxy(model);
106-
}
10791
}
10892
}

src/Typewriter/Typewriter.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<Compile Include="ConfigurationProvider.cs" />
4646
<Compile Include="DocAnnotationWriter.cs" />
4747
<Compile Include="FileWriter.cs" />
48+
<Compile Include="Generator.cs" />
4849
<Compile Include="MetadataPreprocessor.cs" />
4950
<Compile Include="MetadataResolver.cs" />
5051
<Compile Include="Options.cs" />
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using System.IO;
3+
4+
namespace Typewriter.Test
5+
{
6+
[TestClass]
7+
[Ignore] // Work is needed to generate from the test project.
8+
public class GeneratorTests
9+
{
10+
public string testMetadata;
11+
12+
/// <summary>
13+
/// Load metadata from file into a string so we can validate MetadataPreprocessor.
14+
/// </summary>
15+
[TestInitialize]
16+
public void Initialize()
17+
{
18+
testMetadata = Typewriter.Test.Properties.Resources.dirtyMetadata;
19+
}
20+
21+
[TestMethod]
22+
public void GenerateFilesTest()
23+
{
24+
const string outputDirectory = "output";
25+
26+
Options options = new Options()
27+
{
28+
Output = outputDirectory,
29+
Language = "TypeScript"
30+
};
31+
32+
Generator.GenerateFiles(testMetadata, options);
33+
34+
FileInfo fileInfo = new FileInfo(outputDirectory + @"\com\microsoft\graph\src\Microsoft-graph.d.ts");
35+
Assert.IsTrue(fileInfo.Exists);
36+
}
37+
}
38+
}

test/Typewriter.Test/MetadataPreprocessorTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
using System;
2-
using System.Xml.Linq;
3-
using Microsoft.VisualStudio.TestTools.UnitTesting;
4-
using System.Text;
5-
using Typewriter;
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
62
using System.Linq;
7-
using System.Collections.Generic;
3+
using System.Xml.Linq;
84

95
namespace Typewriter.Test
106
{

test/Typewriter.Test/Resources/dirtyMetadata.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<Property Name="url" Type="Edm.String" />
3030
<Property Name="width" Type="Edm.Int32" />
3131
</ComplexType>
32-
<EntityType Name="onenotePage" BaseType="microsoft.graph.onenoteEntitySchemaObjectModel" HasStream="true">
32+
<EntityType Name="onenotePage" HasStream="true">
3333
<Property Name="content" Type="Edm.Stream" />
3434
<NavigationProperty Name="parentNotebook" Type="microsoft.graph.notebook" ContainsTarget="true" />
3535
</EntityType>

0 commit comments

Comments
 (0)