Skip to content

Commit 2660b6d

Browse files
Fix Java generation - support methods that return enums, disambiguate entities that end in "Request" (#251)
* Disambiguate generated class references for type names that end in 'request' * Added test for generated file.
1 parent 958fa94 commit 2660b6d

File tree

5 files changed

+135
-3
lines changed

5 files changed

+135
-3
lines changed

Templates/Java/BaseJavaModel.template.tt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,17 +937,21 @@ import java.util.EnumSet;", host.CurrentModel.NamespaceName());
937937
import {0}.concurrency.*;
938938
import {0}.core.*;
939939
import {0}.models.extensions.*;
940-
import {0}.models.generated.*;
940+
import {0}.models.generated.*;{2}
941941
import {0}.http.*;
942942
import {0}.requests.extensions.*;
943943
import {0}.options.*;
944944
import {0}.serializer.*;
945945

946946
import java.util.Arrays;
947947
import java.util.EnumSet;";
948+
949+
// We need this for disambiguation of generated model class/interfaces references.
950+
string fullyQualifiedImport = host.GetFullyQualifiedImportStatementForModel();
951+
948952
return string.Format(format,
949953
host.CurrentModel.NamespaceName(),
950-
host.TemplateInfo.OutputParentDirectory.Replace("_", "."));
954+
host.TemplateInfo.OutputParentDirectory.Replace("_", "."), fullyQualifiedImport);
951955
}
952956

953957
/// Creates a class declaration

Templates/Java/requests_extensions/BaseMethodCollectionResponse.java.tt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import com.google.gson.annotations.*;
2828
}
2929

3030
<#=CreateRawJsonObject()#>
31-
<# if ( ! ((c as OdcmMethod).ReturnType is OdcmPrimitiveType) ) { #>
31+
<# if ( ! ((c as OdcmMethod).ReturnType is OdcmPrimitiveType) && ! ((c as OdcmMethod).ReturnType is OdcmEnum) ) { #>
3232
<#= UpdateListPropertiesWithinSetRawObject(new [] { "value" })#>
3333
<# } else { #>
3434
}

src/GraphODataTemplateWriter/Extensions/OdcmModelExtensions.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Microsoft.Graph.ODataTemplateWriter.Extensions
88
using Microsoft.Graph.ODataTemplateWriter.Settings;
99
using Vipr.Core.CodeModel;
1010
using NLog;
11+
using Microsoft.Graph.ODataTemplateWriter.TemplateProcessor;
1112

1213
public static class OdcmModelExtensions
1314
{
@@ -137,6 +138,85 @@ public static IEnumerable<OdcmMethod> GetMethods(this OdcmModel model)
137138
return model.GetEntityTypes().SelectMany(entityType => entityType.Methods);
138139
}
139140

141+
/// <summary>
142+
/// This extension method determines whether the current type needs to be disambiguated.
143+
/// If the current type needs dismabiguation, we then provide a fully qualified
144+
/// import statement for the model.
145+
/// This currently operates only on entities whose name ends in "Request". We do this
146+
/// because every entity results in a request object. For example, assume we have the following
147+
/// two entities: timeOff and timeOffRequest. The timeOff entity will trigger the generation
148+
/// of classes named model.timeOff and request.timeOffRequest. The timeOffRequest entity
149+
/// will trigger the generation of a model.timeOffRequest and request.timeOffRequestRequest.
150+
/// The request.timeOffRequest and model.timeOffRequest classes will result in a name collision in
151+
/// a few files.
152+
/// Assumptions: 1) host.CurrentType is an OdcmProperty.
153+
/// </summary>
154+
/// <param name="host">The T4Host that orchestrates applying templates to the OdcmModel.</param>
155+
/// <returns>A boolean value that indicates whether the current type needs to be disambiguated.</returns>
156+
public static bool DoesCurrentTypeNeedDisambiguation(this CustomT4Host host)
157+
{
158+
// At this point this is only applicable to OdcmProperty.
159+
// Challenging this assumption will require a lot more investigation.
160+
if (!(host.CurrentType is OdcmProperty))
161+
return false;
162+
163+
// We only support "Request" dismabiguation at this point. Check whether the
164+
// current type ends in "Request".
165+
var requestSuffix = "Request";
166+
var currentTypeName = (host.CurrentType as OdcmProperty).Type.Name;
167+
int index = currentTypeName.IndexOf(requestSuffix);
168+
if (index == -1 || !currentTypeName.EndsWith(requestSuffix))
169+
return false; // Doesn't need disambiguation
170+
171+
// If it does end in "Request", let's capture the base name to check if an entity of that name
172+
// exists in the schema.
173+
string entityNameToCheckForCollision = currentTypeName.Remove(index, requestSuffix.Length);
174+
175+
// Search across namespaces, looking only at EntityType, to determine whether this type requires
176+
// disambiguation. This needs to be supported across namespaces.
177+
var classes = host.CurrentModel.Namespaces.SelectMany(n => n.Classes);
178+
var shouldDisambiguate = classes.Where(entity => entity.Kind == OdcmClassKind.Entity
179+
&& entity.Name == entityNameToCheckForCollision).Any();
180+
181+
return shouldDisambiguate;
182+
}
183+
184+
/// <summary>
185+
/// An extension method to get an import statement for the fully qualified name of the current type.
186+
/// Assumptions: 1) host.CurrentType is an OdcmProperty. 2) the generated namespace of the current type
187+
/// is in models.generated output namespace (in the generated file, not in the metadata).
188+
/// This method should support multiple namespaces.
189+
/// This currently (6/2020) applies to the following templates:
190+
/// BaseEntityCollectionRequest.java.tt
191+
/// IBaseEntityCollectionRequest.java.tt
192+
/// IBaseEntityCollectionPage.java.tt
193+
/// This currently unintentionally applies to the following templates when the disambiguation condition is met.
194+
/// This is not an issue as there is already a wild card import for the namespace that we should address first.
195+
/// IBaseEntityCollectionRequestBuilder.java.tt
196+
/// BaseEntityCollectionRequestBuilder.java.tt
197+
/// </summary>
198+
/// <param name="host">The T4Host that orchestrates applying templates to the OdcmModel.</param>
199+
/// <returns>A string that represents the import statement of the fully qualified name of the current type.</returns>
200+
public static string GetFullyQualifiedImportStatementForModel(this CustomT4Host host)
201+
{
202+
// By default, we don't need to disambiguate the model in the generated code file.
203+
// This will be the general case.
204+
var importStatement = "";
205+
206+
// Check whether we need to disambiguate the current type for generation of the model in the code file.
207+
var shouldDisambiguate = host.DoesCurrentTypeNeedDisambiguation();
208+
209+
if (shouldDisambiguate)
210+
{
211+
// Form the import statement to disambiguate the model in the generated code file.
212+
var thisNamespace = host.CurrentModel.NamespaceName();
213+
var thisTypeName = (host.CurrentType as OdcmProperty).Type.Name.ToUpperFirstChar();
214+
importStatement = $"\nimport {thisNamespace}.models.generated.{thisTypeName};";
215+
}
216+
217+
return importStatement;
218+
}
219+
140220
/// <summary>
141221
/// Get the service collection navigation property for the given property type
142222
///

