Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions generator/ServiceClientGeneratorLib/GeneratorHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,22 @@ public static string SanitizeQuotes(this string s)
return s.Replace("\"", "\"\"");
}

/// <summary>
/// Sanitizes a deprecation message by removing or replacing characters that could
/// break generated code (e.g. newlines, HTML tags, quotes in [Obsolete("...")] attributes).
/// </summary>
public static string SanitizeDeprecationMessage(this string s)
{
return s
.Replace("\r", string.Empty)
.Replace("\n", string.Empty)
.Replace("\"", "'")
.Replace("<p>", string.Empty)
.Replace("</p>", string.Empty)
Comment on lines +459 to +466
.Replace("<code>", "<c>")
.Replace("</code>", "</c>");
}

public static string ToVariableName(this string s)
{
return s.Replace("-", "_");
Expand Down
2 changes: 1 addition & 1 deletion generator/ServiceClientGeneratorLib/Member.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ public string DeprecationMessage
if (message == null)
throw new Exception(string.Format("The 'message' property of the 'deprecated' trait is missing for member {0}.{1}.\nFor example: \"MemberName\":{{ ... \"deprecated\":true, \"deprecatedMessage\":\"This property is deprecated, use XXX instead.\"}}", this.OwningShape.Name, this._name));

return message ?? "";
return (message ?? "").SanitizeDeprecationMessage();
}
}

Expand Down
2 changes: 1 addition & 1 deletion generator/ServiceClientGeneratorLib/Operation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public string DeprecationMessage
if (message == null)
throw new Exception(string.Format("The 'message' property of the 'deprecated' trait is missing for operation {0}.\nFor example: \"OperationName\":{{\"name\":\"OperationName\", ... \"deprecated\":true, \"deprecatedMessage\":\"This operation is deprecated\"}}", this.name));

return message;
return message.SanitizeDeprecationMessage();
}
}

Expand Down
2 changes: 1 addition & 1 deletion generator/ServiceClientGeneratorLib/Shape.cs
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ public string DeprecationMessage
if (message == null)
throw new Exception(string.Format("The 'message' property of the 'deprecated' trait is missing for shape {0}.\nFor example: \"ShapeName\":{{ ... \"members\":{{ ... }}, \"deprecated\":true, \"deprecatedMessage\":\"This type is deprecated\"}}", this._name));

return message ?? "";
return (message ?? "").SanitizeDeprecationMessage();
}
}

Expand Down
1 change: 1 addition & 0 deletions generator/ServiceClientGeneratorLib/SimpleModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public string XmlNamespacePrefix
return string.Empty;
}
}

}

#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"version": "1.0",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this approach using a json file can be simplified by just testing that the method works as intended by testing it with a string. Is there a reason a json file was chosen like this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the only way to verify the generator works is by running against the real models (these unit tests don't run anywhere in our build system, and I think they were broken for quite some time).

So for now I'd say let's just included the generator/ServiceClientGeneratorLib/ changes in the PR. Also, I assume we'd need to do the same for V3?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did run locally by manually updating one of the service json files, i just havent updated the PR description yet. and yeah i will also need to do same for v3

"metadata": {
"apiVersion": "2020-6-25",
"serviceAbbreviation": "TestService",
"serviceFullName": "TestService",
"signatureVersion": "v4",
"serviceId": "TestService"
},
"operations": {
"DeprecatedOperation": {
"name": "DeprecatedOperation",
"input": { "shape": "DeprecatedOperationInput" },
"output": { "shape": "DeprecatedOperationOutput" },
"documentation": "a deprecated operation",
"deprecated": true,
"deprecatedMessage": "This operation is deprecated.\nUse <code>NewOperation</code> instead.\r\nSee \"docs\" for <p>details</p>."
}
},
"shapes": {
"DeprecatedOperationInput": {
"type": "structure",
"members": {
"OldField": {
"shape": "OldFieldShape",
"deprecated": true,
"deprecatedMessage": "This field is deprecated.\nUse <code>NewField</code> instead.\r\nSee \"docs\" for <p>details</p>."
}
}
},
"DeprecatedOperationOutput": {
"type": "structure",
"members": {}
},
"OldFieldShape": { "type": "string" },
"DeprecatedShape": {
"type": "structure",
"members": {},
"deprecated": true,
"deprecatedMessage": "This shape is deprecated.\nUse <code>NewShape</code> instead.\r\nSee \"docs\" for <p>details</p>."
}
}
}
70 changes: 70 additions & 0 deletions generator/ServiceClientGeneratorTests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,75 @@ public void TestExampleShapes(string shapeType, object value, string expectedTex
var printedText = sb.ToString();
Assert.Equal(expectedText, printedText);
}

#region Deprecation Message Sanitization Tests

private const string _deprecatedModelsPath = "../../Content/TestModelDeprecated.json";

// The expected sanitized message after removing \r, \n, replacing " with ',
// removing <p></p>, and replacing <code></code> with <c></c>.
private const string _expectedSanitizedMessage = "This operation is deprecated.Use <c>NewOperation</c> instead.See 'docs' for details.";
private const string _expectedSanitizedMemberMessage = "This field is deprecated.Use <c>NewField</c> instead.See 'docs' for details.";
private const string _expectedSanitizedShapeMessage = "This shape is deprecated.Use <c>NewShape</c> instead.See 'docs' for details.";

[Fact]
public void OperationDeprecationMessageIsSanitized()
{
var model = new ServiceModel(_deprecatedModelsPath, null);
var operation = model.Operations.Single(x => x.Name.Equals("DeprecatedOperation"));

Assert.True(operation.IsDeprecated);
var message = operation.DeprecationMessage;

Assert.DoesNotContain("\n", message);
Assert.DoesNotContain("\r", message);
Assert.DoesNotContain("\"", message);
Assert.DoesNotContain("<p>", message);
Assert.DoesNotContain("</p>", message);
Assert.DoesNotContain("<code>", message);
Assert.DoesNotContain("</code>", message);
Assert.Equal(_expectedSanitizedMessage, message);
}

[Fact]
public void ShapeDeprecationMessageIsSanitized()
{
var model = new ServiceModel(_deprecatedModelsPath, null);
var shape = model.Shapes.Single(x => x.Name.Equals("DeprecatedShape"));

Assert.True(shape.IsDeprecated);
var message = shape.DeprecationMessage;

Assert.DoesNotContain("\n", message);
Assert.DoesNotContain("\r", message);
Assert.DoesNotContain("\"", message);
Assert.DoesNotContain("<p>", message);
Assert.DoesNotContain("</p>", message);
Assert.DoesNotContain("<code>", message);
Assert.DoesNotContain("</code>", message);
Assert.Equal(_expectedSanitizedShapeMessage, message);
}

[Fact]
public void MemberDeprecationMessageIsSanitized()
{
var model = new ServiceModel(_deprecatedModelsPath, null);
var shape = model.Shapes.Single(x => x.Name.Equals("DeprecatedOperationInput"));
var member = shape.Members.Single(x => x.ModeledName.Equals("OldField"));

Assert.True(member.IsDeprecated);
var message = member.DeprecationMessage;

Assert.DoesNotContain("\n", message);
Assert.DoesNotContain("\r", message);
Assert.DoesNotContain("\"", message);
Assert.DoesNotContain("<p>", message);
Assert.DoesNotContain("</p>", message);
Assert.DoesNotContain("<code>", message);
Assert.DoesNotContain("</code>", message);
Assert.Equal(_expectedSanitizedMemberMessage, message);
}

#endregion
}
}
Loading