Skip to content
Merged
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 docs/samples/client/csharp/SampleService/main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "@azure-tools/typespec-azure-core";
"{sampleTypeSpecUrl}",
"Endpoint Service",
{
sampleTypeSpecUrl: string,
sampleTypeSpecUrl: url,
}
)
@useAuth(ApiKeyAuth<ApiKeyLocation.header, "my-api-key">)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ function fromSdkClient(
const isEndpoint = parameter.name === endpointVariableName;
const parameterType: InputType = isEndpoint
? {
kind: "url",
name: "url",
crossLanguageDefinitionId: "TypeSpec.url",
kind: parameter.type.kind === "string" ? "string" : "url",
name: "endpoint",
crossLanguageDefinitionId:
parameter.type.kind === "string" ? "TypeSpec.string" : "TypeSpec.url",
}
: fromSdkType(sdkContext, parameter.type); // TODO: consolidate with converter.fromSdkEndpointType
parameters.push({
Expand All @@ -132,6 +133,7 @@ function fromSdkClient(
parameter.clientDefaultValue,
parameterType,
),
serverUrlTemplate: type.serverUrl,
});
}
return parameters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export interface InputParameter {
arraySerializationDelimiter?: string;
headerCollectionPrefix?: string;
decorators?: DecoratorInfo[];
serverUrlTemplate?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -626,11 +626,18 @@ describe("Test Cookie Parameters", () => {
);
});
});
});

