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

[1.x] Allow customisation of HttpClient #194

Merged
merged 15 commits into from Jan 12, 2023
Merged

Conversation

armiol
Copy link
Contributor

@armiol armiol commented Jan 5, 2023

This changeset addresses #164, #165, and #189.

Customising HttpClient

It is now possible to customise HttpClient for each type of requests (sending commands, queries, or interacting with subscriptions):

/**
 * Custom client implementation for querying the server-side.
 * 
 * Must extend `HttpClient`.
 */
class CustomHttpClientForQuerying extends HttpClient { ... }

// ...

const queryingHttpClient = new CustomHttpClientForQuerying(endpoint);

const client = init({
      // ...
      forQueries: {
        // ...
        httpClient: queryingHttpClient  /* optional custom implementation; `HttpClient` by default. */
      },
      forSubscriptions: {
        // ...
        httpClient: /* optional custom HTTP client for subscriptions; `HttpClient` by default.  */
      },
      forCommands: {
        // ...
        httpClient: /* optional custom HTTP client for sending commands; `HttpClient` by default.  */
      }

      // ,  ....
    });

Such a custom implementation now allows to override methods, responsible for setting HTTP headers, setting the request mode, or transforming the original proto message into an HTTP body. Here is an excerpt from HttpClient showing the default implementations for each overridable method:

  /**
   * Returns the mode in which the HTTP request transferring the given message is sent.
   *
   * This implementation returns `cors`.
   *
   * @param {!TypedMessage} message a message to send, as a {@link TypedMessage}
   * @return {string} the mode of HTTP requests to use
   */
  requestMode(message) {
    return 'cors';
  }

  /**
   * Returns the string-typed map of HTTP header names to header values,
   * which to use in order to send the passed message.
   *
   * In this implementation, returns {'Content-Type': 'application/x-protobuf'}.
   *
   * @param {!TypedMessage} message a message to send, as a {@link TypedMessage}
   * @returns {{"Content-Type": string}}
   */
  headers(message) {
    return {
      'Content-Type': 'application/x-protobuf'
    };
  }

  /**
   * Transforms the given message to a string, which would become a POST request body.
   *
   * Uses {@link TypedMessage#toBase64 Base64 encoding} to transform the message.
   *
   * @param {!TypedMessage} message a message to transform into a POST body
   * @returns {!string} transformed message
   */
  toBody(message) {
    return message.toBase64();
  }

Interpreting Server Responses

Additionally, a new HttpResponseHandler routine has been extracted from the existing code. It is responsible for transforming the raw response content into JS objects. The default implementation — what we used to have in previous versions as a hard-coded behaviour — expects the server-side to return a JSON string, and parses it into a JS object.

Now, it is possible to provide a custom HttpResponseHandler, in the same manner as HttpClient:

/**
 * A custom response handler for query responses from the server-side.
 *
 * Must extend `HttpResponseHandler`.
 */
class QueryResponseHandler extends HttpResponseHandler { ... }

// ...

const client = init({
      // ... 
      forQueries: {
        // ...
        httpResponseHandler: queryResponseHandler  /* optional; `HttpResponseHandler` by default. */
      },
      forSubscriptions: {
        // ...
        httpResponseHandler: /* optional custom implementation; `HttpResponseHandler` by default. */
      },
      forCommands: {
        // ...
        httpResponseHandler: /* optional custom implementation; `HttpResponseHandler` by default. */
      }
    });

Here is a default implementation of parsing, as specified by HttpResponseHandler. This is a @protected method designed to be overridable in descendants:

  /**
   * Transforms the response into JS object by parsing the response contents.
   *
   * This implementation expects the response to contain JSON data.
   *
   * @param response an HTTP response
   * @return {Promise<Object|SpineError>} a promise of JS object,
   *                                      or a rejection with the corresponding `SpineError`
   * @protected
   */
  parse(response) {
      return response.json()
          .then(json => Promise.resolve(json))
          .catch(error =>
              Promise.reject(new SpineError('Failed to parse response JSON', error))
          );
  }

The library version is set to 1.9.0-SNAPSHOT.9.

@armiol armiol self-assigned this Jan 5, 2023
@armiol armiol changed the base branch from master to v1 January 5, 2023 16:59
@codecov
Copy link

codecov bot commented Jan 6, 2023

Codecov Report

Merging #194 (95a0105) into v1 (4532cf3) will increase coverage by 0.29%.
The diff coverage is 84.48%.

Additional details and impacted files
@@             Coverage Diff              @@
##                 v1     #194      +/-   ##
============================================
+ Coverage     62.88%   63.17%   +0.29%     
  Complexity      214      214              
============================================
  Files            95       96       +1     
  Lines          2697     2724      +27     
  Branches         46       46              
============================================
+ Hits           1696     1721      +25     
- Misses          990      992       +2     
  Partials         11       11              

@armiol armiol marked this pull request as ready for review January 12, 2023 13:45
@armiol armiol changed the title [1.x] Allow the customization of HttpClient [1.x] Allow customisation of HttpClient Jan 12, 2023
@armiol armiol merged commit dae0a71 into v1 Jan 12, 2023
@armiol armiol deleted the 1.x-customize-http-client branch January 12, 2023 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

1 participant