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

[WIP] most of the grunt work to get httpclient integrated #105

Merged
merged 11 commits into from
Mar 1, 2018

Conversation

baronfel
Copy link
Contributor

This doesn't do all of what we want in #100, like hiding the http calls behind the runtime-specific client class, but it brings in the HttpClient dependency and threads through most of the logic that would be needed in the OperationCompiler anyway. Moving to the runtime-specific client class would mostly be a matter of changing the actual call to the static httpclient in operationCompiler to a call to SendAsync or the appropriate call on our client wrapper class.

I think I was having build issues locally, so part of this is just to see if the build servers are happier with this than my local machine is.

@sergey-tihon
Copy link
Member

sergey-tihon commented Feb 19, 2018

Error messages like

error FS3033: The type provider 'SwaggerProvider.SwaggerTypeProvider' reported an error: unknown constant 'fun:httpMethodForRestCall@158-1' of type '<StartupCode$SwaggerProvider-DesignTime>.$OperationCompiler+httpMethodForRestCall@158-1' in generated method. You may need to avoid variable capture in a quotation specifying a type provider.

means that we should not call methods defined in design-time assembly from provided quotation.

So, all building blocks should be moved to runtime assembly.

@baronfel would you like to move them?

@sergey-tihon
Copy link
Member

sergey-tihon commented Feb 21, 2018