describe("Unsupported endpoint url", () => {
it("cookie parameter is not supported", async () => {
const program = await typeSpecCompile(
`
describe("Endpoint parameters", () => {
let runner: TestHost;

beforeEach(async () => {
runner = await createEmitterTestHost();
});

it("Multiple parameters are not supported", async () => {
const program = await typeSpecCompile(
`
@service(#{
title: "Azure Csharp emitter Testing",
})
Expand All @@ -645,23 +652,85 @@ describe("Test Cookie Parameters", () => {

op test() : void;
`,
runner,
{ IsNamespaceNeeded: false },
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const diagnostics = context.program.diagnostics;
createModel(sdkContext);
runner,
{ IsNamespaceNeeded: false },
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const diagnostics = context.program.diagnostics;
createModel(sdkContext);

const unsupportedCookie = diagnostics.find(
(d) => d.code === "@typespec/http-client-csharp/unsupported-endpoint-url",
);
ok(unsupportedCookie);
strictEqual(
unsupportedCookie.message,
"Unsupported server endpoint URL: https://{param1}{param2}/",
);
});
const unsupportedCookie = diagnostics.find(
(d) => d.code === "@typespec/http-client-csharp/unsupported-endpoint-url",
);
ok(unsupportedCookie);
strictEqual(
unsupportedCookie.message,
"Unsupported server endpoint URL: https://{param1}{param2}/",
);
});
it("String endpoint parameter has correct type", async () => {
const program = await typeSpecCompile(
`
@service(#{
title: "Azure Csharp emitter Testing",
})
@server(
"https://{param1}",
"Test endpoint",
{
param1: string,
})
namespace Test;

op test() : void;
`,
runner,
{ IsNamespaceNeeded: false },
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const codeModel = createModel(sdkContext);
const client = codeModel.clients[0];
ok(client);
ok(client.parameters);
const endpointParameter = client.parameters.find((p) => p.isEndpoint);
ok(endpointParameter);
strictEqual(endpointParameter.type.kind, "string");
strictEqual(endpointParameter.type.crossLanguageDefinitionId, "TypeSpec.string");
strictEqual(endpointParameter.serverUrlTemplate, "https://{param1}");
});

it("URL endpoint parameter has correct type", async () => {
const program = await typeSpecCompile(
`
@service(#{
title: "Azure Csharp emitter Testing",
})
@server(
"{param1}",
"Test endpoint",
{
param1: url,
})
namespace Test;

op test() : void;
`,
runner,
{ IsNamespaceNeeded: false },
);
const context = createEmitterContext(program);
const sdkContext = await createCSharpSdkContext(context);
const codeModel = createModel(sdkContext);
const client = codeModel.clients[0];
ok(client);
ok(client.parameters);
const endpointParameter = client.parameters.find((p) => p.isEndpoint);
ok(endpointParameter);
strictEqual(endpointParameter.type.kind, "url");
strictEqual(endpointParameter.type.crossLanguageDefinitionId, "TypeSpec.url");
strictEqual(endpointParameter.serverUrlTemplate, "{param1}");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ private IReadOnlyList<ParameterProvider> GetClientParameters()
}

private Lazy<string?> _endpointParameterName;
private InputParameter? _inputEndpointParam;
internal string? EndpointParameterName => _endpointParameterName.Value;

private string? GetEndpointParameterName()
Expand Down Expand Up @@ -482,11 +483,22 @@ private MethodBodyStatement[] BuildPrimaryConstructorBody(IReadOnlyList<Paramete
{
return [MethodBodyStatement.Empty];
}

AssignmentExpression endpointAssignment;
if (_endpointParameter.Type.Equals(typeof(string)))
{
var serverTemplate = _inputEndpointParam!.ServerUrlTemplate;
endpointAssignment = EndpointField.Assign(
New.Instance(typeof(Uri),
new FormattableStringExpression(serverTemplate!, [_endpointParameter])));
}
else
{
endpointAssignment = EndpointField.Assign(_endpointParameter);
}
List<MethodBodyStatement> body = [
ClientOptionsParameter.Assign(ClientOptionsParameter.InitializationValue!, nullCoalesce: true).Terminate(),
MethodBodyStatement.EmptyLine,
EndpointField.Assign(_endpointParameter).Terminate()
endpointAssignment.Terminate()
];

// add other parameter assignments to their corresponding fields
Expand Down Expand Up @@ -643,14 +655,28 @@ protected override ScmMethodProvider[] BuildMethods()

private ParameterProvider BuildClientEndpointParameter()
{
var endpointParam = _inputClient.Parameters.FirstOrDefault(p => p.IsEndpoint);
if (endpointParam == null)
_inputEndpointParam = _inputClient.Parameters.FirstOrDefault(p => p.IsEndpoint);
if (_inputEndpointParam == null)
{
return KnownParameters.Endpoint;
}

var endpointParamType = ScmCodeModelGenerator.Instance.TypeFactory.CreateCSharpType(_inputEndpointParam.Type);
if (endpointParamType == null)
{
return KnownParameters.Endpoint;
}

ValueExpression? initializationValue = endpointParam.DefaultValue != null
? New.Instance(KnownParameters.Endpoint.Type, Literal(endpointParam.DefaultValue.Value))
ValueExpression? initializationValue = _inputEndpointParam.DefaultValue != null
? New.Instance(endpointParamType, Literal(_inputEndpointParam.DefaultValue.Value))
: null;

if (endpointParamType.Equals(typeof(string)) && _inputEndpointParam.ServerUrlTemplate != null)
{
return new(_inputEndpointParam);
}

// Must be a URI endpoint parameter
return new(
KnownParameters.Endpoint.Name,
KnownParameters.Endpoint.Description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Microsoft.TypeSpec.Generator.Input;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Snippets;
using Microsoft.TypeSpec.Generator.Statements;
using Microsoft.TypeSpec.Generator.Tests.Common;
using NUnit.Framework;
Expand Down Expand Up @@ -960,6 +959,46 @@ public void XmlDocsAreWritten()
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}

[Test]
public void EndpointFieldAssignedFromUriParameter()
{
MockHelpers.LoadMockGenerator();
var client = InputFactory.Client(
TestClientName,
parameters: [InputFactory.Parameter(
"endpoint",
InputPrimitiveType.Url,
isRequired: true,
kind: InputParameterKind.Client,
isEndpoint: true)]);
var clientProvider = new ClientProvider(client);
var constructor = clientProvider.Constructors.FirstOrDefault(
c => c.Signature.Initializer == null && c.Signature?.Modifiers == MethodSignatureModifiers.Public);

StringAssert.Contains("_endpoint = endpoint;", constructor?.BodyStatements?.ToDisplayString());
}

[TestCase("{endpoint}", "endpoint")]
[TestCase("https://{hostName}", "hostName")]
public void EndpointFieldAssignedFromStringParameter(string serverTemplate, string parameterName)
{
MockHelpers.LoadMockGenerator();
var client = InputFactory.Client(
TestClientName,
parameters: [InputFactory.Parameter(
parameterName,
InputPrimitiveType.String,
isRequired: true,
kind: InputParameterKind.Client,
serverUrlTemplate: serverTemplate,
isEndpoint: true)]);
var clientProvider = new ClientProvider(client);
var constructor = clientProvider.Constructors.FirstOrDefault(
c => c.Signature.Initializer == null && c.Signature?.Modifiers == MethodSignatureModifiers.Public);

StringAssert.Contains($"_endpoint = new global::System.Uri($\"{serverTemplate}\");", constructor?.BodyStatements?.ToDisplayString());
}

private static InputClient GetEnumQueryParamClient()
{
return InputFactory.Client(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public InputParameter(
bool skipUrlEncoding,
bool explode,
string? arraySerializationDelimiter,
string? headerCollectionPrefix)
string? headerCollectionPrefix,
string? serverUrlTemplate)
{
Name = name;
NameInRequest = nameInRequest;
Expand All @@ -43,6 +44,7 @@ public InputParameter(
Explode = explode;
ArraySerializationDelimiter = arraySerializationDelimiter;
HeaderCollectionPrefix = headerCollectionPrefix;
ServerUrlTemplate = serverUrlTemplate;
}

public string Name { get; }
Expand All @@ -62,6 +64,7 @@ public InputParameter(
public string? ArraySerializationDelimiter { get; }
public string? HeaderCollectionPrefix { get; }
public IReadOnlyList<InputDecoratorInfo> Decorators { get; internal set; } = new List<InputDecoratorInfo>();
public string? ServerUrlTemplate { get; }

/// <summary>
/// Update the instance with given parameters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static InputParameter CreateInputParameter(ref Utf8JsonReader reader, str
bool explode = false;
string? arraySerializationDelimiter = null;
string? headerCollectionPrefix = null;
string? serverUrlTemplate = null;
IReadOnlyList<InputDecoratorInfo>? decorators = null;
while (reader.TokenType != JsonTokenType.EndObject)
{
Expand All @@ -64,6 +65,7 @@ public static InputParameter CreateInputParameter(ref Utf8JsonReader reader, str
|| reader.TryReadBoolean("explode", ref explode)
|| reader.TryReadString("arraySerializationDelimiter", ref arraySerializationDelimiter)
|| reader.TryReadString("headerCollectionPrefix", ref headerCollectionPrefix)
|| reader.TryReadString("serverUrlTemplate", ref serverUrlTemplate)
|| reader.TryReadComplexType("decorators", options, ref decorators);

if (!isKnownProperty)
Expand Down Expand Up @@ -109,7 +111,8 @@ public static InputParameter CreateInputParameter(ref Utf8JsonReader reader, str
skipUrlEncoding: skipUrlEncoding,
explode: explode,
arraySerializationDelimiter: arraySerializationDelimiter,
headerCollectionPrefix: headerCollectionPrefix)
headerCollectionPrefix: headerCollectionPrefix,
serverUrlTemplate: serverUrlTemplate)
{
Decorators = decorators ?? []
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public static InputParameter Parameter(
bool isContentType = false,
bool isApiVersion = false,
bool explode = false,
string? delimiter = null)
string? delimiter = null,
string? serverUrlTemplate = null)
{
return new InputParameter(
name,
Expand All @@ -96,7 +97,8 @@ public static InputParameter Parameter(
false,
explode,
delimiter,
null);
null,
serverUrlTemplate);
}

public static InputNamespace Namespace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6807,7 +6807,7 @@
"type": {
"$id": "660",
"kind": "url",
"name": "url",
"name": "endpoint",
"crossLanguageDefinitionId": "TypeSpec.url"
},
"location": "Uri",
Expand All @@ -6817,7 +6817,8 @@
"isEndpoint": true,
"skipUrlEncoding": false,
"explode": false,
"kind": "Client"
"kind": "Client",
"serverUrlTemplate": "{sampleTypeSpecUrl}"
}
],
"decorators": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
"type": {
"$id": "21",
"kind": "url",
"name": "url",
"name": "endpoint",
"crossLanguageDefinitionId": "TypeSpec.url"
},
"location": "Uri",
Expand All @@ -218,7 +218,8 @@
"crossLanguageDefinitionId": "TypeSpec.string"
},
"value": "http://localhost:3000"
}
},
"serverUrlTemplate": "{endpoint}"
}
],
"decorators": [],
Expand Down
Loading
Loading