Skip to content

Commit d8ffb4c

Browse files
Add support for specifying properties from the typewriter cli (#178)
* Added a test for validating custom PHP namespace property from the TypeWriter CLI. Fixed test metadata. * Add support for specifying key-value properties from the Typewriter CLI. * Fix broken tests. * Updated test metadata to match preprocessing * Changes support E2E testing when different target platforms are used in a test run for Typewriter. The TemplateProcessor host needs to be reset for the case when we target a new language. Otherwise the wrong *CodeWriter will be used and an unsupported cast will occur in the templates when trying to cast host.CodeWriter to the platform specific CodeWriter. The change to ConfigurationService.Settings is also required to reset the settings for configuring the TemplateWriter. * Updated typewriter tests to be more descriptive
1 parent ef05d6b commit d8ffb4c

File tree

15 files changed

+216
-103
lines changed

15 files changed

+216
-103
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ Typewriter is a new solution for generating code files using the GraphODataTempl
4848
* **-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.
4949
* **-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`.
5050
* **-e**, **-endpointVersion**: The endpoint version used when naming a metadata file. Expected values are `v1.0` and `beta`. Only applicable for `-generationmode Metadata`.
51+
* **-p**, **-properties**: Specify properties to support generation logic in the T4 templates. Properties must take the form of *key-string:value-string*. Multiple properties can be specified by setting a space in between property. The only property currently supported is the *php.namespace* property to specify the generated model file namespace. This property is optional.
5152

5253
### Example typewriter usage
5354

54-
#### Generate TypeScript typings from a CSDL (metadata) file without cleaning or annotating the CSDL.
55+
#### Generate TypeScript typings from a CSDL (metadata) file without cleaning or annotating the CSDL.
5556

5657
The output will go in to the `outputTypeScript` directory.
5758

5859
`.\typewriter.exe -v Info -m D:\cleanMetadataWithDescriptions_v10.xml -o outputTypeScript -l TypeScript -g Files`
5960

60-
#### Clean and annotate a metadata file with documentation annotations sourced from the documentation repo
61+
#### Clean and annotate a metadata file with documentation annotations sourced from the documentation repo
6162

6263
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.
6364

@@ -144,7 +145,7 @@ The type of template.
144145

145146
#### Template Name
146147

147-
To set the name of the template using the `Name` format string. You can insert `<Class>`, `<Property>`, `<Method>`, and `<Container>` the values will be replaced by the names of the corresponding object. If you insert an item that doesn't exist it will be replaced with an empty string.
148+
To set the name of the template using the `Name` format string. You can insert `<Class>`, `<Property>`, `<Method>`, and `<Container>` the values will be replaced by the names of the corresponding object. If you insert an item that doesn't exist it will be replaced with an empty string.
148149
Note: You can also set the template name from inside the template by : `host.SetTemplateName("foo");`
149150

150151
#### Template Editing
@@ -174,9 +175,9 @@ There are currently several steps we take to form the metadata into one that wil
174175
- Remove HasStream properties from ```onenotePage``` and ```onenoteEntityBaseModel```
175176
- Add ```ContainsTarget="true"``` to navigation properties that do not have a corresponding EntitySet. This currently applies to navigation properties that contain plannerBucket, plannerTask, plannerPlan, and plannerDelta.
176177
- Add long descriptions to types and properties from [docs](https://developer.microsoft.com/en-us/graph/docs/concepts/overview)
177-
178+
178179
In order to build against metadata other than that stored in the [metadata](https://github.com/microsoftgraph/MSGraph-SDK-Code-Generator/tree/master/metadata) directory, you will need to perform the first four on this list.
179-
180+
180181
## Contributing
181182

182183
Before we can accept your pull request, you'll need to electronically complete Microsoft's [Contributor License Agreement](https://cla.microsoft.com/). If you've done this for other Microsoft projects, then you're already covered.

Templates/PHP/Model/ComplexType.php.tt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@
55
CustomT4Host host = (CustomT4Host) Host;
66
OdcmModel model = host.CurrentModel;
77
CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter;
8+
TemplateWriterSettings settings = ConfigurationService.Settings;
89
OdcmClass complex = (OdcmClass)host.CurrentType;
910
String complexName = complex.Name.SanitizeEntityName();
11+
string targetNamespace = @"Microsoft\Graph\Model";
1012
String complexBaseName = "";
1113
if (complex.Base != null)
1214
complexBaseName = complex.Base.Name.SanitizeEntityName();
15+
16+
// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line
17+
// documentation for more information on how the TemplateWriterSettings.Properties is used.
18+
if (settings.Properties.ContainsKey("php.namespace"))
19+
{
20+
targetNamespace = settings.Properties["php.namespace"];
21+
}
22+
1323
#>
1424
<#=writer.WriteHeader(writer.GetDocBlock(complexName.ToCheckedCase()))#>
15-
namespace Microsoft\Graph\Model;
25+
namespace <#=targetNamespace#>;
1626
<#=writer.GetClassBlock(complexName.ToCheckedCase().ToString(), "Model")#>
1727
<#
1828
if (complex.Base != null) {
@@ -118,7 +128,7 @@ foreach(var property in complex.Properties.Where(prop => prop.Type.GetTypeString
118128
public function get<#=propertyName.ToCheckedCase()#>()
119129
{
120130
if (array_key_exists("<#=property.Name.ToCamelize()#>", $this->_propDict)) {
121-
if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "Microsoft\Graph\Model\<#=property.Type.GetTypeString()#>")) {
131+
if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=targetNamespace#>\<#=property.Type.GetTypeString()#>")) {
122132
return $this->_propDict["<#=property.Name.ToCamelize()#>"];
123133
} else {
124134
<# if (property.Type.GetTypeString() == "\\GuzzleHttp\\Psr7\\Stream") { #>

Templates/PHP/Model/EntityType.php.tt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter;
88
OdcmClass entity = host.CurrentType.AsOdcmClass();
99
TemplateWriterSettings settings = ConfigurationService.Settings;
1010
String entityName = entity.Name.SanitizeEntityName();
11+
string targetNamespace = @"Microsoft\Graph\Model";
1112
String entityBaseName = "";
13+
1214
if (entity.Base != null)
1315
entityBaseName = entity.Base.Name.SanitizeEntityName();
16+
17+
// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line
18+
// documentation for more information on how the TemplateWriterSettings.Properties is used.
19+
if (settings.Properties.ContainsKey("php.namespace"))
20+
{
21+
targetNamespace = settings.Properties["php.namespace"];
22+
}
23+
1424
#>
1525
<#=writer.WriteHeader(writer.GetDocBlock(entityName.ToCheckedCase()))#>
16-
namespace Microsoft\Graph\Model;
26+
namespace <#=targetNamespace#>;
1727

1828
<#=writer.GetClassBlock(entityName.ToCheckedCase().ToString(), "Model")#>
1929
<#
@@ -122,7 +132,7 @@ foreach(var property in entity.Properties.Where(prop => prop.Type.GetTypeString(
122132
if (property.Type.GetTypeString()[0] == '\\') { #>
123133
if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=property.Type.GetTypeString()#>")) {
124134
<# } else { #>
125-
if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "Microsoft\Graph\Model\<#=property.Type.GetTypeString()#>")) {
135+
if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=targetNamespace#>\<#=property.Type.GetTypeString()#>")) {
126136
<# } #>
127137
return $this->_propDict["<#=property.Name.ToCamelize()#>"];
128138
} else {

Templates/PHP/Model/EnumType.php.tt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
<#@ template debug="true" hostspecific="true" language="C#" #>
33
<#@ output extension="\\" #>
44
<#
5-
CustomT4Host host = (CustomT4Host) Host;
6-
OdcmModel model = host.CurrentModel;
7-
CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter;
5+
CustomT4Host host = (CustomT4Host) Host;
6+
OdcmModel model = host.CurrentModel;
7+
CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter;
8+
TemplateWriterSettings settings = ConfigurationService.Settings;
9+
string targetNamespace = @"Microsoft\Graph\Model";
10+
var enumT = host.CurrentType.AsOdcmEnum();
11+
12+
// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line
13+
// documentation for more information on how the TemplateWriterSettings.Properties is used.
14+
if (settings.Properties.ContainsKey("php.namespace"))
15+
{
16+
targetNamespace = settings.Properties["php.namespace"];
17+
}
818

9-
var enumT = host.CurrentType.AsOdcmEnum();
1019
#>
1120
<#=writer.WriteHeader(writer.GetDocBlock(enumT.Name.ToCheckedCase()))#>
12-
namespace Microsoft\Graph\Model;
21+
namespace <#=targetNamespace#>;
1322

1423
use Microsoft\Graph\Core\Enum;
1524

src/GraphODataTemplateWriter/Settings/ConfigurationService.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,46 @@
33
namespace Microsoft.Graph.ODataTemplateWriter.Settings
44
{
55
using System;
6+
using System.Collections.Generic;
67
using System.Reflection;
78
using System.Reflection.Emit;
89
using Vipr.Core;
910

11+
/// <summary>
12+
/// The ConfigurationService configures the template writer with the target language and
13+
/// other properties used to direct how the code files are generated.
14+
/// </summary>
1015
public static class ConfigurationService
1116
{
1217
private static IConfigurationProvider _configurationProvider;
1318
private static TemplateWriterSettings templateWriterSettings = null;
1419
private static string targetLanguage = null;
15-
public static void Initialize(IConfigurationProvider configurationProvider, string targetLanguage = null)
20+
private static Dictionary<string, string> properties = null;
21+
22+
public static void Initialize(IConfigurationProvider configurationProvider, string targetLanguage = null, IEnumerable<string> properties = null)
1623
{
1724
_configurationProvider = configurationProvider;
1825
if (!String.IsNullOrEmpty(targetLanguage))
1926
{
2027
ConfigurationService.targetLanguage = targetLanguage;
2128
}
29+
if (properties != null)
30+
{
31+
Dictionary<string, string> propertyDictionary = new Dictionary<string, string>();
32+
foreach (string property in properties)
33+
{
34+
string[] props = property.Split(':');
35+
36+
if (props.Length != 2)
37+
{
38+
throw new ArgumentException("A property was set in a unexpected form from the typewriter commandline.", "-p -properties");
39+
}
40+
41+
propertyDictionary.Add(props[0],props[1]);
42+
}
43+
44+
ConfigurationService.properties = propertyDictionary;
45+
}
2246
}
2347

2448
private static TemplateWriterSettings LoadSettingsForLanguage()
@@ -29,6 +53,11 @@ private static TemplateWriterSettings LoadSettingsForLanguage()
2953
{
3054
mainTWS.TargetLanguage = targetLanguage;
3155
}
56+
if (properties != null)
57+
{
58+
mainTWS.Properties = properties;
59+
}
60+
3261

3362
TemplateWriterSettings.mainSettingsObject = mainTWS;
3463

@@ -72,7 +101,7 @@ public static TemplateWriterSettings Settings
72101
{
73102
get
74103
{
75-
if (templateWriterSettings == null)
104+
if (templateWriterSettings == null || templateWriterSettings.TargetLanguage != ConfigurationService.targetLanguage)
76105
{
77106
templateWriterSettings = _configurationProvider != null
78107
? LoadSettingsForLanguage()

src/GraphODataTemplateWriter/Settings/TemplateWriterSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ public Dictionary<string, List<Dictionary<string, string>>> TemplateMapping
101101

102102
private readonly List<Dictionary<string, string>> templateConfiguration;
103103

104+
/// <summary>
105+
/// A dictionary of strings that represent a property. These properties can be provided from the command line
106+
/// and used in the templates.
107+
/// </summary>
108+
public Dictionary<string, string> Properties { get; set; }
109+
104110
/// <summary>
105111
/// The dictionary created by combining the "Shared" and current language mapping.
106112
/// </summary>

src/GraphODataTemplateWriter/TemplateProcessor/TemplateProcessor.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,11 @@ public class TemplateProcessor : ITemplateProcessor
2525

2626
protected static CustomT4Host Host(ITemplateInfo templateInfo, string templatesDirectory, OdcmObject odcmObject, OdcmModel odcmModel)
2727
{
28-
if (_host == null)
29-
{
30-
_host = new CustomT4Host(templateInfo, templatesDirectory, odcmObject, odcmModel);
31-
}
32-
else
33-
{
34-
_host.Reset(templateInfo, templatesDirectory, odcmObject, odcmModel);
35-
}
28+
// Need to always set the host. Typically, this is run against a single platform when generating codefiels.
29+
// In test cases, we need to target multiple platforms. Since much of this code is static, we need to make sure
30+
// reset the information provided to the template processor. This change fixes a bug when targeting
31+
// multiple platforms in a test.
32+
_host = new CustomT4Host(templateInfo, templatesDirectory, odcmObject, odcmModel);
3633

3734
return _host;
3835
}

src/GraphODataTemplateWriter/TemplateProcessor/TemplateWriter.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ public class TemplateWriter : IConfigurable, IOdcmWriter
2424
private static Logger logger = LogManager.GetCurrentClassLogger();
2525

2626
private string pathWriterClassNameFormatString;
27+
28+
private string targetLanguage;
29+
private IEnumerable<string> properties;
30+
31+
public TemplateWriter()
32+
{
33+
}
34+
35+
public TemplateWriter(string targetLanguage)
36+
{
37+
this.targetLanguage = targetLanguage;
38+
}
39+
40+
public TemplateWriter(string targetLanguage, IEnumerable<string> properties)
41+
{
42+
this.targetLanguage = targetLanguage;
43+
this.properties = properties;
44+
}
45+
2746
private string PathWriterClassNameFormatString
2847
{
2948
get {
@@ -76,22 +95,10 @@ IEnumerable<TextFile> ProcessTemplates()
7695
}
7796
}
7897

79-
private string targetLanguage;
80-
81-
public TemplateWriter()
82-
{
83-
84-
}
85-
86-
public TemplateWriter(string targetLanguage)
87-
{
88-
this.targetLanguage = targetLanguage;
89-
}
90-
9198
// IConfigurationProvider
9299
public void SetConfigurationProvider(IConfigurationProvider configurationProvider)
93100
{
94-
ConfigurationService.Initialize(configurationProvider, this.targetLanguage);
101+
ConfigurationService.Initialize(configurationProvider, this.targetLanguage, this.properties);
95102
FileNameCasing nameCasing;
96103
if(!Enum.TryParse(ConfigurationService.Settings.DefaultFileCasing, out nameCasing))
97104
{

src/Typewriter/Generator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ internal static class Generator
1818
/// <param name="options">The options bag</param>
1919
static internal void GenerateFiles(string csdlContents, Options options)
2020
{
21-
var filesToWrite = MetadataToClientSource(csdlContents, options.Language);
21+
var filesToWrite = MetadataToClientSource(csdlContents, options.Language, options.Properties);
2222
FileWriter.WriteAsync(filesToWrite, options.Output);
2323
}
2424

@@ -65,13 +65,13 @@ static private string CleanMetadata(string csdlContents, Options options)
6565
/// <param name="edmxString">The EDMX file as a string.</param>
6666
/// <param name="targetLanguage">Specifies the target language. Possible values are csharp, php, etc.</param>
6767
/// <returns></returns>
68-
static private IEnumerable<TextFile> MetadataToClientSource(string edmxString, string targetLanguage)
68+
static private IEnumerable<TextFile> MetadataToClientSource(string edmxString, string targetLanguage, IEnumerable<string> properties)
6969
{
7070
if (String.IsNullOrEmpty(edmxString))
7171
throw new ArgumentNullException("edmxString", "The EDMX file string contains no content.");
7272

7373
var reader = new OdcmReader();
74-
var writer = new TemplateWriter(targetLanguage);
74+
var writer = new TemplateWriter(targetLanguage, properties);
7575
writer.SetConfigurationProvider(new ConfigurationProvider());
7676

7777
var model = reader.GenerateOdcmModel(new List<TextFile> { new TextFile("$metadata", edmxString) });

src/Typewriter/Options.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using CommandLine;
2+
using System.Collections.Generic;
23
using System.Runtime.CompilerServices;
34

45
[assembly: InternalsVisibleTo("GraphODataTemplateWriter.Test")]
@@ -60,5 +61,10 @@ public class Options
6061

6162
[Option('e', "endpointVersion", Default = "v1.0", HelpText = "The endpoint version. Expected values are 'v1.0' and 'beta'. Only applicable for GenerationMode.Metadata.")]
6263
public string EndpointVersion { get; set; }
64+
65+
[Option('p', "properties", HelpText = "A space separated list of properties in the form of 'key:value'. These properties can be accessed in the " +
66+
"templates from the TemplateWriterSettings object returned by ConfigurationService.Settings. The suggested convention for specifying a key should be " +
67+
"the targeted template language name and the property name. For example, php.namespace:Microsoft\\Graph\\Beta\\Model would be a property to be consumed in the PHP templates.")]
68+
public IEnumerable<string> Properties { get; set; }
6369
}
6470
}

0 commit comments

Comments
 (0)