diff --git a/ccm-apiclient-commons/src/main/typescript/entities.ts b/ccm-apiclient-commons/src/main/typescript/entities.ts index e341e94ce..e9ab8468f 100644 --- a/ccm-apiclient-commons/src/main/typescript/entities.ts +++ b/ccm-apiclient-commons/src/main/typescript/entities.ts @@ -1,3 +1,25 @@ +/** + * A list view contains an array of objects and some data about the list. + */ +export interface ListView { + /** + * The list of objects + */ + list: T[], + /** + * The number of elements for the query. + */ + count: number, + /** + * Items per page + */ + limit: number, + /** + * The first item shown + */ + offset: number, +} + /** * Properties of the type `LocalizedString` are used in several entities * returned by various of the endpoints the RESTful API of LibreCCM. @@ -7,3 +29,4 @@ export interface LocalizedString { values: Record } + diff --git a/ccm-core-apiclient/src/main/typescript/clients/categorization-api.ts b/ccm-core-apiclient/src/main/typescript/clients/categorization-api.ts index 1d4138324..48642434d 100644 --- a/ccm-core-apiclient/src/main/typescript/clients/categorization-api.ts +++ b/ccm-core-apiclient/src/main/typescript/clients/categorization-api.ts @@ -1,9 +1,15 @@ import { ApiResponse, LibreCcmApiClient, + ListView, } from "@libreccm/ccm-apiclient-commons"; -import { Category, buildCategoryFromRecord } from "../entities/categorization"; +import { + Category, + Categorization, + buildCategoryFromRecord, + buildCategorizationFromRecord, +} from "../entities/categorization"; import * as Constants from "../constants"; export class CategorizationApiClient { @@ -69,7 +75,7 @@ export class CategorizationApiClient { } } - async updateCategoryByDomainAndPath( + async updateCategoryOfDomain( domain: string, path: string, category: Category @@ -91,7 +97,7 @@ export class CategorizationApiClient { } } - async updateCategoryById( + async updateCategoryWithId( categoryId: number, category: Category ): Promise { @@ -112,7 +118,7 @@ export class CategorizationApiClient { } } - async updateCategoryByUuid( + async updateCategoryWithUuid( uuid: string, category: Category ): Promise { @@ -133,10 +139,7 @@ export class CategorizationApiClient { } } - async deleteCategoryByDomainAndPath( - domain: string, - path: string - ): Promise { + async deleteCategoryOfDomain(domain: string, path: string): Promise { try { const response: ApiResponse = await this.#apiClient.delete( `${this.#CATEGORIES_API_PREFIX}/${domain}/${path}` @@ -148,12 +151,12 @@ export class CategorizationApiClient { throw `Failed to update category ${path} of domain ${domain}: ${response.status} ${response.statusText}`; } - } catch(err) { + } catch (err) { throw `Failed to delete category ${path} of domain ${domain}: ${err}`; } } - async deleteCategoryById(categoryId: number): Promise { + async deleteCategoryWithId(categoryId: number): Promise { try { const response: ApiResponse = await this.#apiClient.delete( `${this.#CATEGORIES_API_PREFIX}/ID-${categoryId}` @@ -170,12 +173,10 @@ export class CategorizationApiClient { } } - async deleteCategoryByUuid( - uuid: string, - ): Promise { + async deleteCategoryWithUuid(uuid: string): Promise { try { const response: ApiResponse = await this.#apiClient.delete( - `${this.#CATEGORIES_API_PREFIX}/UUID-${uuid}`, + `${this.#CATEGORIES_API_PREFIX}/UUID-${uuid}` ); if (response.ok) { @@ -188,4 +189,238 @@ export class CategorizationApiClient { throw `Failed to delete category with UUID ${uuid}: ${err}`; } } + + async getSubCategoriesByDomainAndPath( + domain: string, + path: string, + limit = 20, + offset = 0 + ): Promise> { + try { + const response: ApiResponse = await this.#apiClient.get( + `${ + this.#CATEGORIES_API_PREFIX + }/${domain}/${path}?limit=${limit}&offset=${offset}` + ); + + if (response.ok) { + const result: Record = await response.json(); + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const categories: Category[] = list.map((record) => + buildCategoryFromRecord(record) + ); + + return { + list: categories, + count: result.count as number, + limit: result.limit as number, + offset: result.offset as number, + }; + } else { + throw `Failed to get subcategories of category ${path} of + domain ${domain}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get subcategories of category ${path} of + domain ${domain}: ${err}`; + } + } + + async getSubCategoriesById( + categoryId: number, + limit = 20, + offset = 0 + ): Promise> { + try { + const response: ApiResponse = await this.#apiClient.get( + `${ + this.#CATEGORIES_API_PREFIX + }/ID-${categoryId}?limit=${limit}&offset=${offset}` + ); + + if (response.ok) { + const result: Record = await response.json(); + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const categories: Category[] = list.map((record) => + buildCategoryFromRecord(record) + ); + + return { + list: categories, + count: result.count as number, + limit: result.limit as number, + offset: result.offset as number, + }; + } else { + throw `Failed to get subcategories of category with ID + ${categoryId}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get subcategories of category with + ID ${categoryId}: ${err}`; + } + } + + async getSubCategoriesByUuid( + uuid: string, + limit = 20, + offset = 0 + ): Promise> { + try { + const response: ApiResponse = await this.#apiClient.get( + `${ + this.#CATEGORIES_API_PREFIX + }/UUID-${uuid}?limit=${limit}&offset=${offset}` + ); + + if (response.ok) { + const result: Record = await response.json(); + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const categories: Category[] = list.map((record) => + buildCategoryFromRecord(record) + ); + + return { + list: categories, + count: result.count as number, + limit: result.limit as number, + offset: result.offset as number, + }; + } else { + throw `Failed to get subcategories of category with UUID + ${uuid}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get subcategories of category with + UUID ${uuid}: ${err}`; + } + } + + /** + * Add a new subcategory to category + * + * @param domain + * @param path + * @param category + * + * @returns A Promise which resolves to the full path (including domain) + * of the new subcategory. + */ + async addSubCategoryToCategoryOfDomain( + domain: string, + path: string, + category: Category + ): Promise { + try { + const response: ApiResponse = await this.#apiClient.post( + `${this.#CATEGORIES_API_PREFIX}/${domain}/${path}`, + JSON.stringify(category) + ); + + if (response.ok) { + const result: string = await response.text(); + return result; + } else { + throw `Failed to add new subcategory to category ${path} of + domain ${domain}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to add new subcategory to category ${path} of + domain ${domain}: ${err}`; + } + } + + async addSubCategoryToWithId( + categoryId: number, + category: Category + ): Promise { + try { + const response: ApiResponse = await this.#apiClient.post( + `${this.#CATEGORIES_API_PREFIX}/ID-${categoryId}`, + JSON.stringify(category) + ); + + if (response.ok) { + const result: string = await response.text(); + return result; + } else { + throw `Failed to add new subcategory to category with + ID ${categoryId}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to add new subcategory to category with + ID ${categoryId}: ${err}`; + } + } + + async addSubCategoryToWithUuid( + uuid: string, + category: Category + ): Promise { + try { + const response: ApiResponse = await this.#apiClient.post( + `${this.#CATEGORIES_API_PREFIX}/UUID-${uuid}`, + JSON.stringify(category) + ); + + if (response.ok) { + const result: string = await response.text(); + return result; + } else { + throw `Failed to add new subcategory to category with + UUID ${uuid}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to add new subcategory to category with + UUID ${uuid}: ${err}`; + } + } + + async getObjectsInCategoryOfDomain( + domain: string, + path: string, + limit = 20, + offset = 0 + ): Promise> { + try { + const response: ApiResponse = await this.#apiClient.get( + `${ + this.#CATEGORIES_API_PREFIX + }/$domain/${path}/objects?limit=${limit}&offset=${offset}` + ); + + if (response.ok) { + const result: Record = await response.json(); + const list: Record[] = result.list as Record< + string, + unknown + >[]; + const objects: Categorization[] = list.map((record) => + buildCategorizationFromRecord(record) + ); + + return { + list: objects, + count: result.count as number, + limit: result.limit as number, + offset: result.offset as number + } + } else { + throw `Failed to get objects in category ${path} of + domain ${domain}: ${response.status} ${response.statusText}`; + } + } catch (err) { + throw `Failed to get objects in category ${path} of + domain ${domain}: ${err}`; + } + } } diff --git a/ccm-core-apiclient/src/main/typescript/entities/categorization.ts b/ccm-core-apiclient/src/main/typescript/entities/categorization.ts index 8660dcbaa..5590810a6 100644 --- a/ccm-core-apiclient/src/main/typescript/entities/categorization.ts +++ b/ccm-core-apiclient/src/main/typescript/entities/categorization.ts @@ -2,6 +2,7 @@ import { LocalizedString, assertProperties, } from "@libreccm/ccm-apiclient-commons"; +import { CcmObjectId } from "./core"; export interface AssociatedCategory { name: string; @@ -22,6 +23,16 @@ export interface Category { categoryOrder: number; } +export interface Categorization { + categorizationId: number; + uuid: string; + categorizedObject: CcmObjectId; + indexObject: boolean; + categoryOrder: number; + objectOrder: number; + type: string; +} + export function buildCategoryFromRecord( record: Record ): Category { @@ -55,3 +66,27 @@ export function buildCategoryFromRecord( categoryOrder: record.categoryOrder as number, }; } + +export function buildCategorizationFromRecord( + record: Record +): Categorization { + assertProperties(record, [ + "categorizationId", + "uuid", + "categorizedObject", + "indexObject", + "categoryOrder", + "objectOrder", + "type" + ]); + + return { + categorizationId: record.categorizationId as number, + uuid: record.uuid as string, + categorizedObject: record.categorizedObject as CcmObjectId, + indexObject: record.indexObject as boolean, + categoryOrder: record.categoryOrder as number, + objectOrder: record.objectOrder as number, + type: record.type as string + } +} diff --git a/ccm-core-apiclient/src/main/typescript/entities/core.ts b/ccm-core-apiclient/src/main/typescript/entities/core.ts new file mode 100644 index 000000000..a75770a1e --- /dev/null +++ b/ccm-core-apiclient/src/main/typescript/entities/core.ts @@ -0,0 +1,8 @@ +/** + * Basic data about an `CcmObject`. + */ +export interface CcmObjectId { + objectId: number, + uuid: string, + displayName: string +} \ No newline at end of file diff --git a/ccm-core/src/site/resources/ccm-core-api.json b/ccm-core/src/site/resources/ccm-core-api.json index baf747b89..9fffbe483 100644 --- a/ccm-core/src/site/resources/ccm-core-api.json +++ b/ccm-core/src/site/resources/ccm-core-api.json @@ -266,7 +266,7 @@ "schema" : { "type" : "integer", "format" : "int32", - "default" : 20 + "default" : 0 } } ], "responses" : { @@ -333,14 +333,16 @@ "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 20 } }, { "name" : "offset", "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 0 } } ], "responses" : { @@ -379,14 +381,16 @@ "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 20 } }, { "name" : "offset", "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 0 } } ], "responses" : { @@ -454,14 +458,16 @@ "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 20 } }, { "name" : "offset", "in" : "query", "schema" : { "type" : "integer", - "format" : "int32" + "format" : "int32", + "default" : 0 } } ], "responses" : { @@ -601,9 +607,9 @@ } } }, - "/api/admin/categories/UUID-{categoryId}/objects/objectIdentifier" : { + "/api/admin/categories/UUID-{categoryId}/objects/{objectIdentifier}" : { "delete" : { - "operationId" : "removeObjectsFromCategory", + "operationId" : "removeObjectFromCategory_2", "parameters" : [ { "name" : "categoryId", "in" : "path", @@ -678,7 +684,7 @@ "schema" : { "type" : "integer", "format" : "int32", - "default" : 20 + "default" : 0 } } ], "responses" : { @@ -720,7 +726,7 @@ "schema" : { "type" : "integer", "format" : "int32", - "default" : 20 + "default" : 0 } } ], "responses" : {