Re-implementing the HTTP Client Abstraction#280
Conversation
|
Related to #274 |
|
I haven't started review yet, but why do we want to support HTTP requests? I am hoping that you are not planning to use this with unencrypted connections. |
|
I want to be able to use this to access things like the local Metadata service, which is only available over HTTP: https://github.com/firebase/firebase-admin-node/blob/master/src/auth/credential.ts#L338 |
src/utils/api-request.ts
Outdated
| public readonly headers: any; | ||
| public readonly text: string; | ||
|
|
||
| private readonly data_: any; |
There was a problem hiding this comment.
private properties should not use trailing underscores (typescript style guide).
src/utils/api-request.ts
Outdated
| public readonly text: string; | ||
|
|
||
| private readonly data_: any; | ||
| private readonly request_: string; |
There was a problem hiding this comment.
private properties should not use trailing underscores (typescript style guide).
src/utils/api-request.ts
Outdated
| return this.data_; | ||
| } | ||
| try { | ||
| return JSON.parse(this.text); |
There was a problem hiding this comment.
Is this try/catch needed again? You already test this in the constructor. You may as well throw an error directly.
| url: string; | ||
| headers?: {[key: string]: string}; | ||
| data?: string | object | Buffer; | ||
| timeout?: number; |
There was a problem hiding this comment.
Why is there no port number (in case a non-default one is used)? It seems like this has to be provided in the URL. Maybe document that.
| readonly data: any; | ||
| } | ||
|
|
||
| class DefaultHttpResponse implements HttpResponse { |
There was a problem hiding this comment.
Javadocs for these new structures would be nice to have.
src/utils/api-request.ts
Outdated
| respStream.on('end', () => { | ||
| const responseData = Buffer.concat(responseBuffer).toString(); | ||
| response.data = responseData; | ||
| settle(resolve, reject, response); |
There was a problem hiding this comment.
Can you rename to something less abstract?
There was a problem hiding this comment.
Renamed to finalizeRequest
| } | ||
|
|
||
| function enhanceError( | ||
| error, |
There was a problem hiding this comment.
That's not possible since we add new fields to the error object in the method body (triggers TS compilation error).
| return 'access_token_' + _.random(999999999); | ||
| } | ||
|
|
||
| export function responseFrom(data: any, status: number = 200, headers: any = {}): HttpResponse { |
There was a problem hiding this comment.
Add javadoc. Also where is this used?
There was a problem hiding this comment.
Done.
Not used anywhere at the moment. But it will be used once we start mocking interactions in other unit tests. I have separate PRs coming up for that.
| }); | ||
| }); | ||
|
|
||
| it('should be fulfilled for a 2xx response with a json payload', () => { |
There was a problem hiding this comment.
This looks exactly like the test above it.
test/unit/utils/api-request.spec.ts
Outdated
| const reqData = {request: 'data'}; | ||
| const respData = {success: true}; | ||
| const scope = nock('https://' + mockHost, { | ||
| reqheaders: { |
There was a problem hiding this comment.
indent 2 spaces instead of 4
|
Made the suggested changes. Over to @bojeil-google for another look. |
bojeil-google
left a comment
There was a problem hiding this comment.
LGTM. Just have a bunch of javadoc nits to be consistent with the rest of the repo.
test/unit/utils.ts
Outdated
| /** | ||
| * Creates a mock HTTP response from the given data and parameters. | ||
| * | ||
| * @param data Data to be included in the response body. |
test/unit/utils.ts
Outdated
| * Creates a mock HTTP response from the given data and parameters. | ||
| * | ||
| * @param data Data to be included in the response body. | ||
| * @param status HTTP status code (defaults to 200). |
test/unit/utils.ts
Outdated
| * | ||
| * @param data Data to be included in the response body. | ||
| * @param status HTTP status code (defaults to 200). | ||
| * @param headers HTTP headers to be included in the ersponse. |
| this.request = `${resp.config.method} ${resp.config.url}`; | ||
| } | ||
|
|
||
| get data(): any { |
There was a problem hiding this comment.
This seems to get inherited from the parent interface. At least on VSCode, hovering over the getter shows the documentation from the interface.
| response?: LowLevelResponse; | ||
| } | ||
|
|
||
| function sendRequest(config: HttpRequestConfig): Promise<LowLevelResponse> { |
| } | ||
|
|
||
| function settle(resolve, reject, response: LowLevelResponse) { | ||
| function finalizeRequest(resolve, reject, response: LowLevelResponse) { |
There was a problem hiding this comment.
Done. I'm only adding a description to these un-exported helper functions.
| private readonly parseError: Error; | ||
| private readonly request: string; | ||
|
|
||
| constructor(resp: LowLevelResponse) { |
There was a problem hiding this comment.
To be consistent, we have been adding javadocs in other files. Let's add one here.
|
Updated the documentation as recommended. |
The existing HTTP client abstractions (
HttpRequestHandlerandSignedApiRequestHandler) have a number of caveats:application/json,text/htmlandtext/plain)I ran into pretty much all the above problems while prototyping the IAM support for token minting.
To resolve these problems I propose a new set of HTTP client abstractions (
HttpClientandAuthorizedHttpClient). My original plan was to use theaxioslibrary under the hood, but its support for handling timeouts needs a bit of work. So I came up with an implementation which borrows from bothaxiosand our already existingHttpRequestHandlerclass. The resulting implementation:HttpErrorwhich provides access to the response object. All other low-level errors result in aFirebaseAppError.HttpRequestConfigtypeThis PR implements the new abstractions and provides unit tests. I will update the rest of the codebase to use them in a series of future PRs (work already underway in a separate branch).