Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Organizations Api Uptake for twilio-csharp and support for bearer token/ no token authentication #595

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion scripts/build_twilio_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
'php': 'Rest'
}
generateForLanguages = {
'twilio_iam_organizations.json' : ['java']
'twilio_iam_organizations.json' : ['java', 'csharp']
}
CLEANUP_IMPORT_LANGUAGES = ['java', 'php']
REMOVE_DUPLICATE_IMPORT_LANGUAGES = ['node']
Expand Down
2 changes: 1 addition & 1 deletion scripts/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function generate() {
rm -rf tmp
mkdir -p tmp
for api_spec in $files_regex; do
if [ "$1" != "twilio-java" ] && [[ $api_spec == "examples/spec/twilio_iam_organizations.yaml" ]]; then
if [ "$1" != "twilio-java" && "$1" != "twilio-csharp"] && [[ $api_spec == "examples/spec/twilio_iam_organizations.yaml" ]]; then
continue
fi
echo "generatorName: $1
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/twilio/oai/TwilioCsharpGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenSecurity;
import org.openapitools.codegen.languages.CSharpClientCodegen;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
Expand All @@ -42,6 +43,11 @@
public class TwilioCsharpGenerator extends CSharpClientCodegen {

private final TwilioCodegenAdapter twilioCodegen;
private final String BEARER_TOKEN_PREFIX = "BearerToken";
private final String NO_AUTH_PREFIX = "NoAuth";
private final String EMPTY_STRING = "";
private final String ORGS_TOKEN_CLIENT = "TwilioOrgsTokenAuthClient";
private final String BASIC_CLIENT = "TwilioClient";
private final DirectoryStructureService directoryStructureService = new DirectoryStructureService(
additionalProperties,
new ResourceMap(new Inflector()),
Expand Down Expand Up @@ -125,10 +131,57 @@ public OperationsMap postProcessOperationsWithModels(final OperationsMap objs, L
final OperationsMap results = super.postProcessOperationsWithModels(objs, allModels);
final List<CodegenOperation> opList = directoryStructureService.processOperations(results);
CsharpApiResources apiResources = processCodegenOperations(opList);
apiResources.setAuthMethod(processAuthMethods(opList));
apiResources.setResourceSetPrefix(setResourceSetPrefix(apiResources.getAuthMethod()));
apiResources.setDomainClass(fetchDomainClass(apiResources.getAuthMethod()));
apiResources.setRestClientMethodName(fetchRestClientClassName(apiResources.getAuthMethod()));
apiResources.setClientName(fetchClientName(apiResources.getAuthMethod()));
apiResources.setRequestName(fetchRequestName(apiResources.getAuthMethod()));
results.put("resources", apiResources);
return results;
}

private String fetchDomainClass(String authMethod) {
if(authMethod == BEARER_TOKEN_PREFIX || authMethod == NO_AUTH_PREFIX) return ORGS_TOKEN_CLIENT;
return BASIC_CLIENT;
}

private String setResourceSetPrefix(String authMethod){
return authMethod == BEARER_TOKEN_PREFIX ? "Token" : EMPTY_STRING;
}

private String fetchClientName(String authMethod){
if(authMethod == BEARER_TOKEN_PREFIX) return "TwilioOrgsTokenRestClient";
if(authMethod == NO_AUTH_PREFIX) return "TwilioNoAuthRestClient";
return "ITwilioRestClient";
}

private String fetchRequestName(String authMethod){
if(authMethod == BEARER_TOKEN_PREFIX) return "Token";
if(authMethod == NO_AUTH_PREFIX) return "NoAuth";
return EMPTY_STRING;
}

private String fetchRestClientClassName(String authMethod){
return authMethod == NO_AUTH_PREFIX ? "GetNoAuthRestClient" : "GetRestClient";
}


private String processAuthMethods(List<CodegenOperation> opList) {
if(opList != null){
List<CodegenSecurity> authMethods = opList.get(0).authMethods;
if(authMethods != null){
for(CodegenSecurity c : authMethods){
if(c.isOAuth == true){
return "BearerToken";
}
}
}
else return "NoAuth";
}
return "";
}

@Override
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
ImmutableMap.Builder<String, Mustache.Lambda> lambdaBuilder = super.addMustacheLambdas();
Expand Down
32 changes: 30 additions & 2 deletions src/main/java/com/twilio/oai/api/CsharpApiResourceBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenSecurity;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -34,9 +35,12 @@

public class CsharpApiResourceBuilder extends ApiResourceBuilder {

public String authMethod = "";

public CsharpApiResourceBuilder(IApiActionTemplate template, List<CodegenOperation> codegenOperations,
List<CodegenModel> allModels) {
super(template, codegenOperations, allModels);
processAuthMethods(codegenOperations);
}

public IApiResourceBuilder updateTemplate() {
Expand All @@ -62,6 +66,13 @@ public IApiResourceBuilder setImports(DirectoryStructureService directoryStructu
metaAPIProperties.put("array-exists-options", true);
}
});

if(this.authMethod == EnumConstants.AuthType.BEARER_TOKEN.getValue()){
metaAPIProperties.put("auth_method-bearer-token", true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Authmethod can be basic and bearer token. We need to handle that as well.

}
else if(this.authMethod == EnumConstants.AuthType.NOAUTH.getValue()){
metaAPIProperties.put("auth_method-no-auth", true);
}
if (OperationStore.getInstance().isEnumPresentInOptions())
metaAPIProperties.put("enum-exists-options", true);

Expand All @@ -78,14 +89,31 @@ public CsharpApiResources build() {
@Override
public ApiResourceBuilder updateOperations(Resolver<CodegenParameter> codegenParameterIResolver) { // CsharpParameterResolver
super.updateOperations(codegenParameterIResolver);
processAuthMethods(this.codegenOperationList);
this.codegenOperationList.forEach(co -> {
co.headerParams.forEach(e -> codegenParameterIResolver.resolve(e, this));
populateRequestBodyArgument(co);
resolveIngressModel(co);
});

return this;
}

public void processAuthMethods(List<CodegenOperation> opList) {
if(opList != null){
List<CodegenSecurity> authMethods = opList.get(0).authMethods;
Copy link
Contributor

Choose a reason for hiding this comment

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

We have to decide rest client on operation not on group of operations.

if(authMethods != null){
for(CodegenSecurity c : authMethods){
if(c.isOAuth == true){
this.authMethod = EnumConstants.AuthType.BEARER_TOKEN.getValue();
}
}
}
else{
this.authMethod = EnumConstants.AuthType.NOAUTH.getValue();
}
}
}

@Override
public void updateHttpMethod(CodegenOperation co) {
switch (co.httpMethod) {
Expand Down Expand Up @@ -175,7 +203,7 @@ public ApiResourceBuilder updateResponseModel(Resolver<CodegenProperty> codegenP
modelName = response.baseType;
}
Optional<CodegenModel> responseModel = Utility.getModel(allModels, modelName, recordKey, codegenOperation);
if (responseModel.isEmpty()) {
if ((responseModel == null) || responseModel.isEmpty() || (Integer.parseInt(response.code) >= 400)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed before that, all twilio APIs uses common exception models and Orgs API follows the same exception model.
Exception Model: https://www.twilio.com/docs/usage/twilios-response#response-formats-exceptions
(Athira has tested that this exception model is used by Orgs API as well)
So we can ignore all error models from generating.

Copy link
Contributor

Choose a reason for hiding this comment

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

I will think about it and we can re-visit it later.

return;
}
codegenModelResolver.resolve(responseModel.get(), this);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/twilio/oai/api/CsharpApiResources.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

import com.twilio.oai.common.ApplicationConstants;
import com.twilio.oai.resolver.csharp.OperationStore;
import lombok.Getter;
import lombok.Setter;
import org.openapitools.codegen.IJsonSchemaValidationProperties;

import java.util.ArrayList;
import java.util.List;

public class CsharpApiResources extends ApiResources {
@Getter @Setter private String authMethod;
@Getter @Setter private String resourceSetPrefix;
@Getter @Setter private String domainClass;
@Getter @Setter private String restClientMethodName;
@Getter @Setter private String clientName;
@Getter @Setter private String requestName;
List<IJsonSchemaValidationProperties> enums = new ArrayList<>(OperationStore.getInstance().getEnums().values());

public String resourceConstant = ApplicationConstants.RESOURCE;
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/twilio/oai/common/EnumConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public enum PathType {
private final String value;
}

@Getter
@RequiredArgsConstructor
public enum AuthType {
BEARER_TOKEN("BearerToken"),
NOAUTH("NoAuth");

private final String value;
}

@Getter
@RequiredArgsConstructor
public enum CsharpDataTypes implements LanguageDataType {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/twilio/oai/common/Utility.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public Optional<CodegenModel> getModel(final List<CodegenModel> models,
.map(CodegenProperty::getComplexType)
.map(classname -> getModelByClassname(models, classname))
.findFirst()
.orElseThrow();
.orElse(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why we are changing to orElse(null) ?

}

return getModelByClassname(models, className);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ private void resolveEnum(CodegenProperty property) {
}
String className = OperationStore.getInstance().getClassName();
property.isEnum = true;
property.enumName = property.complexType.contains("Enum") || property.complexType.contains("enum")
? Utility.removeEnumName(property.complexType) + ApplicationConstants.ENUM
if(property.complexType != null){
property.enumName = property.complexType.contains("Enum") || property.complexType.contains("enum")
? Utility.removeEnumName(property.complexType) + ApplicationConstants.ENUM
Copy link
Contributor

@sbansla sbansla Sep 16, 2024

Choose a reason for hiding this comment

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

Check if it is not breaking existing code.
Also test if an Enum defined as referenced and is used at multiple places as a property or property and parameter.
For Example:
Enum is defined under component.schema
component.schema.UpdateStatusEnum

And above enum is referenced at 2 or more places.

: Utility.removeEnumName(property.complexType);
}

// In case enum is an array
if (property.items != null) {
property.items.enumName = property.enumName;
Expand Down
14 changes: 7 additions & 7 deletions src/main/resources/twilio-csharp/Pagination.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
/// <param name="targetUrl"> API-generated URL for the requested results page </param>
/// <param name="client"> Client to make requests to Twilio </param>
/// <returns> The target page of records </returns>
public static Page<{{apiName}}{{resourceConstant}}> GetPage(string targetUrl, ITwilioRestClient client)
public static Page<{{apiName}}{{resourceConstant}}> GetPage(string targetUrl, {{clientName}} client)
{
client = client ?? TwilioClient.GetRestClient();
client = client ?? {{domainClass}}.{{restClientMethodName}}();

var request = new Request(
var request = new {{requestName}}Request(
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to have different request model ?
What if we use AuthStrategy class as discussed.
AuthStrategy can have child classes which will handle different types of Authentication mechanism
For Example
BasicAuthStrategy, TokenAuthStrategy, NoAuthStrategy.

HttpMethod.Get,
targetUrl
);
Expand All @@ -20,9 +20,9 @@
/// <param name="page"> current page of records </param>
/// <param name="client"> Client to make requests to Twilio </param>
/// <returns> The next page of records </returns>
public static Page<{{apiName}}{{resourceConstant}}> NextPage(Page<{{apiName}}{{resourceConstant}}> page, ITwilioRestClient client)
public static Page<{{apiName}}{{resourceConstant}}> NextPage(Page<{{apiName}}{{resourceConstant}}> page, {{clientName}} client)
{
var request = new Request(
var request = new {{requestName}}Request(
HttpMethod.Get,
page.GetNextPageUrl(Rest.Domain.Api)
);
Expand All @@ -35,9 +35,9 @@
/// <param name="page"> current page of records </param>
/// <param name="client"> Client to make requests to Twilio </param>
/// <returns> The previous page of records </returns>
public static Page<{{apiName}}{{resourceConstant}}> PreviousPage(Page<{{apiName}}{{resourceConstant}}> page, ITwilioRestClient client)
public static Page<{{apiName}}{{resourceConstant}}> PreviousPage(Page<{{apiName}}{{resourceConstant}}> page, {{clientName}} client)
{
var request = new Request(
var request = new {{requestName}}Request(
HttpMethod.Get,
page.GetPreviousPageUrl(Rest.Domain.Api)
);
Expand Down
7 changes: 6 additions & 1 deletion src/main/resources/twilio-csharp/ResourceUsings.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ using Twilio.Constant;
using Twilio.Converters;
using Twilio.Exceptions;
using Twilio.Http;
{{#metaProperties}}{{#enum-exists-resource}}using Twilio.Types;{{/enum-exists-resource}}{{/metaProperties}}
{{#metaProperties}}{{#enum-exists-resource}}using Twilio.Types;{{/enum-exists-resource}}{{/metaProperties}}
{{#metaProperties}}{{#auth_method-bearer-token}}using Twilio.Base.BearerToken;
using Twilio.Clients.BearerToken;
using Twilio.Http.BearerToken;{{/auth_method-bearer-token}}{{/metaProperties}}
{{#metaProperties}}{{#auth_method-no-auth}}using Twilio.Clients.NoAuth;
using Twilio.Http.NoAuth;{{/auth_method-no-auth}}{{/metaProperties}}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

private static Request BuildCreateRequest(Create{{apiName}}Options options, ITwilioRestClient client)
private static {{requestName}}Request BuildCreateRequest(Create{{apiName}}Options options, {{clientName}} client)
{
{{>resource/GeneratePath}}
return new Request(
return new {{requestName}}Request(
{{vendorExtensions.x-http-method}},
Rest.Domain.{{domainPackage}},
path,
Expand All @@ -21,9 +21,9 @@
/// <param name="options"> Create {{apiName}} parameters </param>
/// <param name="client"> Client to make requests to Twilio </param>
{{>resource/ReturnComments}}
public static {{apiName}}{{resourceConstant}} Create(Create{{apiName}}Options options, ITwilioRestClient client = null)
public static {{apiName}}{{resourceConstant}} Create(Create{{apiName}}Options options, {{clientName}} client = null)
{
client = client ?? TwilioClient.GetRestClient();
client = client ?? {{domainClass}}.{{restClientMethodName}}();
var response = client.Request(BuildCreateRequest(options, client));
return FromJson(response.Content);
}
Expand All @@ -33,10 +33,9 @@
/// <param name="options"> Create {{apiName}} parameters </param>
/// <param name="client"> Client to make requests to Twilio </param>
{{>resource/ReturnCommentsAsync}}
public static async System.Threading.Tasks.Task<{{apiName}}{{resourceConstant}}> CreateAsync(Create{{apiName}}Options options,
ITwilioRestClient client = null)
public static async System.Threading.Tasks.Task<{{apiName}}{{resourceConstant}}> CreateAsync(Create{{apiName}}Options options, {{clientName}} client = null)
{
client = client ?? TwilioClient.GetRestClient();
client = client ?? {{domainClass}}.{{restClientMethodName}}();
var response = await client.RequestAsync(BuildCreateRequest(options, client));
return FromJson(response.Content);
}
Expand All @@ -46,7 +45,7 @@
{{>resource/ReturnComments}}
public static {{apiName}}{{resourceConstant}} Create({{#vendorExtensions.x-request-body-param}}
{{{dataType}}} {{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^required}} = null{{/required}},{{/vendorExtensions.x-request-body-param}}
ITwilioRestClient client = null)
{{clientName}} client = null)
{
var options = new Create{{apiName}}Options({{#requiredParams}}{{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^-last}}, {{/-last}}{{/requiredParams}}){ {{#vendorExtensions.x-request-body-param}}{{^required}} {{paramName}} = {{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^-last}},{{/-last}}{{/required}}{{/vendorExtensions.x-request-body-param}} };
return Create(options, client);
Expand All @@ -57,7 +56,7 @@
{{>resource/ReturnCommentsAsync}}
public static async System.Threading.Tasks.Task<{{apiName}}{{resourceConstant}}> CreateAsync({{#vendorExtensions.x-request-body-param}}
{{{dataType}}} {{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^required}} = null{{/required}},{{/vendorExtensions.x-request-body-param}}
ITwilioRestClient client = null)
{{clientName}} client = null)
{
var options = new Create{{apiName}}Options({{#requiredParams}}{{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^-last}}, {{/-last}}{{/requiredParams}}){ {{#vendorExtensions.x-request-body-param}}{{^required}} {{paramName}} = {{#lambda.camelcase}}{{paramName}}{{/lambda.camelcase}}{{^-last}},{{/-last}}{{/required}}{{/vendorExtensions.x-request-body-param}} };
return await CreateAsync(options, client);
Expand Down
Loading
Loading