Azure / autorest.typescript

Extension for AutoRest (https://github.com/Azure/autorest) that generates TypeScript code. The transpiled javascript code is isomorphic. It can be run in browser and in node.js environment.
MIT License
178 stars 75 forks source link

Supporting HTTP client adapter #887

Closed ffMathy closed 3 years ago

ffMathy commented 3 years ago

Right now, the TypeScript generated code uses CoreHttp, which in turn seems to use either XmlHttpRequest or node-fetch, depending on what is available.

However, since I am using Appcelerator Titanium to build my app (which is a node-based language but still doesn't support node's http library and therefore doesn't support node-fetch either), I am out of options to make the generated code work for my app.

In swaggergen (which I would like to switch away from), it is possible to define an "adapter". A simple abstraction that (per default) wraps the CoreHttp client, but can be substituted out (dependency inversion principle, open/closed principle).

I think it would be easy to implement in Autorest, and it would allow the library to be used anywhere, while retaining backwards compatibility.

ramya-rao-a commented 3 years ago

Thanks for sharing the feedback @ffMathy @joheredi Does our current work to move to the new Azure Core help with this or this would be more solvable via the low level client project?

ffMathy commented 3 years ago

@ramya-rao-a thank you for your reply.

Not sure actually. Would it be possible to show some kind of example of how to do this with the new Azure Core?

Is there any documentation on the new way of doing things?

ramya-rao-a commented 3 years ago

@sarangan12 Can you share pointers with @ffMathy as to how to use our latest SDK code generator that is still in preview?

ramya-rao-a commented 3 years ago

@ffMathy Meanwhile you can refer to https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/core/README.md and https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/core/core-rest-pipeline/documentation/core2.md for information around the current and new Azure Core packages

ffMathy commented 3 years ago

Yeah I've actually seen that guide, but I don't think it lives up to the rest of the documentation Microsoft provides in general.

It needs more examples for different use cases etc. It's very limited, and I can't see where it says how to add Middleware.

ramya-rao-a commented 3 years ago

Fair

I was focusing on the fact that moving to the new Azure Core libraries would remove the dependency on node-fetch and did not pay attention to rest of the issues like "node-based language but still doesn't support node's http library"

In swaggergen (which I would like to switch away from), it is possible to define an "adapter". A simple abstraction that (per default) wraps the CoreHttp client, but can be substituted out (dependency inversion principle, open/closed principle).

Can you elaborate on this please?

ffMathy commented 3 years ago

Yeah sure: https://en.m.wikipedia.org/wiki/Adapter_pattern

In Swaggergen you can define an adapter to "tell" Swagger how to make requests. Essentially just an interface you can implement with a sendRequest method, which you can then override and pass an instance of that implementation in via the constructor to the client.

joheredi commented 3 years ago

@ffMathy we now allow setting your own http client in the client options when instantiating a generated client.

Here's a sample of using a custom HttpClient and also pipeline policies as Middleware, hope this is helpful.

Note: AdditionalPropertiesClient is my generated package with the latest version of @autorest/typescript@beta

import {
  createHttpHeaders,
  HttpClient,
} from "@azure/core-rest-pipeline";
import { AdditionalPropertiesClient } from ".";

async function main() {
  // Define a custom client to send the requests
  // In this example we just log the request and
  // send back a dummy response, but this could be
  // plugged into any implementation
  const customClient: HttpClient = {
    sendRequest: async request => {
      console.log(`Sending a ${request.method} request to ${request.url}`);
      return {
        headers: createHttpHeaders(),
        bodyAsText: `{"foo": "bar"}`,
        status: 200,
        request
      };
    }
  };

  // Pass the custom HttpClient to the generated client
  const client = new AdditionalPropertiesClient({
    httpClient: customClient
  });

  // You can also use pipelines as middleware
  client.pipeline.addPolicy({
    name: "customPolicy",
    sendRequest: async (request, next) => {
      // You can manipulate the request before handing it off to the pipeline and eventually
      // get sent.
      request.headers.set("test-header", "hello world!");
      // Handing it off to the pipeline to process all remaining policies and eventually
      // reach the sendRequest on the HttpClient that we defined above (or the default if no custom HttpClient is provided)
      const response = await next(request);

      // You can also manipulate the response before handing it back to the pipeline and eventually reaching your client's method
      const parsedBody = JSON.parse(response.bodyAsText ?? "{}");
      parsedBody.testValue = "Added this is a pipeline policy!";
      response.bodyAsText = JSON.stringify(parsedBody);

      // And off the response to the pipeline
      return response;
    }
  });

  const result = await client.pets.createAPInProperties({ id: 1, name: "Foo" });

  console.log(result);
  // Output:
  // Sending a PUT request to http://localhost:3000/additionalProperties/in/properties
  // { foo: 'bar' }
}

main();
ffMathy commented 3 years ago

@joheredi this is fantastic - any chance it can make it to the documentation, so future users can also understand it?

sarangan12 commented 3 years ago

https://github.com/Azure/autorest.typescript/wiki/Usage-of-Custom-HTTP-Client-&-Pipeline-Policies