test/Typewriter.Test/Given_a_valid_metadata_file_to_Typewriter.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,41 @@ public void It_generates_PHP_models_with_a_property()
7474
Assert.IsTrue(isExpectedNamespaceSet, $"The expected namespace, {testNamespace}, was not set in the generated test file.");
7575
}
7676

77+
/// <summary>
78+
///
79+
/// </summary>
80+
[TestMethod]
81+
public void It_generates_Java_models_with_disambiguated_import()
82+
{
83+
const string outputDirectory = "outputJava";
84+
85+
Options options = new Options()
86+
{
87+
Output = outputDirectory,
88+
Language = "Java",
89+
GenerationMode = GenerationMode.Files
90+
};
91+
92+
Generator.GenerateFiles(testMetadata, options);
93+
94+
FileInfo fileInfo = new FileInfo(outputDirectory + generatedOutputUrl + @"\requests\extensions\TimeOffRequestCollectionRequest.java");
95+
Assert.IsTrue(fileInfo.Exists, $"Expected: {fileInfo.FullName}. File was not found.");
96+
97+
// Check that the namespace applied at the CLI was added to the document.
98+
IEnumerable<string> lines = File.ReadLines(fileInfo.FullName);
99+
bool isExpectedImportStatementFound = false;
100+
string expected = "import com.microsoft.graph.models.generated.TimeOffRequest;";
101+
foreach (var line in lines)
102+
{
103+
if (line.Contains(expected))
104+
{
105+
isExpectedImportStatementFound = true;
106+
break;
107+
}
108+
}
109+
Assert.IsTrue(isExpectedImportStatementFound, $"The expected statement was not found. Expected: {expected}");
110+
}
111+
77112
[TestMethod]
78113
public void It_generates_dotNet_client_with_default_beta_baseUrl()
79114
{

test/Typewriter.Test/Resources/dirtyMetadata.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
<Property Name="email" Type="Edm.String" />
5050
</ComplexType>
5151
<ComplexType Name="emptyComplexType" Abstract="true"/>
52+
53+
<EntityType Name="timeOffRequest" BaseType="microsoft.graph.entity">
54+
<Property Name="name" Type="Edm.String" />
55+
</EntityType>
56+
<EntityType Name="timeOff" BaseType="microsoft.graph.entity">
57+
<Property Name="name" Type="Edm.String" />
58+
</EntityType>
59+
<EntityType Name="schedule" BaseType="microsoft.graph.entity">
60+
<Property Name="enabled" Type="Edm.Boolean" />
61+
<NavigationProperty Name="timesOff" Type="Collection(microsoft.graph.timeOff)" ContainsTarget="true" />
62+
<NavigationProperty Name="timeOffRequests" Type="Collection(microsoft.graph.timeOffRequest)" ContainsTarget="true" />
63+
</EntityType>
64+
5265
<EntityType Name="onenotePage" HasStream="true" BaseType="microsoft.graph.entity">
5366
<Property Name="content" Type="Edm.Stream" />
5467
<NavigationProperty Name="parentNotebook" Type="microsoft.graph.notebook" ContainsTarget="true" />

0 commit comments

Comments
 (0)