From 901375c4cccce6e00352f0316178b97819177660 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Wed, 22 Jul 2020 21:25:21 +0200 Subject: [PATCH] Current state of API client Former-commit-id: aca84af322efd24ed991bdcce2e48ba5d74c455f --- .../src/main/typescript/ApiClient.ts | 206 ++++++++++++++++-- .../main/typescript/ccm-apiclient-commons.ts | 9 +- ccm-core-apiclient/package-lock.json | 5 + 3 files changed, 200 insertions(+), 20 deletions(-) diff --git a/ccm-apiclient-commons/src/main/typescript/ApiClient.ts b/ccm-apiclient-commons/src/main/typescript/ApiClient.ts index 04da3e9e4..57212620e 100644 --- a/ccm-apiclient-commons/src/main/typescript/ApiClient.ts +++ b/ccm-apiclient-commons/src/main/typescript/ApiClient.ts @@ -1,6 +1,4 @@ import * as http from "http"; -import { rejects } from "assert"; -import { timeStamp } from "console"; /** * The interface of a client for the LibreCcm RESTful API. @@ -133,7 +131,7 @@ export interface ApiError { /** * A helper function for building an URL. Because it might be useful for implementing - * client for specific APIs in the exported from this module. + * clients for specific APIs in the exported from this module. * * @param base The base URL pointing the an LibreCCM installation. * @param endpoint The endpoint to address. @@ -157,6 +155,14 @@ export function buildUrl( return url.href; } +export function isFetchAvailable(): boolean { + if (!fetch) { + return false; + } else { + return true; + } +} + /** * An implementation of the {@link ApiResponse} for the Fetch-API supported * by browsers. @@ -524,7 +530,7 @@ export class ApiClientNodeImpl implements LibreCcmApiClient { post( endpoint: string, - body: unknown, + body: ArrayBuffer, searchParams?: Record ): Promise { return new Promise((resolve, reject) => { @@ -556,31 +562,165 @@ export class ApiClientNodeImpl implements LibreCcmApiClient { request.on("error", (error) => reject(error)); + request.write(body); + request.end(); }); } put( endpoint: string, - body: unknown, + body: ArrayBuffer, searchParams?: Record ): Promise { - throw "Not implemented yet."; + return new Promise((resolve, reject) => { + const url = buildUrl(this.#baseUrl, endpoint, searchParams); + const request: http.ClientRequest = http.request(url, { + headers: { + Authorization: this.#jwt, + }, + method: "PUT", + }); + + request.on("response", (response) => { + const status: number = response.statusCode + ? response.statusCode + : -1; + const statusText: string = response.statusMessage + ? response.statusMessage + : ""; + + response.on("end", () => { + const apiResponse: ApiResponse = new NodeResponse( + status, + statusText, + null + ); + resolve(apiResponse); + }); + }); + + request.on("error", (error) => reject(error)); + + request.write(body); + + request.end(); + }); } delete( endpoint: string, searchParams?: Record ): Promise { - throw "Not implemented yet."; + return new Promise((resolve, reject) => { + const url = buildUrl(this.#baseUrl, endpoint, searchParams); + const request: http.ClientRequest = http.request(url, { + headers: { + Authorization: this.#jwt, + }, + method: "DELETE", + }); + + request.on("response", (response) => { + const status: number = response.statusCode + ? response.statusCode + : -1; + const statusText: string = response.statusMessage + ? response.statusMessage + : ""; + + response.on("end", () => { + const apiResponse: ApiResponse = new NodeResponse( + status, + statusText, + null + ); + resolve(apiResponse); + }); + }); + + request.on("error", (error) => reject(error)); + + request.end(); + }); } head(endpoint: string): Promise { - throw "Not implemented yet."; + return new Promise((resolve, reject) => { + const url = buildUrl(this.#baseUrl, endpoint); + const request: http.ClientRequest = http.request(url, { + headers: { + Authorization: this.#jwt, + }, + method: "HEAD", + }); + + request.on("response", (response) => { + const status: number = response.statusCode + ? response.statusCode + : -1; + const statusText: string = response.statusMessage + ? response.statusMessage + : ""; + + const data: Array = []; + + response.on("data", (chunk: Buffer) => { + data.push(chunk); + }); + response.on("end", () => { + const buffer: Buffer = Buffer.concat(data); + const apiResponse: ApiResponse = new NodeResponse( + status, + statusText, + buffer + ); + resolve(apiResponse); + }); + }); + request.on("error", (error) => reject(error)); + + request.end(); + }); } options(endpoint: string): Promise { - throw "Not implemented yet."; + return new Promise((resolve, reject) => { + const url = buildUrl(this.#baseUrl, endpoint); + const request: http.ClientRequest = http.request(url, { + headers: { + Authorization: this.#jwt, + }, + method: "OPTIONS", + }); + + request.on("response", (response) => { + const status: number = response.statusCode + ? response.statusCode + : -1; + const statusText: string = response.statusMessage + ? response.statusMessage + : ""; + + const data: Array = []; + + response.on("data", (chunk: Buffer) => { + data.push(chunk); + }); + response.on("end", () => { + const buffer: Buffer = Buffer.concat(data); + const apiResponse: ApiResponse = new NodeResponse( + status, + statusText, + buffer + ); + resolve(apiResponse); + }); + }); + request.on("error", (error) => reject(error)); + + request.end(); + }); } } @@ -590,41 +730,73 @@ export class ApiClientNodeImpl implements LibreCcmApiClient { * and used either the {@link ApiClientFetchImpl} or the {@link ApiClientNodeImpl}. */ export class IsomorphicClientImpl implements LibreCcmApiClient { + readonly #fetchClient: LibreCcmApiClient; + readonly #nodeClient: LibreCcmApiClient; + + constructor(fetchClient: LibreCcmApiClient, nodeClient: LibreCcmApiClient) { + this.#fetchClient = fetchClient; + this.#nodeClient = nodeClient; + } + get( endpoint: string, searchParams?: Record ): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.get(endpoint, searchParams); + } else { + return this.#nodeClient.get(endpoint, searchParams); + } } post( endpoint: string, - body: unknown, + body: ArrayBuffer, searchParams?: Record ): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.post(endpoint, body, searchParams); + } else { + return this.#nodeClient.post(endpoint, body, searchParams); + } } put( endpoint: string, - body: unknown, + body: ArrayBuffer, searchParams?: Record ): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.put(endpoint, body, searchParams); + } else { + return this.#nodeClient.put(endpoint, body, searchParams); + } } delete( endpoint: string, searchParams?: Record ): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.delete(endpoint, searchParams); + } else { + return this.#nodeClient.delete(endpoint, searchParams); + } } head(endpoint: string): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.head(endpoint); + } else { + return this.#nodeClient.head(endpoint); + } } options(endpoint: string): Promise { - throw "Not implemented yet."; + if (isFetchAvailable()) { + return this.#fetchClient.options(endpoint); + } else { + return this.#nodeClient.options(endpoint); + } } } diff --git a/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts b/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts index 97576c706..9344fd886 100644 --- a/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts +++ b/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts @@ -5,6 +5,7 @@ import { buildUrl, ApiClientFetchImpl, ApiClientNodeImpl, + isFetchAvailable, IsomorphicClientImpl, } from "./ApiClient"; @@ -20,7 +21,7 @@ export { LibreCcmApiClient, ApiResponse, ApiError, buildUrl }; * use the credentials stored in the browser (cookies) to authenticate itself. */ export function buildEmbeddedApiClient(): LibreCcmApiClient { - if (!fetch) { + if (!isFetchAvailable()) { throw "Fetch API is not available. Please use buildIsomorpicApiClient."; } const baseUrl = new URL(document.documentURI); @@ -46,7 +47,7 @@ export function buildRemoteApiClient( baseUrl: string, jwt: string ): LibreCcmApiClient { - if (!fetch) { + if (!isFetchAvailable()) { throw "Fetch API is not available. Please use buildIsomorpicApiClient."; } @@ -83,5 +84,7 @@ export function buildIsomorpicApiClient( baseUrl: string, jwt: string ): LibreCcmApiClient { - return new IsomorphicClientImpl(); + const fetchClient: LibreCcmApiClient = buildRemoteApiClient(baseUrl, jwt); + const nodeClient: LibreCcmApiClient = buildNodeApiClient(baseUrl, jwt); + return new IsomorphicClientImpl(fetchClient, nodeClient); } diff --git a/ccm-core-apiclient/package-lock.json b/ccm-core-apiclient/package-lock.json index b66bc4a7c..cd170d691 100644 --- a/ccm-core-apiclient/package-lock.json +++ b/ccm-core-apiclient/package-lock.json @@ -96,6 +96,11 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==" }, + "@types/node": { + "version": "12.12.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz", + "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==" + }, "@typescript-eslint/eslint-plugin": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz",