diff --git a/ccm-core-apiclient/src/main/typescript/clients/sites-api.ts b/ccm-core-apiclient/src/main/typescript/clients/sites-api.ts new file mode 100644 index 000000000..1115df386 --- /dev/null +++ b/ccm-core-apiclient/src/main/typescript/clients/sites-api.ts @@ -0,0 +1,247 @@ +import { + ApiResponse, + LibreCcmApiClient, + ListView, +} from "@libreccm/ccm-apiclient-commons"; + +import { buildIdentifierParam } from "@libreccm/ccm-apiclient-commons"; + +import * as Constants from "../constants"; + +import { Site, buildSiteFromRecord } from "../entities/site"; +import { + CcmApplicationId, + buildCcmApplicationIdFromRecord, +} from "../entities/web"; + +/** + * Client for managing sites using the RESTful API + */ +export class SitesApiClient { + #apiClient: LibreCcmApiClient; + + readonly #SITES_API_PREFIX = `${Constants.ADMIN_API_PREFIX}/sites`; + + constructor(apiClient: LibreCcmApiClient) { + this.#apiClient = apiClient; + } + + async getSites(limit: number, offset: number): Promise> { + try { + const response: ApiResponse = await this.#apiClient.get( + `${this.#SITES_API_PREFIX}`, + { + limit: limit.toString(), + offset: offset.toString(), + } + ); + if (response.ok) { + const result: Record< + string, + unknown + > = (await response.json()) as Record; + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const sites: Site[] = list.map((record) => + buildSiteFromRecord(record) + ); + + return { + list: sites, + count: result.count as number, + limit: result.limit as number, + offset: result.offset as number, + }; + } else { + throw `Failed to get sites: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get sites: ${err}`; + } + } + + async getSite(siteIdentifier: string | number): Promise { + try { + const response: ApiResponse = await this.#apiClient.get( + `${this.#SITES_API_PREFIX}/${buildIdentifierParam( + siteIdentifier + )}` + ); + if (response.ok) { + return buildSiteFromRecord( + (await response.json()) as Record + ); + } else { + throw `Failed to get site ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get site ${siteIdentifier}: ${err}`; + } + } + + async addSite(site: Site): Promise { + try { + const response: ApiResponse = await this.#apiClient.post( + `${this.#SITES_API_PREFIX}`, + JSON.stringify(site) + ); + if (response.ok) { + return site.domainOfSite; + } else { + throw `Failed to add site ${site.domainOfSite}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to add site ${site.domainOfSite}: ${err}`; + } + } + + async updateSite( + siteIdentifier: string | number, + site: Site + ): Promise { + try { + const response: ApiResponse = await this.#apiClient.put( + `${this.#SITES_API_PREFIX}/${buildIdentifierParam( + siteIdentifier + )}`, + JSON.stringify(site) + ); + if (response.ok) { + return; + } else { + throw `Failed to update site ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to update site ${siteIdentifier}: ${err}`; + } + } + + async deleteSite(siteIdentifier: string | number): Promise { + try { + const response: ApiResponse = await this.#apiClient.delete( + `${this.#SITES_API_PREFIX}/${buildIdentifierParam( + siteIdentifier + )}` + ); + if (response.ok) { + return; + } else { + throw `Failed to delete site ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to delete site ${siteIdentifier}: ${err}`; + } + } + + async getApplications( + siteIdentifier: string | number, + limit: number, + offset: number + ): Promise> { + try { + const siteParam = buildIdentifierParam(siteIdentifier); + + const response: ApiResponse = await this.#apiClient.get( + `${this.#SITES_API_PREFIX}/${siteParam}/applications`, + { + limit: limit.toString(), + offset: offset.toString(), + } + ); + if (response.ok) { + const result: Record< + string, + unknown + > = (await response.json()) as Record; + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const applications: CcmApplicationId[] = list.map((record) => + buildCcmApplicationIdFromRecord(record) + ); + + return { + list: applications, + count: result.count as number, + limit: result.limit as number, + offset: result.limit as number, + }; + } else { + throw `Failed to get applications of + site ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get applications of + site ${siteIdentifier}: ${err}`; + } + } + + async addApplication( + siteIdentifier: string | number, + applicationIdentifier: string | number + ): Promise { + try { + const siteParam: string = buildIdentifierParam(siteIdentifier); + const applicationParam: string = buildIdentifierParam( + applicationIdentifier + ); + + const response: ApiResponse = await this.#apiClient.put( + `${ + this.#SITES_API_PREFIX + }/${siteParam}/groups/${applicationParam}` + ); + if (response.ok) { + return; + } else { + throw `Failed to add application ${applicationIdentifier} + to site + ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to add application ${applicationIdentifier} + to site + ${siteIdentifier}: ${err}`; + } + } + + async removeApplication( + siteIdentifier: string | number, + applicationIdentifier: string | number + ): Promise { + try { + const siteParam: string = buildIdentifierParam(siteIdentifier); + const applicationParam: string = buildIdentifierParam( + applicationIdentifier + ); + + const response: ApiResponse = await this.#apiClient.delete( + `${ + this.#SITES_API_PREFIX + }/${siteParam}/groups/${applicationParam}` + ); + if (response.ok) { + return; + } else { + throw `Failed to remove application ${applicationIdentifier} + fromsite + ${siteIdentifier}: + ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to remove application ${applicationIdentifier} + from site + ${siteIdentifier}: ${err}`; + } + } +} diff --git a/ccm-core-apiclient/src/main/typescript/entities/site.ts b/ccm-core-apiclient/src/main/typescript/entities/site.ts new file mode 100644 index 000000000..bc21f78f1 --- /dev/null +++ b/ccm-core-apiclient/src/main/typescript/entities/site.ts @@ -0,0 +1,38 @@ +/** + * Entities used by the RESTful API for managing sites + * @packageDocumentation + */ + +import { assertProperties } from "@libreccm/ccm-apiclient-commons"; + +export interface Site { + siteId: number; + uuid: string; + domainOfSite: string; + defaultSite: string; + defaultTheme: string; +} + +export interface SiteId { + siteId: number; + uuid: string; + domainOfSite: string; +} + +export function buildSiteFromRecord(record: Record): Site { + assertProperties(record, [ + "siteId", + "uuid", + "domainOfSite", + "defaultSite", + "defaultTheme", + ]); + + return { + siteId: record.siteId as number, + uuid: record.uuid as string, + domainOfSite: record.domainOfSite as string, + defaultSite: record.defaultSite as string, + defaultTheme: record.defaultTheme as string, + }; +} diff --git a/ccm-core-apiclient/src/main/typescript/entities/web.ts b/ccm-core-apiclient/src/main/typescript/entities/web.ts new file mode 100644 index 000000000..154c946fa --- /dev/null +++ b/ccm-core-apiclient/src/main/typescript/entities/web.ts @@ -0,0 +1,25 @@ +/** + * Entities used to manage application instances using the RESTful API. + * @packageDocumentation + */ + +import { + LocalizedString, + assertProperties, +} from "@libreccm/ccm-apiclient-commons"; + +export interface CcmApplicationId { + applicationType: string; + primaryUrl: string; +} + +export function buildCcmApplicationIdFromRecord( + record: Record +): CcmApplicationId { + assertProperties(record, ["applicationType", "primaryUrl"]); + + return { + applicationType: record.applicationType as string, + primaryUrl: record.primaryUrl as string, + }; +}