Current state of API client

Jens Pelzetter 2020-07-22 21:25:21 +02:00
parent 736facc691
commit aca84af322
3 changed files with 200 additions and 20 deletions

View File

@ -1,6 +1,4 @@
import * as http from "http"; import * as http from "http";
import { rejects } from "assert";
import { timeStamp } from "console";
/** /**
* The interface of a client for the LibreCcm RESTful API. * 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 * 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 base The base URL pointing the an LibreCCM installation.
* @param endpoint The endpoint to address. * @param endpoint The endpoint to address.
@ -157,6 +155,14 @@ export function buildUrl(
return url.href; 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 * An implementation of the {@link ApiResponse} for the Fetch-API supported
* by browsers. * by browsers.
@ -524,7 +530,7 @@ export class ApiClientNodeImpl implements LibreCcmApiClient {
post( post(
endpoint: string, endpoint: string,
body: unknown, body: ArrayBuffer,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -556,31 +562,165 @@ export class ApiClientNodeImpl implements LibreCcmApiClient {
request.on("error", (error) => reject(error)); request.on("error", (error) => reject(error));
request.write(body);
request.end(); request.end();
}); });
} }
put( put(
endpoint: string, endpoint: string,
body: unknown, body: ArrayBuffer,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
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( delete(
endpoint: string, endpoint: string,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
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<ApiResponse> { head(endpoint: string): Promise<ApiResponse> {
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<Buffer> = [];
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<ApiResponse> { options(endpoint: string): Promise<ApiResponse> {
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<Buffer> = [];
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}. * and used either the {@link ApiClientFetchImpl} or the {@link ApiClientNodeImpl}.
*/ */
export class IsomorphicClientImpl implements LibreCcmApiClient { export class IsomorphicClientImpl implements LibreCcmApiClient {
readonly #fetchClient: LibreCcmApiClient;
readonly #nodeClient: LibreCcmApiClient;
constructor(fetchClient: LibreCcmApiClient, nodeClient: LibreCcmApiClient) {
this.#fetchClient = fetchClient;
this.#nodeClient = nodeClient;
}
get( get(
endpoint: string, endpoint: string,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.get(endpoint, searchParams);
} else {
return this.#nodeClient.get(endpoint, searchParams);
}
} }
post( post(
endpoint: string, endpoint: string,
body: unknown, body: ArrayBuffer,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.post(endpoint, body, searchParams);
} else {
return this.#nodeClient.post(endpoint, body, searchParams);
}
} }
put( put(
endpoint: string, endpoint: string,
body: unknown, body: ArrayBuffer,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.put(endpoint, body, searchParams);
} else {
return this.#nodeClient.put(endpoint, body, searchParams);
}
} }
delete( delete(
endpoint: string, endpoint: string,
searchParams?: Record<string, string> searchParams?: Record<string, string>
): Promise<ApiResponse> { ): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.delete(endpoint, searchParams);
} else {
return this.#nodeClient.delete(endpoint, searchParams);
}
} }
head(endpoint: string): Promise<ApiResponse> { head(endpoint: string): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.head(endpoint);
} else {
return this.#nodeClient.head(endpoint);
}
} }
options(endpoint: string): Promise<ApiResponse> { options(endpoint: string): Promise<ApiResponse> {
throw "Not implemented yet."; if (isFetchAvailable()) {
return this.#fetchClient.options(endpoint);
} else {
return this.#nodeClient.options(endpoint);
}
} }
} }

View File

@ -5,6 +5,7 @@ import {
buildUrl, buildUrl,
ApiClientFetchImpl, ApiClientFetchImpl,
ApiClientNodeImpl, ApiClientNodeImpl,
isFetchAvailable,
IsomorphicClientImpl, IsomorphicClientImpl,
} from "./ApiClient"; } from "./ApiClient";
@ -20,7 +21,7 @@ export { LibreCcmApiClient, ApiResponse, ApiError, buildUrl };
* use the credentials stored in the browser (cookies) to authenticate itself. * use the credentials stored in the browser (cookies) to authenticate itself.
*/ */
export function buildEmbeddedApiClient(): LibreCcmApiClient { export function buildEmbeddedApiClient(): LibreCcmApiClient {
if (!fetch) { if (!isFetchAvailable()) {
throw "Fetch API is not available. Please use buildIsomorpicApiClient."; throw "Fetch API is not available. Please use buildIsomorpicApiClient.";
} }
const baseUrl = new URL(document.documentURI); const baseUrl = new URL(document.documentURI);
@ -46,7 +47,7 @@ export function buildRemoteApiClient(
baseUrl: string, baseUrl: string,
jwt: string jwt: string
): LibreCcmApiClient { ): LibreCcmApiClient {
if (!fetch) { if (!isFetchAvailable()) {
throw "Fetch API is not available. Please use buildIsomorpicApiClient."; throw "Fetch API is not available. Please use buildIsomorpicApiClient.";
} }
@ -83,5 +84,7 @@ export function buildIsomorpicApiClient(
baseUrl: string, baseUrl: string,
jwt: string jwt: string
): LibreCcmApiClient { ): LibreCcmApiClient {
return new IsomorphicClientImpl(); const fetchClient: LibreCcmApiClient = buildRemoteApiClient(baseUrl, jwt);
const nodeClient: LibreCcmApiClient = buildNodeApiClient(baseUrl, jwt);
return new IsomorphicClientImpl(fetchClient, nodeClient);
} }

View File

@ -96,6 +96,11 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==" "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": { "@typescript-eslint/eslint-plugin": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz",