[csharp] Support internal access of generated code (#4560)

Allows users to specify 'nonPublicApi' additional property (and C#
client CLI switch) to reduce visibility of classes created by the
generator. This includes API and Models as well as supporting code like
ApiClient and other infrastructure.

The requirement is to support codegen generated code to be embedded
within other applications where the generated code is not intended to be
publicly consumable or publicy exposed. An example would be an SDK which
internally consumes an API via the generated code; we wouldn't want the
internal API implementation exposed as part of that SDK.

Reducing visibility of the classes effectively makes the entire
implementation internal, regardless of the public modifier on methods or
static members. To fully make all members internal it would require
explicit interface implementation, which is not ideal.

see #4401
This commit is contained in:
Jim Schubert 2017-01-15 11:19:10 -06:00 committed by wing328
parent 23c5376ed6
commit d97b1da90c
16 changed files with 43 additions and 13 deletions

View File

@ -144,4 +144,7 @@ public class CodegenConstants {
public static final String GENERATE_PROPERTY_CHANGED = "generatePropertyChanged";
public static final String GENERATE_PROPERTY_CHANGED_DESC = "Specifies that models support raising property changed events.";
public static final String NON_PUBLIC_API = "nonPublicApi";
public static final String NON_PUBLIC_API_DESC = "Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers.";
}

View File

@ -45,6 +45,9 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
protected Map<Character, String> regexModifiers;
protected final Map<String, String> frameworks;
// By default, generated code is considered public
protected boolean nonPublicApi = Boolean.FALSE;
public CSharpClientCodegen() {
super();
modelTemplateFiles.put("model.mustache", ".cs");
@ -130,6 +133,14 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
CodegenConstants.PACKAGE_DESCRIPTION_DESC,
this.generatePropertyChanged);
// NOTE: This will reduce visibility of all public members in templates. Users can use InternalsVisibleTo
// https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute(v=vs.110).aspx
// to expose to shared code if the generated code is not embedded into another project. Otherwise, users of codegen
// should rely on default public visibility.
addSwitch(CodegenConstants.NON_PUBLIC_API,
CodegenConstants.NON_PUBLIC_API_DESC,
this.nonPublicApi);
regexModifiers = new HashMap<Character, String>();
regexModifiers.put('i', "IgnoreCase");
regexModifiers.put('m', "Multiline");
@ -232,6 +243,10 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
.get(CodegenConstants.OPTIONAL_ASSEMBLY_INFO).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.NON_PUBLIC_API)) {
setNonPublicApi(Boolean.valueOf(additionalProperties.get(CodegenConstants.NON_PUBLIC_API).toString()));
}
final String testPackageName = testPackageName();
String packageFolder = sourceFolder + File.separator + packageName;
String clientPackageDir = packageFolder + File.separator + clientPackage;
@ -555,6 +570,14 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
this.generatePropertyChanged = generatePropertyChanged;
}
public boolean isNonPublicApi() {
return nonPublicApi;
}
public void setNonPublicApi(final boolean nonPublicApi) {
this.nonPublicApi = nonPublicApi;
}
@Override
public String toModelDocFilename(String name) {
return toModelFilename(name);

View File

@ -19,7 +19,7 @@ namespace {{packageName}}.Client
/// <summary>
/// API client is mainly responsible for making the HTTP call to the API backend.
/// </summary>
public partial class ApiClient
{{>visibility}} partial class ApiClient
{
private JsonSerializerSettings serializerSettings = new JsonSerializerSettings
{

View File

@ -6,7 +6,7 @@ namespace {{packageName}}.Client
/// <summary>
/// API Exception
/// </summary>
public class ApiException : Exception
{{>visibility}} class ApiException : Exception
{
/// <summary>
/// Gets or sets the error code (HTTP status code)

View File

@ -7,7 +7,7 @@ namespace {{packageName}}.Client
/// <summary>
/// API Response
/// </summary>
public class ApiResponse<T>
{{>visibility}} class ApiResponse<T>
{
/// <summary>
/// Gets or sets the status code (HTTP status code)

View File

@ -11,7 +11,7 @@ namespace {{packageName}}.Client
/// <summary>
/// Represents a set of configuration settings
/// </summary>
public class Configuration
{{>visibility}} class Configuration
{
/// <summary>
/// Initializes a new instance of the Configuration class with different settings

View File

@ -10,6 +10,6 @@ namespace {{packageName}}.Client
/// </summary>
/// <param name="methodName">Method name</param>
/// <param name="response">Response</param>
/// <returns>Exceptions</returns>
public delegate Exception ExceptionFactory(string methodName, IRestResponse response);
/// <returns>Exceptions</returns>
{{>visibility}} delegate Exception ExceptionFactory(string methodName, IRestResponse response);
}

View File

@ -11,7 +11,7 @@ namespace {{packageName}}.Client
/// <summary>
/// Represents configuration aspects required to interact with the API endpoints.
/// </summary>
public interface IApiAccessor
{{>visibility}} interface IApiAccessor
{
/// <summary>
/// Gets or sets the configuration object

View File

@ -14,7 +14,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <summary>
/// Represents a collection of functions to interact with the API endpoints
/// </summary>
public interface {{interfacePrefix}}{{classname}} : IApiAccessor
{{>visibility}} interface {{interfacePrefix}}{{classname}} : IApiAccessor
{
#region Synchronous Operations
{{#operation}}
@ -73,7 +73,7 @@ namespace {{packageName}}.{{apiPackage}}
/// <summary>
/// Represents a collection of functions to interact with the API endpoints
/// </summary>
public partial class {{classname}} : {{interfacePrefix}}{{classname}}
{{>visibility}} partial class {{classname}} : {{interfacePrefix}}{{classname}}
{
private {{packageName}}.Client.ExceptionFactory _exceptionFactory = (name, response) => null;

View File

@ -3,7 +3,7 @@
/// </summary>{{#description}}
/// <value>{{{description}}}</value>{{/description}}
[JsonConverter(typeof(StringEnumConverter))]
public enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>

View File

@ -3,7 +3,7 @@
/// </summary>{{#description}}
/// <value>{{{description}}}</value>{{/description}}
[JsonConverter(typeof(StringEnumConverter))]
public enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{{>visibility}} enum {{#datatypeWithEnum}}{{.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>

View File

@ -5,7 +5,7 @@
{{#generatePropertyChanged}}
[ImplementPropertyChanged]
{{/generatePropertyChanged}}
public partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}} IEquatable<{{classname}}>, IValidatableObject
{{>visibility}} partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}} IEquatable<{{classname}}>, IValidatableObject
{
{{#vars}}
{{#isEnum}}

View File

@ -4,7 +4,7 @@
/// </summary>{{#description}}
/// <value>{{{description}}}</value>{{/description}}
[JsonConverter(typeof(StringEnumConverter))]
public enum {{#datatypeWithEnum}}{{&.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{{>visibility}} enum {{#datatypeWithEnum}}{{&.}}{{/datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}
{
{{#allowableValues}}{{#enumVars}}
/// <summary>

View File

@ -0,0 +1 @@
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}}

View File

@ -50,6 +50,8 @@ public class CSharpClientOptionsTest extends AbstractOptionsTest {
times = 1;
clientCodegen.setGeneratePropertyChanged(true);
times = 1;
clientCodegen.setNonPublicApi(true);
times = 1;
clientCodegen.setInterfacePrefix("X");
times = 1;
}};

View File

@ -35,6 +35,7 @@ public class CSharpClientOptionsProvider implements OptionsProvider {
.put(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES, "true")
.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "true")
.put(CodegenConstants.GENERATE_PROPERTY_CHANGED, "true")
.put(CodegenConstants.NON_PUBLIC_API, "true")
.put(CodegenConstants.INTERFACE_PREFIX, "X")
.build();
}