From cdde8ce67afc933d6dc6742ce2ac266a011e24b0 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Sat, 11 Jul 2020 18:14:09 +0200 Subject: [PATCH] Current status of ccm-apiclient-commons. Trying to figure best way for zero dependencies and supporting browsers and node environments --- ccm-apiclient-commons/package-lock.json | 6 + ccm-apiclient-commons/package.json | 1 + .../main/typescript/ccm-apiclient-commons.ts | 155 +++++++++++++++++- 3 files changed, 161 insertions(+), 1 deletion(-) diff --git a/ccm-apiclient-commons/package-lock.json b/ccm-apiclient-commons/package-lock.json index 9a8d26a83..7d37cc2af 100644 --- a/ccm-apiclient-commons/package-lock.json +++ b/ccm-apiclient-commons/package-lock.json @@ -61,6 +61,12 @@ "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", "dev": true }, + "@types/node": { + "version": "12.12.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz", + "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz", diff --git a/ccm-apiclient-commons/package.json b/ccm-apiclient-commons/package.json index 695283364..9c443919a 100644 --- a/ccm-apiclient-commons/package.json +++ b/ccm-apiclient-commons/package.json @@ -17,6 +17,7 @@ }, "dependencies": {}, "devDependencies": { + "@types/node": "^12.12.50", "@typescript-eslint/eslint-plugin": "^3.6.0", "@typescript-eslint/parser": "^3.6.0", "eslint": "^7.4.0", 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 7812e8d45..aaa04415d 100644 --- a/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts +++ b/ccm-apiclient-commons/src/main/typescript/ccm-apiclient-commons.ts @@ -1,2 +1,155 @@ +import http from "http"; +import { AnyARecord } from "dns"; + export * from "./entities"; -export * from "./RequestInitProvider"; \ No newline at end of file +export * from "./RequestInitProvider"; + +export interface LibreCcmApiClient { + get( + endpoint: string, + searchParams: Record + ): Promise; + post( + endpoint: string, + searchParams: Record, + body: unknown + ): Promise; + put(endpoint: string, body: Record): Promise; + delete(endpoint: string): Promise; + head(endpoint: string): Promise; + options(endpoint: string): Promise; +} + +export interface ApiResponse { + status: number; + statusText: string; + json(): Promise>; + blob(): Promise; + text(): Promise +} + +export interface ApiError { + status: number; + statusText: string; + method: "get" | "post" | "put" | "delete" | "head" | "option"; + message: string; + url: string; +} + +export function buildUrl(base: string, endpoint: string, searchParams?: Record): string { + const url = new URL(base); + url.pathname = endpoint; + if (searchParams) { + const urlSearchParams: URLSearchParams = new URLSearchParams(); + for (const key in searchParams) { + urlSearchParams.append(key, searchParams[key]); + } + url.search = urlSearchParams.toString(); + } + + return url.href; +} + +export function buildEmbeddedApiClient(): LibreCcmApiClient { + if (!document) { + throw "document global variable is not available. Please use the buildRemoteApiClient."; + } + const baseUrl = new URL(document.documentURI); + baseUrl.hash = ""; + baseUrl.password = ""; + baseUrl.pathname = ""; + baseUrl.search = ""; + baseUrl.username = ""; + + return new EmbeddedApiClient(baseUrl.href, { + credentials: "include", + mode: "same-origin", + }); +} + +class FetchResponse implements ApiResponse { + readonly status: number; + readonly statusText: string; + #response: Response; + + constructor(response: Response) { + this.status = response.status; + this.statusText = response.statusText; + this.#response = response; + } + + json(): Promise> { + return this.#response.json(); + } + + blob(): Promise { + return this.#response.blob(); + } + + text(): Promise { + return this.#response.text(); + } +} + +class EmbeddedApiClient implements LibreCcmApiClient { + #baseUrl: string; + #fetchOptions: RequestInit; + + constructor(baseUrl: string, fetchOptions: RequestInit) { + this.#baseUrl = baseUrl; + this.#fetchOptions = fetchOptions; + } + + async get( + endpoint: string, + searchParams?: Record + ): Promise { + const fetchOptions: RequestInit = {}; + Object.assign(fetchOptions, this.#fetchOptions); + fetchOptions.method = "get"; + + const url = buildUrl(this.#baseUrl, endpoint, searchParams); + try { + const response = await fetch(url, this.#fetchOptions); + if (response.ok) { + return new FetchResponse(response); + } else { + throw { + status: response.status, + statusText: response.statusText, + method: "get", + message: "Received an error from the API endpoint. ", + url + } + } + } catch(err) { + throw `Failed to execute GET on ${url}: ${err}`; + } + } + + async post( + endpoint: string, + searchParams: Record, + body: unknown + ): Promise { + throw "Not implemented yet"; + } + + put(endpoint: string, body: Record): Promise { + throw "Not implemented yet"; + } + + delete(endpoint: string): Promise { + throw "Not implemented yet"; + } + + head(endpoint: string): Promise { + throw "Not implemented yet"; + } + + options(endpoint: string): Promise { + throw "Not implemented yet"; + } + + +}