[23:28:31 ERR] All/Swashbuckle.UpdateControllers.Tests/Update Array Int POST Test errored in 00:00:00.0990000
System.NotSupportedException: Specified method is not supported.
at Lambda77b383a1-35e4-47ed-84cf-eddba544373c.Invoke (Microsoft.FSharp.Core.Unit ) [0x001af] in <5a8c84ea3a16bde5a7450383ea848c5a>:0
at Microsoft.FSharp.Control.AsyncBuilderImpl+callA@522[b,a].Invoke (Microsoft.FSharp.Control.AsyncParams`1[T] args) [0x00051] in <5a7d678a904cf4daa74503838a677d5a>:0

generated code

      public FSharpAsync<int[]> PostApiUpdateArrayInt(int[] x)
      {
        FSharpFunc<FSharpAsync<object>, object> fsharpFunc = Swagger.Internal.RuntimeHelpers.get_asyncCast().Invoke(typeof (int[]));
        Lambdad92d1b9e\u002D8d77\u002D4787\u002D8a44\u002Dc9f992846b21 obj = new Lambdad92d1b9e\u002D8d77\u002D4787\u002D8a44\u002Dc9f992846b21();
        obj.@this = this;
        obj.x = x;
        FSharpAsyncBuilder defaultAsyncBuilder = ExtraTopLevelOperators.DefaultAsyncBuilder;
        FSharpAsync<object> func = ((FSharpFunc<FSharpAsyncBuilder, FSharpAsync<object>>) obj).Invoke(defaultAsyncBuilder);
        return (FSharpAsync<int[]>) fsharpFunc.Invoke(func);
      }
internal class Lambdad92d1b9e\u002D8d77\u002D4787\u002D8a44\u002Dc9f992846b21 : FSharpFunc<FSharpAsyncBuilder, FSharpAsync<object>>
{
  internal SwashbuckleReturnControllersTests.WebAPI.Client @this;
  internal int[] x;

  public override sealed FSharpAsync<object> Invoke([In] FSharpAsyncBuilder obj0)
  {
    SwashbuckleReturnControllersTests.WebAPI.Client client = this.@this;
    int[] x = this.x;
    return obj0.Delay<object>((FSharpFunc<Unit, FSharpAsync<object>>) new Lambda77b383a1\u002D35e4\u002D47ed\u002D84cf\u002Deddba544373c()
    {
      builder\u0040 = obj0,
      @this = client,
      x = x
    });
  }
}
internal class Lambda77b383a1\u002D35e4\u002D47ed\u002D84cf\u002Deddba544373c : FSharpFunc<Unit, FSharpAsync<object>>
{
  internal FSharpAsyncBuilder builder\u0040;
  internal SwashbuckleReturnControllersTests.WebAPI.Client @this;
  internal int[] x;

  public override sealed FSharpAsync<object> Invoke([In] Unit obj0)
  {
    FSharpAsyncBuilder builder = this.builder\u0040;
    SwashbuckleReturnControllersTests.WebAPI.Client client = this.@this;
    int[] x = this.x;
    FSharpAsyncBuilder fsharpAsyncBuilder = builder;
    StringContent stringContent = new StringContent(RuntimeHelpers.get_serialize().Invoke((object) x), Encoding.UTF8, "application/json");
    UriBuilder uriBuilder1 = new UriBuilder(RuntimeHelpers.combineUrl(RuntimeHelpers.combineUrl(client.Host, ""), "/api/UpdateArrayInt"));
    Lambda2ac7e0f0\u002D6564\u002D4f6f\u002Db372\u002Da37aab7be429 b372A37aab7be429 = new Lambda2ac7e0f0\u002D6564\u002D4f6f\u002Db372\u002Da37aab7be429();
    FSharpFunc<Tuple<string, string>, string> fsharpFunc1 = (FSharpFunc<Tuple<string, string>, string>) new Lambdaeb6c1cba\u002Dea2d\u002D4b3c\u002D9225\u002Daed56e6700e6();
    Lambdaf58a7048\u002D295f\u002D4823\u002Daff2\u002D5c5d47ab9752 aff25c5d47ab9752 = new Lambdaf58a7048\u002D295f\u002D4823\u002Daff2\u002D5c5d47ab9752();
    aff25c5d47ab9752.mapping = fsharpFunc1;
    FSharpList<Tuple<string, string>> empty = FSharpList<Tuple<string, string>>.get_Empty();
    IEnumerable<string> func1 = ((FSharpFunc<FSharpList<Tuple<string, string>>, IEnumerable<string>>) aff25c5d47ab9752).Invoke(empty);
    string func2 = ((FSharpFunc<IEnumerable<string>, string>) b372A37aab7be429).Invoke(func1);
    if (string.IsNullOrEmpty(uriBuilder1.Query))
    {
      UriBuilder uriBuilder2 = uriBuilder1;
      FSharpFunc<string, FSharpFunc<string, string>> fsharpFunc2 = ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<string, FSharpFunc<string, string>>>((PrintfFormat<FSharpFunc<string, FSharpFunc<string, string>>, Unit, string, string>) new PrintfFormat<FSharpFunc<string, FSharpFunc<string, string>>, Unit, string, string, Tuple<string, string>>("%s&%s"));
      Lambdac780c90b\u002D354e\u002D4a1e\u002D8826\u002D1690749a9708 obj = new Lambdac780c90b\u002D354e\u002D4a1e\u002D8826\u002D1690749a9708();
      obj.clo1 = fsharpFunc2;
      string query = uriBuilder1.Query;
      string str = ((FSharpFunc<string, FSharpFunc<string, string>>) obj).Invoke(query).Invoke(func2);
      uriBuilder2.Query = str;
    }
    else
    {
      UriBuilder uriBuilder2 = uriBuilder1;
      FSharpFunc<string, FSharpFunc<string, string>> fsharpFunc2 = ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<string, FSharpFunc<string, string>>>((PrintfFormat<FSharpFunc<string, FSharpFunc<string, string>>, Unit, string, string>) new PrintfFormat<FSharpFunc<string, FSharpFunc<string, string>>, Unit, string, string, Tuple<string, string>>("%s?%s"));
      Lambda65989771\u002D811c\u002D4bb4\u002Daf04\u002Dd24f9c59e9f0 af04D24f9c59e9f0 = new Lambda65989771\u002D811c\u002D4bb4\u002Daf04\u002Dd24f9c59e9f0();
      af04D24f9c59e9f0.clo1 = fsharpFunc2;
      string query = uriBuilder1.Query;
      string str = ((FSharpFunc<string, FSharpFunc<string, string>>) af04D24f9c59e9f0).Invoke(query).Invoke(func2);
      uriBuilder2.Query = str;
    }
    HttpRequestMessage httpRequestMessage1 = new HttpRequestMessage(new HttpMethod("Post"), uriBuilder1.Uri);
    FSharpFunc<Tuple<string, string>, bool> fsharpFunc3 = (FSharpFunc<Tuple<string, string>, bool>) new Lambda07b64d8d\u002D2f0d\u002D4faa\u002D973b\u002Daef634f7dd3b();
    Lambda4eefb031\u002D7cda\u002D4177\u002Db058\u002D268569af8b89 b058268569af8b89 = new Lambda4eefb031\u002D7cda\u002D4177\u002Db058\u002D268569af8b89();
    b058268569af8b89.predicate = fsharpFunc3;
    Tuple<string, string>[] headers = client.Headers;
    Tuple<string, string>[] tupleArray1;
    if ((!Operators.Not(((FSharpFunc<Tuple<string, string>[], bool>) b058268569af8b89).Invoke(headers)) ? 0 : 1) != 0)
      tupleArray1 = ArrayModule.Append<Tuple<string, string>>(new Tuple<string, string>[1]
      {
        new Tuple<string, string>("Content-Type", "application/json")
      }, client.Headers);
    else
      tupleArray1 = client.Headers;
    Tuple<string, string>[] tupleArray2 = tupleArray1;
    for (int index = 0; index <= (int) (ArrayModule.Length<Tuple<string, string>>(tupleArray2) - 1); ++index)
    {
      Tuple<string, string> array = LanguagePrimitives.IntrinsicFunctions.GetArray<Tuple<string, string>>(tupleArray2, index);
      string str = array.Item2;
      string name = array.Item1;
      httpRequestMessage1.Headers.Add(name, str);
    }
    HttpRequestMessage httpRequestMessage2 = httpRequestMessage1;
    httpRequestMessage2.Content = (HttpContent) stringContent;
    Lambda38469ad6\u002D2abe\u002D4744\u002Db77b\u002D39423822d2f5 b77b39423822d2f5 = new Lambda38469ad6\u002D2abe\u002D4744\u002Db77b\u002D39423822d2f5();
    b77b39423822d2f5.@this = client;
    HttpRequestMessage func3 = httpRequestMessage2;
    FSharpAsync<string> computation = RuntimeHelpers.sendMessage(((FSharpFunc<HttpRequestMessage, HttpRequestMessage>) b77b39423822d2f5).Invoke(func3));
    return fsharpAsyncBuilder.Bind<string, object>(computation, (FSharpFunc<string, FSharpAsync<object>>) new Lambda520e5ab5\u002D479e\u002D4f5a\u002D908d\u002Dc856cad41e08()
    {
      builder\u0040 = builder
    });
  }
}

@sergey-tihon
Copy link
Member

Interesting... What is exactly not supported =)

… to see if that fixes our 'op_Subtraction' issue
@baronfel
Copy link
Contributor Author

How did you dump the generated code? did you find the temporary assembly on-disk and use ildasm/dotpeek on it?

@baronfel
Copy link
Contributor Author

The errors I get now are all related to an unsupported subtraction operator call:

[09:28:27 ERR] All/Swashbuckle.ReturnControllers.Tests/Return Object Point POST Test errored in 00:00:00.0050000 <Expecto>
System.NotSupportedException: Specified method is not supported.
   at Microsoft.FSharp.Core.Operators.op_Subtraction[T1,T2,T3](T1 x, T2 y)
   at Lambdaa27c5b4a-112c-4b9b-beca-9a0989c45239.Invoke(Unit )
   at Microsoft.FSharp.Control.AsyncBuilderImpl.callA@522.Invoke(AsyncParams`1 args)

@sergey-tihon
Copy link
Member

Compiler merges temp assembly to target assembly, so these samples dotpeek'ed from SwaggerProvider.ProviderTests.dll

@baronfel
Copy link
Contributor Author

Thanks, that gives me enough information to do some digging on my own.

@sergey-tihon
Copy link
Member

sergey-tihon commented Feb 24, 2018

One more error message (this time from Windows)

[13:29:22 ERR] All/Swashbuckle.ResourceControllers.Tests/ResourceStringString Update value in the resource dictionary errored in 00:00:00.1470000
System.NotSupportedException: Specified method is not supported.
at Microsoft.FSharp.Core.Operators.op_Subtraction[T1,T2,T3](T1 x, T2 y)
at Lambda30cb63ff-f943-4fb8-8bb4-125c35c8a163.Invoke(Unit )
at Microsoft.FSharp.Control.AsyncBuilderImpl.callA@522.Invoke(AsyncParams`1 args)

and the code of Lambda30cb63ff-f943-4fb8-8bb4-125c35c8a163.Invoke(Unit ) ILSpy'ed

using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Control;
using Microsoft.FSharp.Core;
using Swagger.Internal;
using System;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;

internal class Lambda30cb63ff-f943-4fb8-8bb4-125c35c8a163 : FSharpFunc<Unit, FSharpAsync<Unit>>
{
	internal FSharpAsyncBuilder builder@;

	internal string key;

	internal SwashbuckleReturnControllersTests.WebAPI.Client @this;

	internal string value;

	public sealed override FSharpAsync<Unit> Invoke(Unit P_0)
	{
		FSharpAsyncBuilder fSharpAsyncBuilder = this.builder@;
		string text = this.key;
		SwashbuckleReturnControllersTests.WebAPI.Client client = this.@this;
		string text2 = this.value;
		FSharpAsyncBuilder fSharpAsyncBuilder2 = fSharpAsyncBuilder;
		StringContent stringContent = new StringContent(RuntimeHelpers.serialize.Invoke((object)text2), Encoding.UTF8, "application/json");
		UriBuilder uriBuilder = new UriBuilder(RuntimeHelpers.combineUrl(((ProvidedSwaggerBaseType)client).Host, Regex.Replace("/api/ResourceStringString/{key}", "{key}", ((object)text).ToString())));
		Lambdad5a343e1-86df-4083-986b-2e426dc32573 lambdad5a343e1-86df-4083-986b-2e426dc = new Lambdad5a343e1-86df-4083-986b-2e426dc32573();
		FSharpFunc<Tuple<string, string>, string> mapping = new Lambda06ae4daf-22d3-4ffa-827f-9028b83fe252();
		string text3 = lambdad5a343e1-86df-4083-986b-2e426dc.Invoke(new Lambda816af573-5b74-455c-ab88-2ecd5f855dd0
		{
			mapping = mapping
		}.Invoke(FSharpList<Tuple<string, string>>.Empty));
		if (string.IsNullOrEmpty(uriBuilder.Query))
		{
			uriBuilder.Query = text3;
		}
		else
		{
			uriBuilder.Query = string.Format("{0}&{1}", (object)uriBuilder.Query, (object)text3);
		}
		Uri uri = uriBuilder.Uri;
		HttpMethod method = new HttpMethod("PUT");
		HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, uri);
		FSharpFunc<Tuple<string, string>, bool> predicate = new Lambdaf994f006-bf1f-4214-b467-a497daba022a();
		bool flag = new Lambda8bb67850-4e78-435c-bc44-59d57d006a76
		{
			predicate = predicate
		}.Invoke(((ProvidedSwaggerBaseType)client).Headers);
		Tuple<string, string>[] array = (!Operators.Not(flag) || 1 == 0) ? ((ProvidedSwaggerBaseType)client).Headers : ArrayModule.Append(new Tuple<string, string>[1]
		{
			new Tuple<string, string>("Content-Type", "application/json")
		}, ((ProvidedSwaggerBaseType)client).Headers);
		int num = 0;
		while (true)
		{
			if (num > ArrayModule.Length(array) - 1)
			{
				break;
			}
			Tuple<string, string> array2 = LanguagePrimitives.IntrinsicFunctions.GetArray(array, num);
			string item = array2.Item2;
			string item2 = array2.Item1;
			httpRequestMessage.Headers.Add(item2, item);
			num++;
		}
		HttpRequestMessage httpRequestMessage2 = httpRequestMessage;
		httpRequestMessage2.Content = (HttpContent)stringContent;
		HttpRequestMessage message = new Lambda3a7f3438-b2c7-4c4a-b462-3d1f1425d04d
		{
			@this = client
		}.Invoke(httpRequestMessage2);
		return fSharpAsyncBuilder2.Bind(RuntimeHelpers.sendMessage(message), new Lambda9528c2a3-90fe-444c-9e82-4e1ab737d311
		{
			builder@ = fSharpAsyncBuilder
		});
	}
}

not so much options where Microsoft.FSharp.Core.Operators.op_Subtraction[T1,T2,T3](T1 x, T2 y) could be... the only place is the while loop

              while (true)
		{
			if (num > ArrayModule.Length(array) - 1)
			{

This is IL of provided code

IL_0167: stloc.s 12
IL_0169: ldc.i4.0
IL_016a: stloc.s 15
// loop start (head: IL_016c)
    IL_016c: ldloc.s 15
    IL_016e: ldloc.s 12
    IL_0170: call int32 [FSharp.Core]Microsoft.FSharp.Collections.ArrayModule::Length<class [mscorlib]System.Tuple`2<string, string>>(!!0[])
    IL_0175: ldc.i4.1
    IL_0176: call !!2 [FSharp.Core]Microsoft.FSharp.Core.Operators::op_Subtraction<int32, int32, int32>(!!0, !!1)
    IL_017b: bgt.s IL_01b2

    IL_017d: ldloc.s 12
    IL_017f: ldloc.s 15
    IL_0181: call !!0 [FSharp.Core]Microsoft.FSharp.Core.LanguagePrimitives/IntrinsicFunctions::GetArray<class [mscorlib]System.Tuple`2<string, string>>(!!0[], int32)
    IL_0186: stloc.s 16
    IL_0188: ldloc.s 16
    IL_018a: call instance !1 class [mscorlib]System.Tuple`2<string, string>::get_Item2()
    IL_018f: stloc.s 17
    IL_0191: ldloc.s 16
    IL_0193: call instance !0 class [mscorlib]System.Tuple`2<string, string>::get_Item1()
    IL_0198: stloc.s 18
    IL_019a: ldloc.s 11
    IL_019c: call instance class [System.Net.Http]System.Net.Http.Headers.HttpRequestHeaders [System.Net.Http]System.Net.Http.HttpRequestMessage::get_Headers()
    IL_01a1: ldloc.s 18
    IL_01a3: ldloc.s 17
    IL_01a5: call instance void [System.Net.Http]System.Net.Http.Headers.HttpHeaders::Add(string, string)
    IL_01aa: ldloc.s 15
    IL_01ac: ldc.i4.1
    IL_01ad: add
    IL_01ae: stloc.s 15
    IL_01b0: br.s IL_016c
// end loop

At this point, I really need some ideas or advice, because I do not see the issue ...

@dsyme please take a look if you have a moment. The compilation is successful, but in the runtime provided code fails.

The quotation is

               <@
                    ...
                    for (name, value) in %heads do msg.Headers.Add(name, value)
                    msg 
                @>

@sergey-tihon
Copy link
Member

sergey-tihon commented Feb 24, 2018

Summary

TPSDK can compile the for loop over spliced array, but generated code is incorrect

<@
       ...
       for (name, value) in %heads do msg.Headers.Add(name, value)
       msg 
@>

as workaround we could replace for loop by Seq.iter (this option works)

<@
     %heads
     |> Seq.iter (fun (name, value) ->
          msg.Headers.Add(name, value)
      )
     msg 
@>

@dsyme do you understand the reason? Could we fix it or should we update docs or improve error message? thx

@sergey-tihon sergey-tihon merged commit e6b664f into fsprojects:dev Mar 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants