diff --git a/app/Classes/ApiManager/AnalyticsApi.ts b/app/Classes/ApiManager/AnalyticsApi.ts new file mode 100644 index 000000000..b7868b88f --- /dev/null +++ b/app/Classes/ApiManager/AnalyticsApi.ts @@ -0,0 +1,38 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export class AnalyticsApi extends ApiManager { + public PostAnalytics({ + maybeLastAction, + maybeLogToken, + }: { + maybeLastAction: string; + maybeLogToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/analytics/log_session`; + + const body = { + maybe_last_action: maybeLastAction, + maybe_log_token: maybeLogToken, + }; + + return this.post< + { + maybe_last_action: string; + maybe_log_token: string; + }, + { + success?: boolean; + log_token?: string; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + data: response.log_token, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/ApiManager.ts b/app/Classes/ApiManager/ApiManager.ts index b30c21bb3..07c4c447f 100644 --- a/app/Classes/ApiManager/ApiManager.ts +++ b/app/Classes/ApiManager/ApiManager.ts @@ -1,5 +1,7 @@ import environmentVariables from "../EnvironmentVariables"; +type NonNullableObject = NonNullable; + export interface ApiResponse { success: boolean; errorMessage?: string; @@ -132,4 +134,41 @@ export class ApiManager { body: formData, }); } + + protected camelToSnakeCase(str: string) { + return str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase(); + } + + protected parseQueryValues( + params: Record, + ): Record { + return Object.entries(params).reduce( + (allParams, [key, value]) => { + if (!value) { + return allParams; + } + const snakeKey = this.camelToSnakeCase(key); + if (Array.isArray(value)) { + return { ...allParams, [snakeKey]: value.join(",") }; + } + return { ...allParams, [snakeKey]: value.toString() }; + }, + {} as Record, + ); + } + + protected parseBodyValues( + params: NonNullableObject, + ): B { + return Object.entries(params).reduce((allParams, [key, value]) => { + if (!value) { + return allParams; + } + const snakeKey = this.camelToSnakeCase(key); + if (Array.isArray(value)) { + return { ...allParams, [snakeKey]: value }; + } + return { ...allParams, [snakeKey]: value }; + }, {} as B); + } } diff --git a/app/Classes/ApiManager/BetaKeysApi.ts b/app/Classes/ApiManager/BetaKeysApi.ts new file mode 100644 index 000000000..d74caf8c5 --- /dev/null +++ b/app/Classes/ApiManager/BetaKeysApi.ts @@ -0,0 +1,167 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; +import { BetaKey } from "~/models"; +import { Pagination } from "~/pages/PageEnigma/models"; + +export class BetaKeysApi extends ApiManager { + public CreateBetaKey({ + maybeNote, + maybeReferrerUsername, + numberOfKeys, + uuidIdempotencyToken, + }: { + maybeNote?: string; + maybeReferrerUsername?: string; + numberOfKeys: number; + uuidIdempotencyToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/beta_keys/create`; + + const body = { + maybe_note: maybeNote, + maybe_referrer_username: maybeReferrerUsername, + number_of_keys: numberOfKeys, + uuid_idempotency_token: uuidIdempotencyToken, + }; + + return this.post< + { + maybe_note?: string; + maybe_referrer_username?: string; + number_of_keys: number; + uuid_idempotency_token: string; + }, + { + success?: boolean; + beta_keys?: string[]; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + data: response.beta_keys, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListBetaKeys({ + sortAscending, + pageSize, + pageIndex, + maybeReferrerUsername, + onlyListRemaining, + }: { + sortAscending?: boolean; + pageSize?: number; + pageIndex?: number; + maybeReferrerUsername?: string; + onlyListRemaining?: boolean; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/beta_keys/list`; + + const query = { + sort_ascending: sortAscending, + page_size: pageSize, + page_index: pageIndex, + maybe_referrer_username: maybeReferrerUsername, + only_list_remaining: onlyListRemaining, + }; + + return this.get<{ + success?: boolean; + beta_keys?: BetaKey[]; + pagination?: Pagination; + BadInput?: string; + }>({ endpoint, query }) + .then((response) => ({ + success: response.success ?? false, + data: response.beta_keys, + pagination: response.pagination, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public RedeemBetaKey({ + betaKey, + }: { + betaKey: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/beta_keys/redeem`; + + return this.post< + { + beta_key: string; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { beta_key: betaKey } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public UpdateBetaKeyIsDistributed({ + betaKey, + isDistributed, + }: { + betaKey: string; + isDistributed: boolean; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/beta_keys/${betaKey}/distributed`; + + return this.post< + { + is_distributed: boolean; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { is_distributed: isDistributed } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public UpdateBetaKeyNote({ + token, + note, + }: { + token: string; + note?: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/beta_keys/${token}/note`; + + return this.post< + { + note?: string; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { note } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/CommentsApi.ts b/app/Classes/ApiManager/CommentsApi.ts new file mode 100644 index 000000000..8319f4ac4 --- /dev/null +++ b/app/Classes/ApiManager/CommentsApi.ts @@ -0,0 +1,91 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export class CommentsApi extends ApiManager { + public CreateComment({ + commentMarkdown, + entityToken, + entityType, + uuidIdempotencyToken, + }: { + commentMarkdown: string; + entityToken: string; + entityType: string; + uuidIdempotencyToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/new`; + + const body = { + comment_markdown: commentMarkdown, + entity_token: entityToken, + entity_type: entityType, + uuid_idempotency_token: uuidIdempotencyToken, + }; + + return this.post< + { + comment_markdown: string; + entity_token: string; + entity_type: string; + uuid_idempotency_token: string; + }, + { + success?: boolean; + comment_token?: string; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + data: response.comment_token, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public DeleteComment({ + commentToken, + }: { + commentToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/delete/${commentToken}`; + + return this.post< + { as_mod: boolean }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { as_mod: true } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListCommentsByEntity({ + entityType, + entityToken, + }: { + entityType: string; + entityToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/list/${entityType}/${entityToken}`; + + return this.get<{ + success: boolean; + comments: string[]; + }>({ endpoint }) + .then((response) => ({ + success: true, + data: response.comments, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/EngineApi.ts b/app/Classes/ApiManager/EngineApi.ts new file mode 100644 index 000000000..605d6da28 --- /dev/null +++ b/app/Classes/ApiManager/EngineApi.ts @@ -0,0 +1,39 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export class EngineApi extends ApiManager { + public async ConvertTbxToGltf({ + mediaFileToken, + uuidIdempotencyToken, + }: { + mediaFileToken: string; + uuidIdempotencyToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/conversion/enqueue_fbx_to_gltf`; + + const body = { + media_file_token: mediaFileToken, + uuid_idempotency_token: uuidIdempotencyToken, + }; + + return this.post< + { media_file_token: string; uuid_idempotency_token: string }, + { success?: boolean; inference_job_token?: string; BadInput?: string } + >({ + endpoint, + body: body, + }) + .then((response) => { + return { + success: response.success ?? false, + data: response.inference_job_token, + errorMessage: response.BadInput, + }; + }) + .catch((err) => { + return { + success: false, + errorMessage: err.message, + }; + }); + } +} diff --git a/app/Classes/ApiManager/FeaturedItemsApi.ts b/app/Classes/ApiManager/FeaturedItemsApi.ts new file mode 100644 index 000000000..5a07de782 --- /dev/null +++ b/app/Classes/ApiManager/FeaturedItemsApi.ts @@ -0,0 +1,96 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export class FeaturedItemsApi extends ApiManager { + public CreateFeaturedItem({ + entityToken, + entityType, + }: { + entityToken: string; + entityType: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/create`; + + const body = { + entity_token: entityToken, + entity_type: entityType, + }; + + return this.post< + { + entity_token: string; + entity_type: string; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public DeleteFeaturedItem({ + entityToken, + entityType, + }: { + entityToken: string; + entityType: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/delete`; + + const body = { + entity_token: entityToken, + entity_type: entityType, + }; + + return this.delete< + { + entity_token: string; + entity_type: string; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public CheckFeaturedItem({ + entityType, + entityToken, + }: { + entityType: string; + entityToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/is_featured/${entityType}/${entityToken}`; + + return this.post< + undefined, + { + success?: boolean; + is_featured?: boolean; + BadInput?: string; + } + >({ endpoint }) + .then((response) => ({ + success: response.success ?? false, + data: response.is_featured, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/JobsApi.ts b/app/Classes/ApiManager/JobsApi.ts index 4dc39c2be..b9fec0bc4 100644 --- a/app/Classes/ApiManager/JobsApi.ts +++ b/app/Classes/ApiManager/JobsApi.ts @@ -1,7 +1,43 @@ import { ApiManager, ApiResponse } from "./ApiManager"; -import { Job } from "~/models"; +import { Job, JobState } from "~/models"; export class JobsApi extends ApiManager { + public GetJobByToken({ + token, + }: { + token: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/jobs/job/${token}`; + + return this.get<{ + success: boolean; + state: JobState; + }>({ endpoint }) + .then((response) => ({ + success: response.success, + data: response.state, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListJobs(): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/jobs/batch`; + + return this.get<{ + success: boolean; + job_states: JobState[]; + }>({ endpoint }) + .then((response) => ({ + success: response.success, + data: response.job_states, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + public ListRecentJobs(): Promise> { const endpoint = `${this.ApiTargets.BaseApi}/v1/jobs/session`; diff --git a/app/Classes/ApiManager/MediaFilesApi.ts b/app/Classes/ApiManager/MediaFilesApi.ts index 988eb035f..a50299b41 100644 --- a/app/Classes/ApiManager/MediaFilesApi.ts +++ b/app/Classes/ApiManager/MediaFilesApi.ts @@ -6,29 +6,15 @@ import { } from "~/pages/PageEnigma/models"; import { ApiManager, ApiResponse } from "./ApiManager"; import { authentication } from "~/signals"; +import { Property } from "csstype"; +import Visibility = Property.Visibility; +import { + FilterEngineCategories, + FilterMediaClasses, + FilterMediaType, +} from "~/enums"; -export enum FilterMediaClasses { - AUDIO = "audio", - IMAGE = "image", - VIDEO = "video", -} - -export enum FilterMediaType { - SCENE_JSON = "scene_json", - GLB = "glb", - GLTF = "gltf", -} - -export enum FilterEngineCategories { - ANIMATION = "animation", - AUDIO = "audio", - CHARACTER = "character", - EXPRESSION = "expression", - OBJECT = "object", - SCENE = "scene", -} - -interface ListFeaturedMediaQuery { +interface ListMediaQuery { sort_ascending?: boolean; page_size?: number; cursor?: string; @@ -48,6 +34,40 @@ interface ListUserMediaQuery { } export class MediaFilesApi extends ApiManager { + public async DeleteMediaFileByToken({ + mediaFileToken, + asMod = true, + setDelete = true, + }: { + mediaFileToken: string; + asMod?: boolean; + setDelete?: boolean; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/file/${mediaFileToken}`; + + const body = { as_mod: asMod, set_delete: setDelete }; + return await this.delete< + { + as_mod: boolean; + set_delete: boolean; + }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { + success: false, + errorMessage: err.message, + }; + }); + } + public async ListMediaFilesByTokens({ mediaTokens, }: { @@ -91,6 +111,74 @@ export class MediaFilesApi extends ApiManager { }); } + public async ListMediaFiles( + query: ListMediaQuery, + ): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list`; + const queryWithStrings = { + ...query, + filter_media_classes: query.filter_media_classes + ? query.filter_media_classes.join(",") + : undefined, + filter_media_type: query.filter_media_type + ? query.filter_media_type.join(",") + : undefined, + filter_engine_categories: query.filter_engine_categories + ? query.filter_engine_categories.join(",") + : undefined, + }; + return await this.get<{ + success: boolean; + results: MediaInfo[]; + pagination?: PaginationInfinite; + }>({ endpoint, query: queryWithStrings }) + .then((response) => ({ + success: response.success, + data: response.results ?? [], + pagination: response.pagination, + })) + .catch((err) => { + return { + success: false, + errorMessage: err.message, + }; + }); + } + + public async ListFeaturedMediaFiles( + query: ListMediaQuery, + ): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list_featured`; + const queryWithStrings = { + ...query, + filter_media_classes: query.filter_media_classes + ? query.filter_media_classes.join(",") + : undefined, + filter_media_type: query.filter_media_type + ? query.filter_media_type.join(",") + : undefined, + filter_engine_categories: query.filter_engine_categories + ? query.filter_engine_categories.join(",") + : undefined, + }; + return await this.get<{ + success: boolean; + results: MediaInfo[]; + pagination: Pagination; + }>({ endpoint, query: queryWithStrings }) + .then((response) => ({ + success: true, + data: response.results, + pagination: response.pagination, + })) + .catch((err) => { + return { + success: false, + errorMessage: err.message, + }; + }); + } + public async ListUserMediaFiles( query: ListUserMediaQuery, ): Promise> { @@ -126,31 +214,77 @@ export class MediaFilesApi extends ApiManager { }); } - public async ListFeaturedMediaFiles( - query: ListFeaturedMediaQuery, - ): Promise> { - const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list_featured`; - const queryWithStrings = { - ...query, - filter_media_classes: query.filter_media_classes - ? query.filter_media_classes.join(",") - : undefined, - filter_media_type: query.filter_media_type - ? query.filter_media_type.join(",") - : undefined, - filter_engine_categories: query.filter_engine_categories - ? query.filter_engine_categories.join(",") - : undefined, - }; - return await this.get<{ - success: boolean; - results: MediaInfo[]; - pagination: Pagination; - }>({ endpoint, query: queryWithStrings }) + public async RenameMediaFileByToken({ + mediaToken, + name, + }: { + mediaToken: string; + name: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_file/rename/${mediaToken}`; + const body = { name }; + + return await this.post< + { name: string }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body }) .then((response) => ({ - success: true, - data: response.results, - pagination: response.pagination, + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public async UpdateCoverImage({ + mediaFileToken, + imageToken, + }: { + mediaFileToken: string; + imageToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/cover_image/${mediaFileToken}`; + return await this.post< + { cover_image_media_file_token: string }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { cover_image_media_file_token: imageToken } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { + success: false, + errorMessage: err.message, + }; + }); + } + + public async UpdateVisibility({ + mediaFileToken, + visibility, + }: { + mediaFileToken: string; + visibility: Visibility; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/visibility/${mediaFileToken}`; + return await this.post< + { creator_set_visibility: string }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { creator_set_visibility: visibility } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, })) .catch((err) => { return { diff --git a/app/Classes/ApiManager/MediaUploadApi.ts b/app/Classes/ApiManager/MediaUploadApi.ts index 9a3d1986f..d5b26b196 100644 --- a/app/Classes/ApiManager/MediaUploadApi.ts +++ b/app/Classes/ApiManager/MediaUploadApi.ts @@ -2,52 +2,29 @@ import { ApiManager, ApiResponse } from "./ApiManager"; import { Visibility } from "~/enums"; export class MediaUploadApi extends ApiManager { - public async UploadThumbnail({ - mediaFileToken, - imageToken, - }: { - mediaFileToken: string; - imageToken: string; - }): Promise> { - const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/cover_image/${mediaFileToken}`; - return await this.post< - { cover_image_media_file_token: string }, - { - success?: boolean; - BadInput?: string; - } - >({ endpoint, body: { cover_image_media_file_token: imageToken } }) - .then((response) => ({ - success: response.success ?? false, - errorMessage: response.BadInput, - })) - .catch((err) => { - return { - success: false, - errorMessage: err.message, - }; - }); - } - - public async UploadImage({ + private async Upload({ + endpoint, uuid, blob, fileName, - title, - visibility = Visibility.Public, + options, }: { + endpoint: string; blob: Blob; fileName: string; uuid: string; - title?: string; - visibility?: Visibility; + options: Record; }): Promise> { - const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/image`; - const formRecord = { - is_intermediate_system_file: "true", - ...(title ? { maybe_title: title } : {}), - maybe_visibility: visibility, - }; + const formRecord = Object.entries(options).reduce( + (allOptions, [key, value]) => { + if (value === undefined) { + return allOptions; + } + return { ...allOptions, [key]: value.toString() }; + }, + {} as Record, + ); + return await this.postForm<{ success: boolean; media_file_token?: string; @@ -66,56 +43,169 @@ export class MediaUploadApi extends ApiManager { }); } - public async UploadVideo({ - uuid, + public async UploadAudio({ blob, fileName, - title, - styleName, - sceneSourceMediaFileToken, - visibility = Visibility.Public, + uuid, + maybe_title, + maybe_visibility = Visibility.Public, }: { blob: Blob; fileName: string; uuid: string; - title: string; - styleName?: string; - sceneSourceMediaFileToken?: string; - visibility?: Visibility; + maybe_title?: string | undefined; + maybe_visibility?: Visibility | undefined; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/audio`; + const options: Record = { + maybe_title, + maybe_visibility: maybe_visibility?.toString(), + }; + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadImage({ + blob, + fileName, + uuid, + maybe_title, + maybe_visibility = Visibility.Public, + }: { + blob: Blob; + fileName: string; + uuid: string; + maybe_title?: string | undefined; + maybe_visibility?: Visibility | undefined; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/image`; + const options: Record = { + is_intermediate_system_file: "true", + maybe_title, + maybe_visibility: maybe_visibility?.toString(), + }; + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadNewEngineAsset({ + blob, + fileName, + uuid, + maybe_animation_type, + maybe_duration_millis, + maybe_title, + maybe_visibility = Visibility.Public, + }: { + blob: Blob; + fileName: string; + uuid: string; + maybe_animation_type?: string; + maybe_duration_millis?: number; + maybe_title?: string; + maybe_visibility?: Visibility; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/new_engine_asset`; + const options: Record = { + maybe_title, + maybe_visibility: maybe_visibility?.toString(), + maybe_animation_type, + maybe_duration_millis, + }; + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadNewScene({ + blob, + fileName, + uuid, + maybe_title, + maybe_visibility = Visibility.Public, + }: { + blob: Blob; + fileName: string; + uuid: string; + maybe_title?: string; + maybe_visibility?: Visibility; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/new_scene`; + const options: Record = { + maybe_title, + maybe_visibility: maybe_visibility?.toString(), + }; + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadNewVideo({ + blob, + fileName, + uuid, + maybe_title, + maybe_visibility = Visibility.Public, + maybe_style_name, + maybe_scene_source_media_file_token, + }: { + blob: Blob; + fileName: string; + uuid: string; + maybe_title?: string; + maybe_visibility?: Visibility; + maybe_style_name?: string; + maybe_scene_source_media_file_token?: string; }): Promise> { const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/new_video`; - const formRecord = { + const options: Record = { is_intermediate_system_file: "true", - maybe_title: title, - maybe_visibility: visibility, - ...(styleName ? { maybe_style_name: styleName } : {}), - ...(sceneSourceMediaFileToken - ? { - maybe_scene_source_media_file_token: sceneSourceMediaFileToken, - } - : {}), + maybe_title, + maybe_visibility: maybe_visibility?.toString(), + maybe_style_name, + maybe_scene_source_media_file_token, }; - return await this.postForm<{ - success?: string; - media_file_token?: string; - BadInput?: string; - }>({ - endpoint, - formRecord, - blob, - blobFileName: fileName, - uuid, - }) - .then((response) => ({ - success: Boolean(response.success ?? false), - data: response.media_file_token, - errorMessage: response.BadInput, - })) - .catch((err) => { - return { - success: false, - errorMessage: err.message, - }; - }); + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadPmx({ + blob, + fileName, + uuid, + engine_category, + maybe_animation_type, + maybe_duration_millis, + maybe_title, + maybe_visibility = Visibility.Public, + }: { + blob: Blob; + fileName: string; + uuid: string; + engine_category?: string; + maybe_animation_type?: string; + maybe_duration_millis?: number; + maybe_title?: string; + maybe_visibility?: Visibility; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/pmx`; + const options: Record = { + is_intermediate_system_file: "true", + engine_category, + maybe_animation_type, + maybe_duration_millis, + maybe_title, + maybe_visibility, + }; + return this.Upload({ endpoint, blob, fileName, uuid, options }); + } + + public async UploadSavedScene({ + blob, + fileName, + uuid, + mediaToken, + }: { + blob: Blob; + fileName: string; + uuid: string; + mediaToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/upload/saved_scene/${mediaToken}`; + const options: Record = {}; + return this.Upload({ endpoint, blob, fileName, uuid, options }); } } diff --git a/app/Classes/ApiManager/ModerationApi.ts b/app/Classes/ApiManager/ModerationApi.ts new file mode 100644 index 000000000..f5e80b03f --- /dev/null +++ b/app/Classes/ApiManager/ModerationApi.ts @@ -0,0 +1,42 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export class ModerationApi extends ApiManager { + public async UpdateFeatureFlagsByToken({ + usernameOrToken, + flags, + }: { + usernameOrToken: string; + flags: string[]; + }): Promise< + ApiResponse<{ + inference_job_token?: string; + inference_job_token_type?: string; + }> + > { + const endpoint = `${this.ApiTargets.BaseApi}/v1/moderation/user_feature_flags/${usernameOrToken}`; + + const body = { + action: { + AddFlags: { + flags, + }, + }, + }; + + return this.post< + { action: { AddFlags: { flags: string[] } } }, + { success?: boolean; BadInput?: string } + >({ + endpoint, + body: body, + }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => ({ + success: false, + errorMessage: err.message, + })); + } +} diff --git a/app/Classes/ApiManager/PromptsApi.ts b/app/Classes/ApiManager/PromptsApi.ts new file mode 100644 index 000000000..6c9d96779 --- /dev/null +++ b/app/Classes/ApiManager/PromptsApi.ts @@ -0,0 +1,25 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; +import { Prompts } from "~/pages/PageEnigma/models"; + +export class PromptsApi extends ApiManager { + public GetPromptsByToken({ + token, + }: { + token: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/prompts/${token}`; + + return this.get<{ + success: boolean; + prompt: Prompts; + error_reason?: string; + }>({ endpoint }) + .then((response) => ({ + success: response.success, + data: response.prompt, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/UserBookmarksApi.ts b/app/Classes/ApiManager/UserBookmarksApi.ts new file mode 100644 index 000000000..aa911f5a4 --- /dev/null +++ b/app/Classes/ApiManager/UserBookmarksApi.ts @@ -0,0 +1,202 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; +import { + Pagination, + UserBookmarkBatch, + UserBookmarkByEntity, + UserBookmarkByUser, +} from "~/pages/PageEnigma/models"; +import { authentication } from "~/signals"; + +export enum ScopedEntityTypes { + USER = "user", + TTS_MODEL = "tts-model", + TTS_RESULT = "tts-result", + W2L_TEMPLATE = "w2l-template", + W2L_RESULT = "w2l-result", + MEDIA_FILE = "media_file", + MODEL_WEIGHT = "model_weight", + VOICE_CONVERSION_MODEL = "voice_conversion_model", + ZS_VOICE = "z_voice", +} + +export enum ScopedWeightTypes { + HIFIGAN_TTl2 = "hifigan_tt2", + RVC_V2 = "rvc_v2", + SD_1_5 = "sd_1.5", + SDXL = "sdxl", + SO_VITS_SVC = "so_vits_svc", + TT2 = "tt2", + LORA = "loRA", + VALL_E = "vall_e", + COMFY_UI = "comfy_ui", +} + +export enum ScopedMediaFileType { + IMAGE_GENERATION = "image_generation", + TEXT_TO_SPEECH = "text_to_speech", + VOCODER = "vocoder", + VOICE_CONVERSION = "voice_conversion", + WORKFLOW_CONFIG = "workflow_config", +} + +interface ListUserBookmarksByUserRequest { + username?: string; + sort_ascending?: boolean; + page_size?: number; + page_index?: number; + maybe_scoped_entity_type?: ScopedEntityTypes[]; + maybe_scoped_weight_type?: ScopedWeightTypes[]; + maybe_scoped_media_file_type?: ScopedMediaFileType[]; +} + +export class UserBookmarksApi extends ApiManager { + public CreateUserBookmark({ + entityToken, + entityType, + }: { + entityToken: string; + entityType: string; + }): Promise< + ApiResponse<{ + new_bookmark_count_for_entity?: number; + user_bookmark_token?: string; + }> + > { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_bookmarks/create`; + const body = { + entity_token: entityToken, + entity_type: entityType, + }; + + return this.post< + { + entity_token: string; + entity_type: string; + }, + { + success?: boolean; + new_bookmark_count_for_entity?: number; + user_bookmark_token: string; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + data: { + new_bookmark_count_for_entity: response.new_bookmark_count_for_entity, + user_bookmark_token: response.user_bookmark_token, + }, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public DeleteUserBookmark({ + entityToken, + }: { + entityToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_bookmarks/delete/${entityToken}`; + + return this.delete< + { as_mod: boolean }, + { + success?: boolean; + BadInput?: string; + } + >({ endpoint, body: { as_mod: true } }) + .then((response) => ({ + success: response.success ?? false, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListUserBookmarks(): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_bookmarks/batch`; + + return this.get<{ + success?: boolean; + bookmarks?: UserBookmarkBatch[]; + BadInput?: string; + }>({ endpoint }) + .then((response) => ({ + success: response.success ?? false, + data: response.bookmarks, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListUserBookmarksByUser({ + username, + sort_ascending, + page_size, + page_index, + maybe_scoped_entity_type, + maybe_scoped_weight_type, + maybe_scoped_media_file_type, + }: ListUserBookmarksByUserRequest): Promise< + ApiResponse + > { + const user = username ?? authentication.userInfo.value?.username; + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_bookmarks/list/${user}`; + + const query = { + sort_ascending, + page_size, + page_index, + maybe_scoped_entity_type: maybe_scoped_entity_type + ? maybe_scoped_entity_type.join(",") + : undefined, + maybe_scoped_weight_type: maybe_scoped_weight_type + ? maybe_scoped_weight_type.join(",") + : undefined, + maybe_scoped_media_file_type: maybe_scoped_media_file_type + ? maybe_scoped_media_file_type.join(",") + : undefined, + }; + + return this.get<{ + success?: boolean; + pagination?: Pagination; + results: UserBookmarkByUser[]; + }>({ endpoint, query }) + .then((response) => ({ + success: response.success ?? false, + data: response.results, + pagination: response.pagination, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListUserBookmarksByEntity({ + entityType, + entityToken, + }: { + entityType: string; + entityToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_bookmarks/list/${entityType}/${entityToken}`; + + return this.get<{ + success: boolean; + user_bookmarks: UserBookmarkByEntity[]; + }>({ endpoint }) + .then((response) => ({ + success: true, + data: response.user_bookmarks, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/UserRatingsApi.ts b/app/Classes/ApiManager/UserRatingsApi.ts new file mode 100644 index 000000000..cc289c974 --- /dev/null +++ b/app/Classes/ApiManager/UserRatingsApi.ts @@ -0,0 +1,92 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; + +export interface UserRating { + entity_token: string; + entity_type: string; + rating_value: string; +} + +export class UserRatingApi extends ApiManager { + public ListUserRatings(): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_rating/batch`; + + return this.get<{ + success?: boolean; + ratings?: UserRating[]; + BadInput?: string; + }>({ endpoint }) + .then((response) => ({ + success: response.success ?? false, + data: response.ratings, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public PostUserRating({ + entityToken, + entityType, + ratingValue, + }: { + entityToken: string; + entityType: string; + ratingValue: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_rating/rate`; + + const body = { + entity_token: entityToken, + entity_type: entityType, + rating_value: ratingValue, + }; + + return this.post< + { + entity_token: string; + entity_type: string; + rating_value: string; + }, + { + success?: boolean; + new_positive_rating_count_for_entity?: number; + BadInput?: string; + } + >({ endpoint, body }) + .then((response) => ({ + success: response.success ?? false, + data: response.new_positive_rating_count_for_entity, + errorMessage: response.BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListUserRatingByEntity({ + entityType, + entityToken, + }: { + entityType: string; + entityToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/user_rating/view/${entityType}/${entityToken}`; + + return this.get<{ + success?: boolean; + maybe_rating_value?: string; + BadInput?: string; + }>({ endpoint }) + .then((response) => { + return { + success: response.success ?? false, + data: response.maybe_rating_value, + errorMessage: response.BadInput, + }; + }) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/Classes/ApiManager/WeightsApi.ts b/app/Classes/ApiManager/WeightsApi.ts new file mode 100644 index 000000000..c18d4bcaa --- /dev/null +++ b/app/Classes/ApiManager/WeightsApi.ts @@ -0,0 +1,293 @@ +import { ApiManager, ApiResponse } from "./ApiManager"; +import { authentication } from "~/signals"; +import { Weight } from "~/models"; +import { Pagination, PaginationInfinite } from "~/pages/PageEnigma/models"; +import { + FilterEngineCategories, + FilterMediaClasses, + FilterMediaType, + Visibility, +} from "~/enums"; + +export enum ScopedWeightType { + HIFIGAN_TT2 = "hifigan_tt2", + RVC_V2 = "rvc_v2", + SD_1_5 = "sd_1.5", + SDXL = "sdxl", + SO_VITS_SVC = "so_vits_svc", + TT2 = "tt2", + LORA = "loRA", + VALL_E = "vall_e", + COMFY_UI = "comfy_ui", +} +export enum ScopedWeightCategory { + IMAGE_GENERATION = "image_generation", + TEXT_TO_SPEECH = "text_to_speech", + VOCODER = "vocoder", + VOICE_CONVERSION = "voice_conversion", + WORKFLOW_CONFIG = "workflow_config", +} + +interface WeightsRequest { + username?: string; + pageSize?: number; + scopedWeightType?: ScopedWeightType[]; + scopedWeightCategory?: ScopedWeightCategory[]; +} + +export interface ListWeightsByUserRequest extends WeightsRequest { + pageIndex?: number; + sortAscending?: boolean; +} + +export interface ListWeightsRequest extends WeightsRequest { + cursor?: string; + cursorIsReversed?: boolean; +} + +export interface ListFeaturedWeightsRequest { + sortAscending?: boolean; + pageSize?: number; + cursor?: string; + cursorIsReversed?: boolean; + filterMediaClasses?: FilterMediaClasses[]; + filterMediaType?: FilterMediaType[]; + filterEngineCategories?: FilterEngineCategories[]; +} + +export interface SearchWeightParams { + ietfLanguageSubtag: string; + searchTerm: string; + weightCategory: ScopedWeightCategory; + weightType: ScopedWeightType; +} + +export interface SearchWeightRequest { + ietf_language_subtag: string; + search_term: string; + weight_category: string; + weight_type: string; +} + +interface GetWeightByTokenResponse extends Weight { + success: boolean; +} + +export interface UpdateWeightByTokenParams { + weightToken: string; + coverImageMediaFileToken: string; + descriptionMarkdown: string; + title: string; + visibility: Visibility; +} +export interface UpdateWeightByTokenRequest { + cover_image_media_file_token: string; + description_markdown: string; + title: string; + visibility: Visibility; +} + +export class WeightsApi extends ApiManager { + public ListWeightsByUser({ + username, + ...params + }: ListWeightsByUserRequest): Promise> { + const user = username ?? authentication.userInfo.value?.username; + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/by_user/${user}`; + + const query = this.parseQueryValues(params); + + return this.get<{ + success: boolean; + results: Weight[]; + pagination: Pagination; + }>({ endpoint, query }) + .then((response) => ({ + success: true, + data: response.results, + pagination: response.pagination, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListWeights({ + ...params + }: ListWeightsRequest): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/list`; + + const query = this.parseQueryValues(params); + + return this.get<{ + success: boolean; + results: Weight[]; + pagination: PaginationInfinite; + }>({ endpoint, query }) + .then((response) => ({ + success: true, + data: response.results, + pagination: response.pagination, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListWeightsFeatured({ + ...params + }: ListFeaturedWeightsRequest): Promise< + ApiResponse + > { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/list_featured`; + + const query = this.parseQueryValues(params); + + return this.get<{ + success: boolean; + results: Weight[]; + pagination: PaginationInfinite; + }>({ endpoint, query }) + .then((response) => ({ + success: true, + data: response.results, + pagination: response.pagination, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public ListWeightsPinned(): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/list_pinned`; + + return this.get<{ + success: boolean; + results: Weight[]; + }>({ endpoint }) + .then((response) => ({ + success: true, + data: response.results, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public SearchWeights( + params: SearchWeightParams, + ): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/search`; + + const body = this.parseBodyValues( + params, + ); + + return this.post< + SearchWeightRequest, + { + success: boolean; + weights: Weight[]; + } + >({ endpoint, body }) + .then((response) => ({ + success: true, + data: response.weights, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public GetWeightByToken({ + weightToken, + }: { + weightToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/weight/${weightToken}`; + + return this.get({ endpoint }) + .then(({ success, ...mediaFile }) => ({ + success: success, + data: mediaFile, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public UpdateWeightByToken({ + weightToken, + ...params + }: UpdateWeightByTokenParams): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/weight/${weightToken}`; + + const body = this.parseBodyValues< + Omit, + UpdateWeightByTokenRequest + >(params); + + return this.post< + UpdateWeightByTokenRequest, + { success?: boolean; BadInput?: string } + >({ endpoint, body }) + .then(({ success, BadInput }) => ({ + success: success ?? false, + errorMessage: BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public DeleteWeightByToken({ + weightToken, + }: { + weightToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/weight/${weightToken}`; + + const body = { + as_mod: true, + set_delete: true, + }; + + return this.delete< + { as_mod: boolean; set_delete: boolean }, + { success?: boolean; BadInput?: string } + >({ endpoint, body }) + .then(({ success, BadInput }) => ({ + success: success ?? false, + errorMessage: BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } + + public UpdateWeightCoverImageByToken({ + weightToken, + coverImageMediaFileToken, + }: { + weightToken: string; + coverImageMediaFileToken: string; + }): Promise> { + const endpoint = `${this.ApiTargets.BaseApi}/v1/weights/${weightToken}/cover_image`; + + const body = { + cover_image_media_file_token: coverImageMediaFileToken, + }; + + return this.post< + { cover_image_media_file_token: string }, + { success?: boolean; BadInput?: string } + >({ endpoint, body }) + .then(({ success, BadInput }) => ({ + success: success ?? false, + errorMessage: BadInput, + })) + .catch((err) => { + return { success: false, errorMessage: err.message }; + }); + } +} diff --git a/app/enums/QueryFilters.ts b/app/enums/QueryFilters.ts new file mode 100644 index 000000000..d5f1c7b52 --- /dev/null +++ b/app/enums/QueryFilters.ts @@ -0,0 +1,20 @@ +export enum FilterMediaClasses { + AUDIO = "audio", + IMAGE = "image", + VIDEO = "video", +} + +export enum FilterMediaType { + SCENE_JSON = "scene_json", + GLB = "glb", + GLTF = "gltf", +} + +export enum FilterEngineCategories { + ANIMATION = "animation", + AUDIO = "audio", + CHARACTER = "character", + EXPRESSION = "expression", + OBJECT = "object", + SCENE = "scene", +} diff --git a/app/enums/index.ts b/app/enums/index.ts index 341970058..5b25ee291 100644 --- a/app/enums/index.ts +++ b/app/enums/index.ts @@ -7,5 +7,6 @@ export * from "./ClipGroup"; export * from "./ClipType"; export * from "./FileTypes"; export * from "./JobStatus"; +export * from "./QueryFilters"; export * from "./toasts"; export * from "./Visibility"; diff --git a/app/models/BetaKeys.ts b/app/models/BetaKeys.ts new file mode 100644 index 000000000..9bde5a9de --- /dev/null +++ b/app/models/BetaKeys.ts @@ -0,0 +1,26 @@ +interface DefaultAvatar { + color_index: number; + image_index: number; +} + +interface User { + default_avatar: DefaultAvatar; + display_name: string; + gravatar_hash: string; + user_token: string; + username: string; +} + +export interface BetaKey { + created_at: string; + creator: User; + is_distributed: boolean; + key_value: string; + maybe_note?: string; + maybe_note_html?: string; + maybe_redeemed_at?: string; + maybe_redeemer?: User; + maybe_referrer?: User; + product: string; + token: string; +} diff --git a/app/models/Job.ts b/app/models/Job.ts index 75fda5457..b2c748842 100644 --- a/app/models/Job.ts +++ b/app/models/Job.ts @@ -1,3 +1,5 @@ +import { JobStatus } from "~/enums"; + export interface Job { created_at: string; job_token: string; diff --git a/app/models/JobState.ts b/app/models/JobState.ts new file mode 100644 index 000000000..29c103ef7 --- /dev/null +++ b/app/models/JobState.ts @@ -0,0 +1,35 @@ +interface MaybeResult { + entity_token: string; + entity_type: string; + maybe_public_bucket_media_path: string; + maybe_successfully_completed_at: string; +} + +interface Request { + inference_category: string; + maybe_model_title?: string; + maybe_model_token: string; + maybe_model_type: string; + maybe_raw_inference_text: string; + maybe_style_name: string; +} + +interface Status { + attempt_count: number; + maybe_assigned_cluster?: string; + maybe_assigned_worker?: string; + maybe_extra_status_description?: string; + maybe_failure_category?: string; + maybe_first_started_at: string; + requires_keepalive: boolean; + status: string; +} + +export interface JobState { + created_at: string; + job_token: string; + maybe_result?: MaybeResult; + request: Request; + status: Status; + updated_at: string; +} diff --git a/app/models/Weight.ts b/app/models/Weight.ts new file mode 100644 index 000000000..aec5f5c14 --- /dev/null +++ b/app/models/Weight.ts @@ -0,0 +1,40 @@ +interface CoverImage { + default_cover: { + color_index: number; + image_index: number; + }; + maybe_cover_image_public_bucket_path: string; +} + +interface DefaultAvatar { + color_index: number; + image_index: number; +} + +interface Creator { + default_avatar: DefaultAvatar; + display_name: string; + gravatar_hash: string; + user_token: string; + username: string; +} + +interface Stats { + bookmark_count: number; + positive_rating_count: number; +} + +export interface Weight { + cover_image: CoverImage; + created_at: string; + creator: Creator; + creator_set_visibility: string; + file_checksum_sha2: string; + file_size_bytes: number; + stats: Stats; + title: string; + updated_at: string; + weight_category: string; + weight_token: string; + weight_type: string; +} diff --git a/app/models/index.ts b/app/models/index.ts index 813a20e2b..a08bd301d 100644 --- a/app/models/index.ts +++ b/app/models/index.ts @@ -1,3 +1,6 @@ export * from "./Billing"; +export * from "./BetaKeys"; export * from "./Job"; +export * from "./JobState"; export * from "./Users"; +export * from "./Weight"; diff --git a/app/pages/PageEnigma/PageEditor.tsx b/app/pages/PageEnigma/PageEditor.tsx index ea24f22ba..18259bf33 100644 --- a/app/pages/PageEnigma/PageEditor.tsx +++ b/app/pages/PageEnigma/PageEditor.tsx @@ -10,7 +10,7 @@ import { ControlPanelSceneObject } from "./comps/ControlPanelSceneObject"; import { PreviewEngineCamera } from "./comps/PreviewEngineCamera"; import { PreviewFrameImage } from "./comps/PreviewFrameImage"; import { pageHeight, pageWidth } from "~/signals"; -import { Helmet } from 'react-helmet'; +// import { Helmet } from 'react-helmet'; import { timelineHeight, sidePanelWidth, @@ -53,7 +53,7 @@ export const PageEditor = () => { return (
- + {/* - + */}
{
- + diff --git a/app/pages/PageEnigma/models/index.ts b/app/pages/PageEnigma/models/index.ts index 850f32a2c..83deaffde 100644 --- a/app/pages/PageEnigma/models/index.ts +++ b/app/pages/PageEnigma/models/index.ts @@ -3,8 +3,10 @@ export * from "./inferenceJobs"; export * from "./message"; export * from "./movies"; export * from "./pagination"; +export * from "./prompts"; export * from "./sidePanel"; export * from "./track"; export * from "./tts"; export * from "./types"; +export * from "./userBookmark"; export * from "./voice"; diff --git a/app/pages/PageEnigma/models/prompts.ts b/app/pages/PageEnigma/models/prompts.ts new file mode 100644 index 000000000..fafe2fd39 --- /dev/null +++ b/app/pages/PageEnigma/models/prompts.ts @@ -0,0 +1,16 @@ +export interface Prompts { + created_at: string; + lcm_disabled: boolean; + lipsync_enabled: boolean; + maybe_inference_duration_millis: number; + maybe_moderator_fields: string; + maybe_negative_prompt: string; + maybe_positive_prompt: string; + maybe_strength: number; + maybe_style_name: string; + prompt_type: string; + token: string; + use_cinematic: boolean; + used_face_detailer: boolean; + used_upscaler: boolean; +} diff --git a/app/pages/PageEnigma/models/userBookmark.ts b/app/pages/PageEnigma/models/userBookmark.ts new file mode 100644 index 000000000..2477ffae5 --- /dev/null +++ b/app/pages/PageEnigma/models/userBookmark.ts @@ -0,0 +1,75 @@ +export interface UserBookmarkBatch { + entity_token: string; + entity_type: string; + is_bookmarked: boolean; + maybe_bookmark_token: string; +} + +interface DefaultAvatar { + color_index: number; + image_index: number; +} + +interface User { + default_avatar: DefaultAvatar; + display_name: string; + gravatar_hash: string; + user_token: string; + username: string; +} + +export interface UserBookmarkByEntity { + created_at: string; + token: string; + updated_at: string; + user: User; +} + +interface DefaultAvatar { + color_index: number; + image_index: number; +} + +interface Creator { + default_avatar: DefaultAvatar; + display_name: string; + gravatar_hash: string; + user_token: string; + username: string; +} + +interface MediaFileData { + maybe_creator: Creator; + media_type: string; // Adjust type as per your specific schema + public_bucket_path: string; +} + +interface WeightData { + maybe_cover_image_public_bucket_path: string; + maybe_creator: Creator; + title: string; + weight_category: string; // Adjust type as per your specific schema + weight_type: string; // Adjust type as per your specific schema +} + +interface Stats { + bookmark_count: number; + positive_rating_count: number; +} + +interface Details { + entity_token: string; + entity_type: string; // Adjust type as per your specific schema + maybe_media_file_data?: MediaFileData; + maybe_summary_text?: string; + maybe_thumbnail_url?: string; + maybe_weight_data?: WeightData; + stats: Stats; +} + +export interface UserBookmarkByUser { + created_at: string; + details: Details; + token: string; + updated_at: string; +} diff --git a/app/root.tsx b/app/root.tsx index cea9820b1..0c03a271e 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -54,18 +54,18 @@ export const links: LinksFunction = () => [ ]; export async function loader() { - if (process.env.NODE_ENV === "development") { - const env = { - BASE_API: "", - GOOGLE_API: "", - // @ts-expect-error ProvessEnv is correct - FUNNEL_API: process.env.FUNNEL_API || "%BUILD_FUNNEL_API%", - // @ts-expect-error ProvessEnv is correct - CDN_API: process.env.CDN_API || "%BUILD_CDN_API%", - GRAVATAR_API: "", - } as Record; - return { ENV: env }; - } + // if (process.env.NODE_ENV === "development") { + // const env = { + // BASE_API: "", + // GOOGLE_API: "", + // // @ts-expect-error ProvessEnv is correct + // FUNNEL_API: process.env.FUNNEL_API || "%BUILD_FUNNEL_API%", + // // @ts-expect-error ProvessEnv is correct + // CDN_API: process.env.CDN_API || "%BUILD_CDN_API%", + // GRAVATAR_API: "", + // } as Record; + // return { ENV: env }; + // } const env = { // @ts-expect-error ProvessEnv is correct BASE_API: process.env.BASE_API || "%BUILD_BASE_API%", diff --git a/package-lock.json b/package-lock.json index 7bcb5bfaa..95b3257a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7943,9 +7943,9 @@ } }, "node_modules/@storybook/core-server/node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -26392,9 +26392,9 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/test/app/Classes/ApiManager/AnalyticsApi.test.ts b/test/app/Classes/ApiManager/AnalyticsApi.test.ts new file mode 100644 index 000000000..fd79b5515 --- /dev/null +++ b/test/app/Classes/ApiManager/AnalyticsApi.test.ts @@ -0,0 +1,92 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { AnalyticsApi } from "~/Classes/ApiManager/AnalyticsApi"; + +describe("AnalyticsApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("PostAnalytics", () => { + it("success", async () => { + const api = new AnalyticsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + log_token: "lt1", + success: true, + }); + const response = await api.PostAnalytics({ + maybeLastAction: "mla1", + maybeLogToken: "mlt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/analytics/log_session", + { + method: "POST", + body: { + maybe_last_action: "mla1", + maybe_log_token: "mlt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + data: "lt1", + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new AnalyticsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.PostAnalytics({ + maybeLastAction: "mla1", + maybeLogToken: "mlt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/analytics/log_session", + { + method: "POST", + body: { + maybe_last_action: "mla1", + maybe_log_token: "mlt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new AnalyticsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.PostAnalytics({ + maybeLastAction: "mla1", + maybeLogToken: "mlt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/analytics/log_session", + { + method: "POST", + body: { + maybe_last_action: "mla1", + maybe_log_token: "mlt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/BetaKeysApi.test.ts b/test/app/Classes/ApiManager/BetaKeysApi.test.ts new file mode 100644 index 000000000..6499c01c4 --- /dev/null +++ b/test/app/Classes/ApiManager/BetaKeysApi.test.ts @@ -0,0 +1,401 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { BetaKeysApi } from "~/Classes/ApiManager/BetaKeysApi"; + +const mockBetaKey = { + created_at: "2024-06-16T00:06:04.724Z", + creator: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + is_distributed: true, + key_value: "string", + maybe_note: "string", + maybe_note_html: "string", + maybe_redeemed_at: "2024-06-16T00:06:04.724Z", + maybe_redeemer: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + maybe_referrer: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + product: "studio", + token: "string", +}; + +describe("BetaKeysApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("CreateBetaKey", () => { + it("success", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + beta_keys: ["bk1", "bk2"], + success: true, + }); + const response = await api.CreateBetaKey({ + maybeNote: "mn1", + maybeReferrerUsername: "mru1", + numberOfKeys: 3, + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/create", + { + method: "POST", + body: { + maybe_note: "mn1", + maybe_referrer_username: "mru1", + number_of_keys: 3, + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + data: ["bk1", "bk2"], + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.CreateBetaKey({ + maybeNote: "mn1", + maybeReferrerUsername: "mru1", + numberOfKeys: 3, + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/create", + { + method: "POST", + body: { + maybe_note: "mn1", + maybe_referrer_username: "mru1", + number_of_keys: 3, + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.CreateBetaKey({ + maybeNote: "mn1", + maybeReferrerUsername: "mru1", + numberOfKeys: 3, + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/create", + { + method: "POST", + body: { + maybe_note: "mn1", + maybe_referrer_username: "mru1", + number_of_keys: 3, + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListBetaKeys", () => { + it("no params", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + beta_keys: [mockBetaKey], + pagination: { + current: 0, + total_page_count: 0, + }, + success: true, + }); + const response = await api.ListBetaKeys({}); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/list", + { + method: "GET", + query: { + maybe_referrer_username: undefined, + only_list_remaining: undefined, + page_index: undefined, + page_size: undefined, + sort_ascending: undefined, + }, + }, + ); + expect(response).toEqual({ + success: true, + data: [mockBetaKey], + pagination: { + current: 0, + total_page_count: 0, + }, + errorMessage: undefined, + }); + }); + + it("all params", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + beta_keys: [mockBetaKey], + pagination: { + current: 0, + total_page_count: 0, + }, + success: true, + }); + const response = await api.ListBetaKeys({ + sortAscending: true, + pageSize: 88, + pageIndex: 2, + maybeReferrerUsername: "mru1", + onlyListRemaining: false, + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/list", + { + method: "GET", + query: { + maybe_referrer_username: "mru1", + only_list_remaining: false, + page_index: 2, + page_size: 88, + sort_ascending: true, + }, + }, + ); + expect(response).toEqual({ + success: true, + data: [mockBetaKey], + pagination: { + current: 0, + total_page_count: 0, + }, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.ListBetaKeys({}); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/list", + { + method: "GET", + query: { + maybe_referrer_username: undefined, + only_list_remaining: undefined, + page_index: undefined, + page_size: undefined, + sort_ascending: undefined, + }, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListBetaKeys({}); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/list", + { + method: "GET", + query: { + maybe_referrer_username: undefined, + only_list_remaining: undefined, + page_index: undefined, + page_size: undefined, + sort_ascending: undefined, + }, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("RedeemBetaKey", () => { + it("success", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.RedeemBetaKey({ + betaKey: "bk1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/redeem", + { + method: "POST", + body: { beta_key: "bk1" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.RedeemBetaKey({ + betaKey: "bk1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/redeem", + { + method: "POST", + body: { beta_key: "bk1" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.RedeemBetaKey({ + betaKey: "bk1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/redeem", + { + method: "POST", + body: { beta_key: "bk1" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("UpdateBetaKeyNote", () => { + it("success", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.UpdateBetaKeyNote({ + token: "t1", + note: "a note", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/t1/note", + { + method: "POST", + body: { note: "a note" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.UpdateBetaKeyNote({ + token: "t1", + note: "a note", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/t1/note", + { + method: "POST", + body: { note: "a note" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new BetaKeysApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.UpdateBetaKeyNote({ + token: "t1", + note: "a note", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/beta_keys/t1/note", + { + method: "POST", + body: { note: "a note" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/CommentsApi.test.ts b/test/app/Classes/ApiManager/CommentsApi.test.ts new file mode 100644 index 000000000..00dc56f07 --- /dev/null +++ b/test/app/Classes/ApiManager/CommentsApi.test.ts @@ -0,0 +1,217 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { CommentsApi } from "~/Classes/ApiManager/CommentsApi"; + +describe("CommentsApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("CreateComment", () => { + it("success", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + comment_token: "ct1", + success: true, + }); + const response = await api.CreateComment({ + commentMarkdown: "my comment", + entityToken: "et1", + entityType: "type1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/new", + { + method: "POST", + body: { + comment_markdown: "my comment", + entity_token: "et1", + entity_type: "type1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + data: "ct1", + success: true, + }); + }); + + it("failure", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.CreateComment({ + commentMarkdown: "my comment", + entityToken: "et1", + entityType: "type1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/new", + { + method: "POST", + body: { + comment_markdown: "my comment", + entity_token: "et1", + entity_type: "type1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.CreateComment({ + commentMarkdown: "my comment", + entityToken: "et1", + entityType: "type1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/new", + { + method: "POST", + body: { + comment_markdown: "my comment", + entity_token: "et1", + entity_type: "type1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("DeleteComment", () => { + it("success", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.DeleteComment({ + commentToken: "ct1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/delete/ct1", + { + method: "POST", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + }); + }); + + it("failure", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.DeleteComment({ + commentToken: "ct1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/delete/ct1", + { + method: "POST", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.DeleteComment({ + commentToken: "ct1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/delete/ct1", + { + method: "POST", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListCommentsByEntity", () => { + it("success", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + comments: ["comm1", "comm2"], + }); + const response = await api.ListCommentsByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/list/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: ["comm1", "comm2"], + }); + }); + + it("exception", async () => { + const api = new CommentsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListCommentsByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/comments/list/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/EngineApi.test.ts b/test/app/Classes/ApiManager/EngineApi.test.ts new file mode 100644 index 000000000..4368c8e26 --- /dev/null +++ b/test/app/Classes/ApiManager/EngineApi.test.ts @@ -0,0 +1,91 @@ +import { authentication } from "~/signals"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { JobsApi } from "~/Classes/ApiManager/JobsApi"; +import { UserInfo } from "~/models"; +import { EngineApi } from "~/Classes/ApiManager/EngineApi"; + +describe("EngineApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("FetchRecentJobs", () => { + it("success", async () => { + const api = new EngineApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + inference_job_token: "ijt1", + }); + const response = await api.ConvertTbxToGltf({ + mediaFileToken: "mft1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/conversion/enqueue_fbx_to_gltf", + { + method: "POST", + body: { + media_file_token: "mft1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: "ijt1", + }); + }); + + it("failure", async () => { + const api = new EngineApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.ConvertTbxToGltf({ + mediaFileToken: "mft1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/conversion/enqueue_fbx_to_gltf", + { + method: "POST", + body: { + media_file_token: "mft1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new EngineApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ConvertTbxToGltf({ + mediaFileToken: "mft1", + uuidIdempotencyToken: "uuid", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/conversion/enqueue_fbx_to_gltf", + { + method: "POST", + body: { + media_file_token: "mft1", + uuid_idempotency_token: "uuid", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/FeaturedItemsApi.test.ts b/test/app/Classes/ApiManager/FeaturedItemsApi.test.ts new file mode 100644 index 000000000..ba8e5a1ee --- /dev/null +++ b/test/app/Classes/ApiManager/FeaturedItemsApi.test.ts @@ -0,0 +1,236 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { FeaturedItemsApi } from "~/Classes/ApiManager/FeaturedItemsApi"; + +describe("FeaturedItemsApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("CreateFeaturedItem", () => { + it("success", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + is_featured: true, + success: true, + }); + const response = await api.CreateFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.CreateFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.CreateFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("DeleteFeaturedItem", () => { + it("success", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.DeleteFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/delete", + { + method: "DELETE", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + }); + }); + + it("failure", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.DeleteFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/delete", + { + method: "DELETE", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.DeleteFeaturedItem({ + entityToken: "et1", + entityType: "type1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/delete", + { + method: "DELETE", + body: { + entity_token: "et1", + entity_type: "type1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("CheckFeaturedItem", () => { + it("success", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + is_featured: true, + success: true, + }); + const response = await api.CheckFeaturedItem({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/is_featured/et1/tok1", + { + method: "POST", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.CheckFeaturedItem({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/is_featured/et1/tok1", + { + method: "POST", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new FeaturedItemsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.CheckFeaturedItem({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/featured_item/is_featured/et1/tok1", + { + method: "POST", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/JobsApi.test.ts b/test/app/Classes/ApiManager/JobsApi.test.ts index 0e8be3896..aa0ec521d 100644 --- a/test/app/Classes/ApiManager/JobsApi.test.ts +++ b/test/app/Classes/ApiManager/JobsApi.test.ts @@ -32,6 +32,35 @@ const mockJob = { }, updated_at: "2024-06-13T13:28:18.610Z", }; +const mockJobState = { + created_at: "2024-06-16T11:43:29.304Z", + job_token: "string", + maybe_result: { + entity_token: "string", + entity_type: "string", + maybe_public_bucket_media_path: "string", + maybe_successfully_completed_at: "2024-06-16T11:43:29.304Z", + }, + request: { + inference_category: "lipsync_animation", + maybe_model_title: "string", + maybe_model_token: "string", + maybe_model_type: "string", + maybe_raw_inference_text: "string", + maybe_style_name: "anime_2_5d", + }, + status: { + attempt_count: 0, + maybe_assigned_cluster: "string", + maybe_assigned_worker: "string", + maybe_extra_status_description: "string", + maybe_failure_category: "face_not_detected", + maybe_first_started_at: "2024-06-16T11:43:29.305Z", + requires_keepalive: true, + status: "pending", + }, + updated_at: "2024-06-16T11:43:29.305Z", +}; describe("JobsApi", () => { beforeAll(() => { @@ -41,15 +70,97 @@ describe("JobsApi", () => { } as UserInfo; EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); }); - describe("FetchRecentJobs", () => { + describe("GetJobByToken", () => { it("fetch data", async () => { - const jobsApi = new JobsApi(); - jest.spyOn(jobsApi, "fetch").mockResolvedValue({ + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + state: mockJobState, + }); + const response = await api.GetJobByToken({ token: "tok1" }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/jobs/job/tok1", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: mockJobState, + }); + }); + + it("exception", async () => { + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.GetJobByToken({ token: "tok1" }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/jobs/job/tok1", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListJobs", () => { + it("fetch data", async () => { + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + job_states: [mockJobState], + }); + const response = await api.ListJobs(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/jobs/batch", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: [mockJobState], + }); + }); + + it("exception", async () => { + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListJobs(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/jobs/batch", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListRecentJobs", () => { + it("fetch data", async () => { + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ success: true, jobs: [mockJob], }); - const response = await jobsApi.ListRecentJobs(); - expect(jobsApi.fetch).toHaveBeenCalledWith( + const response = await api.ListRecentJobs(); + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/jobs/session", { method: "GET", @@ -64,10 +175,10 @@ describe("JobsApi", () => { }); it("exception", async () => { - const jobsApi = new JobsApi(); - jest.spyOn(jobsApi, "fetch").mockRejectedValue(new Error("server error")); - const response = await jobsApi.ListRecentJobs(); - expect(jobsApi.fetch).toHaveBeenCalledWith( + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListRecentJobs(); + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/jobs/session", { method: "GET", @@ -84,12 +195,12 @@ describe("JobsApi", () => { describe("DeleteJob", () => { it("success", async () => { - const jobsApi = new JobsApi(); - jest.spyOn(jobsApi, "fetch").mockResolvedValue({ + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ success: true, }); - const response = await jobsApi.DeleteJobByToken("token1"); - expect(jobsApi.fetch).toHaveBeenCalledWith( + const response = await api.DeleteJobByToken("token1"); + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/jobs/job/token1", { method: "DELETE", @@ -103,10 +214,10 @@ describe("JobsApi", () => { }); it("exception", async () => { - const jobsApi = new JobsApi(); - jest.spyOn(jobsApi, "fetch").mockRejectedValue(new Error("server error")); - const response = await jobsApi.DeleteJobByToken("token1"); - expect(jobsApi.fetch).toHaveBeenCalledWith( + const api = new JobsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.DeleteJobByToken("token1"); + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/jobs/job/token1", { method: "DELETE", diff --git a/test/app/Classes/ApiManager/MediaFileApi.test.ts b/test/app/Classes/ApiManager/MediaFileApi.test.ts index 54c385205..71009add0 100644 --- a/test/app/Classes/ApiManager/MediaFileApi.test.ts +++ b/test/app/Classes/ApiManager/MediaFileApi.test.ts @@ -1,12 +1,13 @@ +import { MediaFilesApi } from "~/Classes/ApiManager/MediaFilesApi"; +import { authentication } from "~/signals"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { UserInfo } from "~/models"; import { FilterEngineCategories, FilterMediaClasses, FilterMediaType, - MediaFilesApi, -} from "~/Classes/ApiManager/MediaFilesApi"; -import { authentication } from "~/signals"; -import EnvironmentVariables from "~/Classes/EnvironmentVariables"; -import { UserInfo } from "~/models"; + Visibility, +} from "~/enums"; describe("MediaFilesApi", () => { beforeAll(() => { @@ -76,7 +77,84 @@ describe("MediaFilesApi", () => { updated_at: "2024-06-13T12:33:48.693Z", }; - describe("FetchBatch", () => { + describe("DeleteMediaFileByToken", () => { + it("success", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + success: true, + }); + const response = await mediaFilesApi.DeleteMediaFileByToken({ + mediaFileToken: "mt1", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/file/mt1", + { + method: "DELETE", + body: { + as_mod: true, + set_delete: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + BadInput: "Bad Input", + }); + const response = await mediaFilesApi.DeleteMediaFileByToken({ + mediaFileToken: "mt1", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/file/mt1", + { + method: "DELETE", + body: { + as_mod: true, + set_delete: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "Bad Input", + }); + }); + + it("exception", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest + .spyOn(mediaFilesApi, "fetch") + .mockRejectedValue(new Error("server error")); + const response = await mediaFilesApi.DeleteMediaFileByToken({ + mediaFileToken: "mt1", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/file/mt1", + { + method: "DELETE", + body: { + as_mod: true, + set_delete: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListMediaFilesByTokens", () => { it("success", async () => { const mediaFilesApi = new MediaFilesApi(); jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ @@ -127,7 +205,7 @@ describe("MediaFilesApi", () => { }); }); - describe("LoadMediaFile", () => { + describe("GetMediaFileByToken", () => { it("success", async () => { const mediaFilesApi = new MediaFilesApi(); jest @@ -174,6 +252,159 @@ describe("MediaFilesApi", () => { }); }); + describe("ListMediaFiles", () => { + it("no parameters", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + results: [mediaFile], + success: true, + }); + const response = await mediaFilesApi.ListMediaFiles({}); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/list", + { + method: "GET", + query: { + filter_engine_categories: undefined, + filter_media_classes: undefined, + filter_media_type: undefined, + }, + }, + ); + expect(response).toEqual({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + errorMessage: undefined, + success: true, + data: [mediaFile], + }); + }); + + it("page_size and filter_engine_categories", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + results: [mediaFile], + success: true, + }); + const response = await mediaFilesApi.ListMediaFiles({ + page_size: 22, + filter_engine_categories: [ + FilterEngineCategories.ANIMATION, + FilterEngineCategories.CHARACTER, + ], + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/list", + { + method: "GET", + query: { + filter_engine_categories: "animation,character", + filter_media_classes: undefined, + filter_media_type: undefined, + page_size: 22, + }, + }, + ); + expect(response).toEqual({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + success: true, + data: [mediaFile], + }); + }); + + it("with a bunch", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + results: [mediaFile], + success: true, + }); + const response = await mediaFilesApi.ListMediaFiles({ + sort_ascending: true, + cursor: "cursor", + cursor_is_reversed: false, + filter_media_classes: [FilterMediaClasses.AUDIO], + filter_media_type: [FilterMediaType.GLB, FilterMediaType.GLTF], + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/list", + { + method: "GET", + query: { + filter_engine_categories: undefined, + filter_media_classes: "audio", + filter_media_type: "glb,gltf", + sort_ascending: true, + cursor: "cursor", + cursor_is_reversed: false, + }, + }, + ); + expect(response).toEqual({ + pagination: { + cursor_is_reversed: false, + maybe_next: "mn1", + maybe_previous: "mp1", + }, + success: true, + data: [mediaFile], + }); + }); + + it("exception", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest + .spyOn(mediaFilesApi, "fetch") + .mockRejectedValue(new Error("server error")); + const response = await mediaFilesApi.ListMediaFiles({ + sort_ascending: true, + cursor: "cursor", + cursor_is_reversed: false, + filter_media_classes: [FilterMediaClasses.AUDIO], + filter_media_type: [FilterMediaType.GLB, FilterMediaType.GLTF], + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/list", + { + method: "GET", + query: { + filter_engine_categories: undefined, + filter_media_classes: "audio", + filter_media_type: "glb,gltf", + sort_ascending: true, + cursor: "cursor", + cursor_is_reversed: false, + }, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + describe("ListUserMediaFiles", () => { it("no parameters", async () => { const mediaFilesApi = new MediaFilesApi(); @@ -469,4 +700,201 @@ describe("MediaFilesApi", () => { }); }); }); + + describe("RenameMediaFileByToken", () => { + it("success", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest.spyOn(mediaFilesApi, "fetch").mockResolvedValueOnce({ + success: true, + }); + const response = await mediaFilesApi.RenameMediaFileByToken({ + mediaToken: "mt1", + name: "new name", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_file/rename/mt1", + { + method: "POST", + body: { name: "new name" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest + .spyOn(mediaFilesApi, "fetch") + .mockResolvedValue({ BadInput: "bad input" }); + const response = await mediaFilesApi.RenameMediaFileByToken({ + mediaToken: "mt1", + name: "new name", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_file/rename/mt1", + { + method: "POST", + body: { name: "new name" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const mediaFilesApi = new MediaFilesApi(); + jest + .spyOn(mediaFilesApi, "fetch") + .mockRejectedValue(new Error("server error")); + const response = await mediaFilesApi.RenameMediaFileByToken({ + mediaToken: "mt1", + name: "new name", + }); + expect(mediaFilesApi.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_file/rename/mt1", + { + method: "POST", + body: { name: "new name" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("UpdateCoverImage", () => { + it("success", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + success: true, + }); + const response = await api.UpdateCoverImage({ + mediaFileToken: "mft1", + imageToken: "it1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/cover_image/mft1", + { + method: "POST", + body: { cover_image_media_file_token: "it1" }, + query: undefined, + }, + ); + expect(response).toEqual({ success: true }); + }); + + it("failure", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + BadInput: "bi1", + }); + const response = await api.UpdateCoverImage({ + mediaFileToken: "mft1", + imageToken: "it1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/cover_image/mft1", + { + method: "POST", + body: { cover_image_media_file_token: "it1" }, + query: undefined, + }, + ); + expect(response).toEqual({ success: false, errorMessage: "bi1" }); + }); + + it("exception", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.UpdateCoverImage({ + mediaFileToken: "mft1", + imageToken: "it1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/cover_image/mft1", + { + method: "POST", + body: { cover_image_media_file_token: "it1" }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("UpdateVisibility", () => { + it("success", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + success: true, + }); + const response = await api.UpdateVisibility({ + mediaFileToken: "mft1", + visibility: Visibility.Hidden, + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/visibility/mft1", + { + method: "POST", + body: { creator_set_visibility: Visibility.Hidden }, + query: undefined, + }, + ); + expect(response).toEqual({ success: true }); + }); + + it("failure", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + BadInput: "bi1", + }); + const response = await api.UpdateVisibility({ + mediaFileToken: "mft1", + visibility: Visibility.Hidden, + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/visibility/mft1", + { + method: "POST", + body: { creator_set_visibility: Visibility.Hidden }, + query: undefined, + }, + ); + expect(response).toEqual({ success: false, errorMessage: "bi1" }); + }); + + it("exception", async () => { + const api = new MediaFilesApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.UpdateVisibility({ + mediaFileToken: "mft1", + visibility: Visibility.Hidden, + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/visibility/mft1", + { + method: "POST", + body: { creator_set_visibility: Visibility.Hidden }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); }); diff --git a/test/app/Classes/ApiManager/MediaUploadApi.test.ts b/test/app/Classes/ApiManager/MediaUploadApi.test.ts index d28746796..46c646a3b 100644 --- a/test/app/Classes/ApiManager/MediaUploadApi.test.ts +++ b/test/app/Classes/ApiManager/MediaUploadApi.test.ts @@ -4,78 +4,251 @@ import { MediaUploadApi } from "~/Classes/ApiManager/MediaUploadApi"; import { Visibility } from "~/enums"; import { UserInfo } from "~/models"; +EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + describe("MediaUploadApi", () => { beforeAll(() => { authentication.userInfo.value = { user_token: "un1", username: "un1", } as UserInfo; - EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); }); - describe("UploadImage", () => { - it("no parameters", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - media_file_token: "mft1", - success: true, - }); - const blob = new Blob(["my blob data"]); - const uuid = "some uuid"; - const response = await mediaUploadApi.UploadImage({ - blob: blob, - fileName: "some file name", - uuid, - }); - const formData = new FormData(); - formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); - formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/image", - { - method: "POST", - body: formData, + describe("run tests", () => { + const api = new MediaUploadApi(); + [ + { + name: "UploadAudio", + function: api.UploadAudio.bind(api), + endpoint: "http://localhost:3000/v1/media_files/upload/audio", + is_intermediate_system_file: false, + params: { + maybe_title: "title", + maybe_visibility: Visibility.Private, }, - ); - expect(response).toEqual({ - data: "mft1", - success: true, - errorMessage: undefined, + }, + { + name: "UploadImage", + function: api.UploadImage.bind(api), + endpoint: "http://localhost:3000/v1/media_files/upload/image", + is_intermediate_system_file: true, + params: { + maybe_title: "title", + maybe_visibility: Visibility.Private, + }, + }, + { + name: "UploadNewEngineAsset", + function: api.UploadNewEngineAsset.bind(api), + endpoint: + "http://localhost:3000/v1/media_files/upload/new_engine_asset", + is_intermediate_system_file: false, + params: { + maybe_title: "title", + maybe_visibility: Visibility.Private, + maybe_animation_type: "animate", + maybe_duration_millis: 55, + }, + }, + { + name: "UploadNewScene", + function: api.UploadNewScene.bind(api), + endpoint: "http://localhost:3000/v1/media_files/upload/new_scene", + is_intermediate_system_file: false, + params: { + maybe_title: "title", + maybe_visibility: Visibility.Private, + }, + }, + { + name: "UploadNewVideo", + function: api.UploadNewVideo.bind(api), + endpoint: "http://localhost:3000/v1/media_files/upload/new_video", + is_intermediate_system_file: true, + params: { + maybe_title: "title", + maybe_visibility: Visibility.Private, + maybe_style_name: "style", + maybe_scene_source_media_file_token: "ssmft1", + }, + }, + { + name: "UploadPmx", + function: api.UploadPmx.bind(api), + endpoint: "http://localhost:3000/v1/media_files/upload/pmx", + is_intermediate_system_file: true, + params: { + engine_category: "ec1", + maybe_animation_type: "animate", + maybe_duration_millis: 77, + maybe_title: "a title", + maybe_visibility: Visibility.Private, + }, + }, + ].forEach((testMethod) => { + describe(testMethod.name, () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("no parameters", async () => { + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + media_file_token: "mft1", + success: true, + }); + const blob = new Blob(["my blob data"]); + const uuid = "some uuid"; + const response = await testMethod.function({ + blob: blob, + fileName: "some file name", + uuid, + }); + const formData = new FormData(); + formData.append("uuid_idempotency_token", uuid); + Object.entries({ + ...(testMethod.is_intermediate_system_file + ? { is_intermediate_system_file: "true" } + : {}), + maybe_visibility: Visibility.Public, + }).forEach(([key, value]) => { + formData.append(key, value); + }); + formData.append("file", blob, "some file name"); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: formData, + }); + expect(response).toEqual({ + data: "mft1", + success: true, + errorMessage: undefined, + }); + }); + + it("all parameters", async () => { + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + media_file_token: "mft1", + success: true, + }); + const blob = new Blob(["my blob data"]); + const uuid = "some uuid"; + const response = await testMethod.function({ + blob: blob, + fileName: "some file name", + uuid, + ...testMethod.params, + }); + const formData = new FormData(); + formData.append("uuid_idempotency_token", uuid); + Object.entries({ + ...(testMethod.is_intermediate_system_file + ? { is_intermediate_system_file: "true" } + : {}), + ...testMethod.params, + }).forEach(([key, value]) => { + if (value !== undefined) { + formData.append(key, value.toString()); + } + }); + formData.append("file", blob, "some file name"); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: formData, + }); + expect(response).toEqual({ + data: "mft1", + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + jest.spyOn(api, "fetch").mockResolvedValueOnce({ + BadInput: "error error", + }); + const blob = new Blob(["my blob data"]); + const uuid = "some uuid"; + const response = await testMethod.function({ + blob: blob, + fileName: "some file name", + uuid, + }); + const formData = new FormData(); + formData.append("uuid_idempotency_token", uuid); + Object.entries({ + ...(testMethod.is_intermediate_system_file + ? { is_intermediate_system_file: "true" } + : {}), + maybe_visibility: Visibility.Public, + }).forEach(([key, value]) => { + formData.append(key, value); + }); + formData.append("file", blob, "some file name"); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: formData, + }); + expect(response).toEqual({ + data: undefined, + success: false, + errorMessage: "error error", + }); + }); + + it("exception", async () => { + jest.spyOn(api, "fetch").mockRejectedValue(new Error("error error")); + const blob = new Blob(["my blob data"]); + const uuid = "some uuid"; + const response = await testMethod.function({ + blob: blob, + fileName: "some file name", + uuid, + }); + const formData = new FormData(); + formData.append("uuid_idempotency_token", uuid); + Object.entries({ + ...(testMethod.is_intermediate_system_file + ? { is_intermediate_system_file: "true" } + : {}), + maybe_visibility: Visibility.Public, + }).forEach(([key, value]) => { + formData.append(key, value); + }); + formData.append("file", blob, "some file name"); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: formData, + }); + expect(response).toEqual({ + data: undefined, + success: false, + errorMessage: "error error", + }); + }); }); }); - it("all parameters", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ + }); + + describe("UploadSavedScene", () => { + it("success", async () => { + const api = new MediaUploadApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ media_file_token: "mft1", success: true, }); const blob = new Blob(["my blob data"]); const uuid = "some uuid"; - const response = await mediaUploadApi.UploadImage({ + const response = await api.UploadSavedScene({ blob: blob, fileName: "some file name", uuid, - title: "title", - visibility: Visibility.Private, + mediaToken: "mt1", }); const formData = new FormData(); formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_title: "title", - maybe_visibility: Visibility.Private, - }).forEach(([key, value]) => { - formData.append(key, value); - }); formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/image", + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/upload/saved_scene/mt1", { method: "POST", body: formData, @@ -89,28 +262,23 @@ describe("MediaUploadApi", () => { }); it("failure", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ + const api = new MediaUploadApi(); + jest.spyOn(api, "fetch").mockResolvedValueOnce({ BadInput: "error error", }); const blob = new Blob(["my blob data"]); const uuid = "some uuid"; - const response = await mediaUploadApi.UploadImage({ + const response = await api.UploadSavedScene({ blob: blob, fileName: "some file name", uuid, + mediaToken: "mt1", }); const formData = new FormData(); formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/image", + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/upload/saved_scene/mt1", { method: "POST", body: formData, @@ -124,28 +292,21 @@ describe("MediaUploadApi", () => { }); it("exception", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest - .spyOn(mediaUploadApi, "fetch") - .mockRejectedValue(new Error("error error")); + const api = new MediaUploadApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("error error")); const blob = new Blob(["my blob data"]); const uuid = "some uuid"; - const response = await mediaUploadApi.UploadImage({ + const response = await api.UploadSavedScene({ blob: blob, fileName: "some file name", uuid, + mediaToken: "mt1", }); const formData = new FormData(); formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/image", + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/media_files/upload/saved_scene/mt1", { method: "POST", body: formData, @@ -158,200 +319,4 @@ describe("MediaUploadApi", () => { }); }); }); - - describe("UploadVideo", () => { - it("success all parameters", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - media_file_token: "mft2", - success: true, - }); - const blob = new Blob(["my blob data"]); - const uuid = "some uuid"; - const response = await mediaUploadApi.UploadVideo({ - blob: blob, - fileName: "some file name", - uuid, - title: "title", - styleName: "style", - sceneSourceMediaFileToken: "ssmft1", - }); - const formData = new FormData(); - formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_title: "title", - maybe_visibility: Visibility.Public, - maybe_style_name: "style", - maybe_scene_source_media_file_token: "ssmft1", - }).forEach(([key, value]) => { - formData.append(key, value); - }); - formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/new_video", - { - method: "POST", - body: formData, - }, - ); - expect(response).toEqual({ - success: true, - data: "mft2", - errorMessage: undefined, - }); - }); - - it("success few parameters", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - media_file_token: "mft2", - success: true, - }); - const blob = new Blob(["my blob data"]); - const uuid = "some uuid"; - const response = await mediaUploadApi.UploadVideo({ - blob: blob, - fileName: "some file name", - uuid, - title: "title", - }); - const formData = new FormData(); - formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_title: "title", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); - formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/new_video", - { - method: "POST", - body: formData, - }, - ); - expect(response).toEqual({ - success: true, - data: "mft2", - errorMessage: undefined, - }); - }); - - it("failure", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - BadInput: "error", - }); - const blob = new Blob(["my blob data"]); - const uuid = "some uuid"; - const response = await mediaUploadApi.UploadVideo({ - blob: blob, - fileName: "some file name", - uuid, - title: "title", - }); - const formData = new FormData(); - formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_title: "title", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); - formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/new_video", - { - method: "POST", - body: formData, - }, - ); - expect(response).toEqual({ - success: false, - data: undefined, - errorMessage: "error", - }); - }); - - it("exception", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockRejectedValue(new Error("error")); - const blob = new Blob(["my blob data"]); - const uuid = "some uuid"; - const response = await mediaUploadApi.UploadVideo({ - blob: blob, - fileName: "some file name", - uuid, - title: "title", - }); - const formData = new FormData(); - formData.append("uuid_idempotency_token", uuid); - Object.entries({ - is_intermediate_system_file: "true", - maybe_title: "title", - maybe_visibility: Visibility.Public, - }).forEach(([key, value]) => { - formData.append(key, value); - }); - formData.append("file", blob, "some file name"); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/upload/new_video", - { - method: "POST", - body: formData, - }, - ); - expect(response).toEqual({ - success: false, - data: undefined, - errorMessage: "error", - }); - }); - }); - - describe("UploadThumbnail", () => { - it("success", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - success: true, - }); - const response = await mediaUploadApi.UploadThumbnail({ - mediaFileToken: "mft1", - imageToken: "it1", - }); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/cover_image/mft1", - { - method: "POST", - body: { cover_image_media_file_token: "it1" }, - query: undefined, - }, - ); - expect(response).toEqual({ success: true }); - }); - - it("failure", async () => { - const mediaUploadApi = new MediaUploadApi(); - jest.spyOn(mediaUploadApi, "fetch").mockResolvedValueOnce({ - BadInput: "bi1", - }); - const response = await mediaUploadApi.UploadThumbnail({ - mediaFileToken: "mft1", - imageToken: "it1", - }); - expect(mediaUploadApi.fetch).toHaveBeenCalledWith( - "http://localhost:3000/v1/media_files/cover_image/mft1", - { - method: "POST", - body: { cover_image_media_file_token: "it1" }, - query: undefined, - }, - ); - expect(response).toEqual({ success: false, errorMessage: "bi1" }); - }); - }); }); diff --git a/test/app/Classes/ApiManager/ModerationApi.test.ts b/test/app/Classes/ApiManager/ModerationApi.test.ts new file mode 100644 index 000000000..8369c1d92 --- /dev/null +++ b/test/app/Classes/ApiManager/ModerationApi.test.ts @@ -0,0 +1,100 @@ +import { authentication } from "~/signals"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { UserInfo } from "~/models"; +import { ModerationApi } from "~/Classes/ApiManager/ModerationApi"; + +describe("ModerationApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("UpdateFeatureFlagsByToken", () => { + it("success", async () => { + const api = new ModerationApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.UpdateFeatureFlagsByToken({ + usernameOrToken: "token", + flags: ["flag1", "flag2"], + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/moderation/user_feature_flags/token", + { + method: "POST", + body: { + action: { + AddFlags: { + flags: ["flag1", "flag2"], + }, + }, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new ModerationApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + BadInput: "error", + }); + const response = await api.UpdateFeatureFlagsByToken({ + usernameOrToken: "token", + flags: ["flag1", "flag2"], + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/moderation/user_feature_flags/token", + { + method: "POST", + body: { + action: { + AddFlags: { + flags: ["flag1", "flag2"], + }, + }, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "error", + }); + }); + + it("exception", async () => { + const api = new ModerationApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("error")); + const response = await api.UpdateFeatureFlagsByToken({ + usernameOrToken: "token", + flags: ["flag1", "flag2"], + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/moderation/user_feature_flags/token", + { + method: "POST", + body: { + action: { + AddFlags: { + flags: ["flag1", "flag2"], + }, + }, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/PromptsApi.test.ts b/test/app/Classes/ApiManager/PromptsApi.test.ts new file mode 100644 index 000000000..0c0b6340d --- /dev/null +++ b/test/app/Classes/ApiManager/PromptsApi.test.ts @@ -0,0 +1,71 @@ +import { authentication } from "~/signals"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { UserInfo } from "~/models"; +import { PromptsApi } from "~/Classes/ApiManager/PromptsApi"; + +const mockPrompts = { + created_at: "2024-06-14T17:33:39.283Z", + lcm_disabled: true, + lipsync_enabled: true, + maybe_inference_duration_millis: 0, + maybe_moderator_fields: "string", + maybe_negative_prompt: "string", + maybe_positive_prompt: "string", + maybe_strength: 0, + maybe_style_name: "anime_2_5d", + prompt_type: "stable_diffusion", + token: "string", + use_cinematic: true, + used_face_detailer: true, + used_upscaler: true, +}; + +describe("PromptsApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("FetchRecentJobs", () => { + it("fetch data", async () => { + const api = new PromptsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + prompt: mockPrompts, + }); + const response = await api.GetPromptsByToken({ token: "t1" }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/prompts/t1", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: mockPrompts, + }); + }); + + it("exception", async () => { + const api = new PromptsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.GetPromptsByToken({ token: "t1" }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/prompts/t1", + { + method: "GET", + body: undefined, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/TtsApi.test.ts b/test/app/Classes/ApiManager/TtsApi.test.ts index 09c981801..a745535be 100644 --- a/test/app/Classes/ApiManager/TtsApi.test.ts +++ b/test/app/Classes/ApiManager/TtsApi.test.ts @@ -14,19 +14,19 @@ describe("TtsApi", () => { }); describe("GenerateTtsAudio", () => { it("success no visibility", async () => { - const ttsApi = new TtsApi(); - jest.spyOn(ttsApi, "fetch").mockResolvedValue({ + const api = new TtsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ inference_job_token: "ijt1", inference_job_token_type: "ijtt1", success: true, }); - const response = await ttsApi.GenerateTtsAudio({ + const response = await api.GenerateTtsAudio({ uuid_idempotency_token: "uit1", tts_model_token: "tmt1", inference_text: "inference text", is_storyteller_demo: false, }); - expect(ttsApi.fetch).toHaveBeenCalledWith( + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/tts/inference", { method: "POST", @@ -50,20 +50,20 @@ describe("TtsApi", () => { }); it("success visibility", async () => { - const ttsApi = new TtsApi(); - jest.spyOn(ttsApi, "fetch").mockResolvedValue({ + const api = new TtsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ inference_job_token: "ijt1", inference_job_token_type: "ijtt1", success: true, }); - const response = await ttsApi.GenerateTtsAudio({ + const response = await api.GenerateTtsAudio({ creator_set_visibility: Visibility.Private, uuid_idempotency_token: "uit1", tts_model_token: "tmt1", inference_text: "inference text", is_storyteller_demo: false, }); - expect(ttsApi.fetch).toHaveBeenCalledWith( + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/tts/inference", { method: "POST", @@ -87,17 +87,17 @@ describe("TtsApi", () => { }); it("failure", async () => { - const ttsApi = new TtsApi(); - jest.spyOn(ttsApi, "fetch").mockResolvedValue({ + const api = new TtsApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "error", }); - const response = await ttsApi.GenerateTtsAudio({ + const response = await api.GenerateTtsAudio({ uuid_idempotency_token: "uit1", tts_model_token: "tmt1", inference_text: "inference text", is_storyteller_demo: false, }); - expect(ttsApi.fetch).toHaveBeenCalledWith( + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/tts/inference", { method: "POST", @@ -119,15 +119,15 @@ describe("TtsApi", () => { }); it("exception", async () => { - const ttsApi = new TtsApi(); - jest.spyOn(ttsApi, "fetch").mockRejectedValue(new Error("error")); - const response = await ttsApi.GenerateTtsAudio({ + const api = new TtsApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("error")); + const response = await api.GenerateTtsAudio({ uuid_idempotency_token: "uit1", tts_model_token: "tmt1", inference_text: "inference text", is_storyteller_demo: false, }); - expect(ttsApi.fetch).toHaveBeenCalledWith( + expect(api.fetch).toHaveBeenCalledWith( "http://localhost:3000/v1/tts/inference", { method: "POST", diff --git a/test/app/Classes/ApiManager/UserBookmarksApi.test.ts b/test/app/Classes/ApiManager/UserBookmarksApi.test.ts new file mode 100644 index 000000000..cc54dfec4 --- /dev/null +++ b/test/app/Classes/ApiManager/UserBookmarksApi.test.ts @@ -0,0 +1,477 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { + ScopedEntityTypes, + ScopedMediaFileType, + ScopedWeightTypes, + UserBookmarksApi, +} from "~/Classes/ApiManager/UserBookmarksApi"; + +const mockBookmark = { + created_at: "2024-06-15T11:15:26.997Z", + details: { + entity_token: "string", + entity_type: "user", + maybe_media_file_data: { + maybe_creator: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + media_type: "audio", + public_bucket_path: "string", + }, + maybe_summary_text: "string", + maybe_thumbnail_url: "string", + maybe_weight_data: { + maybe_cover_image_public_bucket_path: "string", + maybe_creator: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + title: "string", + weight_category: "image_generation", + weight_type: "hifigan_tt2", + }, + stats: { + bookmark_count: 0, + positive_rating_count: 0, + }, + }, + token: "string", + updated_at: "2024-06-15T11:15:26.997Z", +}; + +describe("UserBookmarksApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("CreateUserBookmark", () => { + it("success", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + new_bookmark_count_for_entity: 0, + success: true, + user_bookmark_token: "ubt1", + }); + const response = await api.CreateUserBookmark({ + entityToken: "et1", + entityType: "entt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + data: { + new_bookmark_count_for_entity: 0, + user_bookmark_token: "ubt1", + }, + success: true, + }); + }); + + it("failure", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.CreateUserBookmark({ + entityToken: "et1", + entityType: "entt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: { + new_bookmark_count_for_entity: undefined, + user_bookmark_token: undefined, + }, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.CreateUserBookmark({ + entityToken: "et1", + entityType: "entt1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/create", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("DeleteUserBookmark", () => { + it("success", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await api.DeleteUserBookmark({ + entityToken: "et1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/delete/et1", + { + method: "DELETE", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + }); + }); + + it("failure", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.DeleteUserBookmark({ + entityToken: "et1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/delete/et1", + { + method: "DELETE", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.DeleteUserBookmark({ + entityToken: "et1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/delete/et1", + { + method: "DELETE", + body: { + as_mod: true, + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListUserBookmarks", () => { + it("success", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + bookmarks: [ + { + entity_token: "string", + entity_type: "user", + is_bookmarked: true, + maybe_bookmark_token: "string", + }, + ], + success: true, + }); + const response = await api.ListUserBookmarks(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: [ + { + entity_token: "string", + entity_type: "user", + is_bookmarked: true, + maybe_bookmark_token: "string", + }, + ], + }); + }); + + it("failure", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.ListUserBookmarks(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListUserBookmarks(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListUserBookmarksByUser", () => { + it("no params", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + pagination: { + current: 0, + total_page_count: 0, + }, + results: [mockBookmark], + success: true, + }); + const response = await api.ListUserBookmarksByUser({}); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/list/un1", + { + method: "GET", + query: { + maybe_scoped_entity_type: undefined, + maybe_scoped_media_file_type: undefined, + maybe_scoped_weight_type: undefined, + page_index: undefined, + page_size: undefined, + sort_ascending: undefined, + }, + }, + ); + expect(response).toEqual({ + success: true, + data: [mockBookmark], + pagination: { + current: 0, + total_page_count: 0, + }, + }); + }); + + it("all params", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + pagination: { + current: 0, + total_page_count: 0, + }, + results: [mockBookmark], + success: true, + }); + const response = await api.ListUserBookmarksByUser({ + username: "user1", + sort_ascending: true, + page_size: 76, + page_index: 2, + maybe_scoped_entity_type: [ScopedEntityTypes.MEDIA_FILE], + maybe_scoped_weight_type: [ + ScopedWeightTypes.COMFY_UI, + ScopedWeightTypes.SDXL, + ], + maybe_scoped_media_file_type: [ + ScopedMediaFileType.IMAGE_GENERATION, + ScopedMediaFileType.TEXT_TO_SPEECH, + ScopedMediaFileType.WORKFLOW_CONFIG, + ], + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/list/user1", + { + method: "GET", + query: { + maybe_scoped_entity_type: "media_file", + maybe_scoped_media_file_type: + "image_generation,text_to_speech,workflow_config", + maybe_scoped_weight_type: "comfy_ui,sdxl", + page_index: 2, + page_size: 76, + sort_ascending: true, + }, + }, + ); + expect(response).toEqual({ + success: true, + data: [mockBookmark], + pagination: { + current: 0, + total_page_count: 0, + }, + }); + }); + + it("exception", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListUserBookmarksByUser({}); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/list/un1", + { + method: "GET", + query: { + maybe_scoped_entity_type: undefined, + maybe_scoped_media_file_type: undefined, + maybe_scoped_weight_type: undefined, + page_index: undefined, + page_size: undefined, + sort_ascending: undefined, + }, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListUserBookmarksByEntity", () => { + it("success", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + user_bookmarks: [ + { + created_at: "2024-06-15T11:34:56.678Z", + token: "string", + updated_at: "2024-06-15T11:34:56.678Z", + user: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + }, + ], + }); + const response = await api.ListUserBookmarksByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/list/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: [ + { + created_at: "2024-06-15T11:34:56.678Z", + token: "string", + updated_at: "2024-06-15T11:34:56.678Z", + user: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + }, + ], + }); + }); + + it("exception", async () => { + const api = new UserBookmarksApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListUserBookmarksByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_bookmarks/list/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/UserRatingApi.test.ts b/test/app/Classes/ApiManager/UserRatingApi.test.ts new file mode 100644 index 000000000..d5fe66bdb --- /dev/null +++ b/test/app/Classes/ApiManager/UserRatingApi.test.ts @@ -0,0 +1,233 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { UserRatingApi } from "~/Classes/ApiManager/UserRatingsApi"; + +describe("UserBookmarksApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + }); + describe("PostUserRating", () => { + it("success", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + new_positive_rating_count_for_entity: 22, + success: true, + }); + const response = await api.PostUserRating({ + entityToken: "et1", + entityType: "entt1", + ratingValue: "rate1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/rate", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + rating_value: "rate1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + data: 22, + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.PostUserRating({ + entityToken: "et1", + entityType: "entt1", + ratingValue: "rate1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/rate", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + rating_value: "rate1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.PostUserRating({ + entityToken: "et1", + entityType: "entt1", + ratingValue: "rate1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/rate", + { + method: "POST", + body: { + entity_token: "et1", + entity_type: "entt1", + rating_value: "rate1", + }, + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListUserRatings", () => { + it("success", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + ratings: [ + { + entity_token: "string", + entity_type: "media_file", + rating_value: "neutral", + }, + ], + success: true, + }); + const response = await api.ListUserRatings(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: [ + { + entity_token: "string", + entity_type: "media_file", + rating_value: "neutral", + }, + ], + errorMessage: undefined, + }); + }); + + it("failure", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.ListUserRatings(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + data: undefined, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListUserRatings(); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/batch", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + + describe("ListUserRatingsByEntity", () => { + it("success", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ + maybe_rating_value: "neutral", + success: true, + }); + const response = await api.ListUserRatingByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/view/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: true, + data: "neutral", + }); + }); + + it("failure", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await api.ListUserRatingByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/view/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + const api = new UserRatingApi(); + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await api.ListUserRatingByEntity({ + entityType: "et1", + entityToken: "tok1", + }); + expect(api.fetch).toHaveBeenCalledWith( + "http://localhost:3000/v1/user_rating/view/et1/tok1", + { + method: "GET", + query: undefined, + }, + ); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); +}); diff --git a/test/app/Classes/ApiManager/WeightsApi.test.ts b/test/app/Classes/ApiManager/WeightsApi.test.ts new file mode 100644 index 000000000..7b86ad2c3 --- /dev/null +++ b/test/app/Classes/ApiManager/WeightsApi.test.ts @@ -0,0 +1,439 @@ +import { authentication } from "~/signals"; +import { UserInfo } from "~/models"; +import EnvironmentVariables from "~/Classes/EnvironmentVariables"; +import { + ListFeaturedWeightsRequest, + ListWeightsByUserRequest, + ListWeightsRequest, + ScopedWeightCategory, + ScopedWeightType, + SearchWeightParams, + WeightsApi, +} from "~/Classes/ApiManager/WeightsApi"; +import { + FilterEngineCategories, + FilterMediaClasses, + FilterMediaType, + Visibility, +} from "~/enums"; + +const mockWeight = { + cover_image: { + default_cover: { + color_index: 0, + image_index: 0, + }, + maybe_cover_image_public_bucket_path: "string", + }, + created_at: "2024-06-17T11:11:03.533Z", + creator: { + default_avatar: { + color_index: 0, + image_index: 0, + }, + display_name: "string", + gravatar_hash: "string", + user_token: "string", + username: "string", + }, + creator_set_visibility: "public", + file_checksum_sha2: "string", + file_size_bytes: 0, + stats: { + bookmark_count: 0, + positive_rating_count: 0, + }, + title: "string", + updated_at: "2024-06-17T11:11:03.533Z", + weight_category: "string", + weight_token: "string", + weight_type: "string", +}; + +EnvironmentVariables.initialize({ BASE_API: "http://localhost:3000" }); + +describe("UserBookmarksApi", () => { + beforeAll(() => { + authentication.userInfo.value = { + user_token: "un1", + username: "un1", + } as UserInfo; + }); + describe("run list tests", () => { + const api = new WeightsApi(); + [ + { + name: "ListWeightsByUser", + function: api.ListWeightsByUser.bind(api), + tests: [ + { + name: "no params", + endpoint: "http://localhost:3000/v1/weights/by_user/un1", + query: {} as ListWeightsByUserRequest, + fetchQuery: {}, + }, + { + name: "all params", + endpoint: "http://localhost:3000/v1/weights/by_user/user1", + query: { + pageIndex: 2, + sortAscending: true, + pageSize: 32, + username: "user1", + weightCategory: ["image_generation"], + weightType: ["hifigan_tt2"], + } as ListWeightsByUserRequest, + fetchQuery: { + page_index: "2", + page_size: "32", + sort_ascending: "true", + weight_category: "image_generation", + weight_type: "hifigan_tt2", + }, + }, + ], + }, + { + name: "ListWeights", + function: api.ListWeights.bind(api), + tests: [ + { + name: "no params", + endpoint: "http://localhost:3000/v1/weights/list", + query: {} as ListWeightsRequest, + fetchQuery: {}, + }, + { + name: "all params", + endpoint: "http://localhost:3000/v1/weights/list", + query: { + cursor: "cursor1", + cursorIsReversed: true, + pageSize: 32, + sortAscending: true, + username: "user1", + weightCategory: ["image_generation"], + weightType: ["hifigan_tt2"], + } as ListWeightsRequest, + fetchQuery: { + cursor: "cursor1", + cursor_is_reversed: "true", + page_size: "32", + sort_ascending: "true", + username: "user1", + weight_category: "image_generation", + weight_type: "hifigan_tt2", + }, + }, + ], + }, + { + name: "ListWeightsFeatured", + function: api.ListWeightsFeatured.bind(api), + tests: [ + { + name: "no params", + endpoint: "http://localhost:3000/v1/weights/list_featured", + query: {} as ListFeaturedWeightsRequest, + fetchQuery: {}, + }, + { + name: "all params", + endpoint: "http://localhost:3000/v1/weights/list_featured", + query: { + sortAscending: true, + pageSize: 32, + cursor: "cursor1", + cursorIsReversed: true, + filterMediaClasses: [FilterMediaClasses.AUDIO], + filterMediaType: [FilterMediaType.GLB, FilterMediaType.GLTF], + filterEngineCategories: [ + FilterEngineCategories.ANIMATION, + FilterEngineCategories.AUDIO, + FilterEngineCategories.CHARACTER, + ], + } as ListFeaturedWeightsRequest, + fetchQuery: { + page_size: "32", + sort_ascending: "true", + cursor: "cursor1", + cursor_is_reversed: "true", + filter_media_classes: "audio", + filter_media_type: "glb,gltf", + filter_engine_categories: "animation,audio,character", + }, + }, + ], + }, + { + name: "ListWeightsPinned", + function: api.ListWeightsPinned.bind(api), + tests: [ + { + name: "success", + endpoint: "http://localhost:3000/v1/weights/list_pinned", + query: undefined, + fetchQuery: undefined, + }, + ], + }, + ].forEach((testMethod) => { + describe(testMethod.name, () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + testMethod.tests.forEach((test) => { + it(test.name, async () => { + jest.spyOn(api, "fetch").mockResolvedValue({ + results: [mockWeight], + success: true, + }); + const response = await testMethod.function(test.query as any); + expect(api.fetch).toHaveBeenCalledWith(test.endpoint, { + method: "GET", + query: test.fetchQuery, + }); + expect(response).toEqual({ + data: [mockWeight], + success: true, + errorMessage: undefined, + }); + }); + }); + + it("exception", async () => { + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await testMethod.function( + testMethod.tests[0].query as any, + ); + expect(api.fetch).toHaveBeenCalledWith(testMethod.tests[0].endpoint, { + method: "GET", + query: testMethod.tests[0].fetchQuery, + }); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + }); + }); + describe("run search test", () => { + const api = new WeightsApi(); + [ + { + name: "SearchWeights", + function: api.SearchWeights.bind(api), + endpoint: "http://localhost:3000/v1/weights/search", + body: { + ietfLanguageSubtag: "ietf1", + searchTerm: "st1", + weightCategory: ScopedWeightCategory.IMAGE_GENERATION, + weightType: ScopedWeightType.HIFIGAN_TT2, + } as SearchWeightParams, + fetchBody: { + ietf_language_subtag: "ietf1", + search_term: "st1", + weight_category: "image_generation", + weight_type: "hifigan_tt2", + }, + }, + ].forEach((testMethod) => { + describe(testMethod.name, () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("success", async () => { + jest.spyOn(api, "fetch").mockResolvedValue({ + weights: [mockWeight], + success: true, + }); + const response = await testMethod.function(testMethod.body); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: testMethod.fetchBody, + query: undefined, + }); + expect(response).toEqual({ + data: [mockWeight], + success: true, + errorMessage: undefined, + }); + }); + + it("exception", async () => { + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await testMethod.function(testMethod.body); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "POST", + body: testMethod.fetchBody, + query: undefined, + }); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + }); + }); + describe("run get test", () => { + const api = new WeightsApi(); + [ + { + name: "GetWeightByToken", + function: api.GetWeightByToken.bind(api), + endpoint: "http://localhost:3000/v1/weights/weight/wt1", + params: { + weightToken: "wt1", + }, + }, + ].forEach((testMethod) => { + describe(testMethod.name, () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("success", async () => { + jest.spyOn(api, "fetch").mockResolvedValue({ + ...mockWeight, + success: true, + }); + const response = await testMethod.function(testMethod.params); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "GET", + body: undefined, + query: undefined, + }); + expect(response).toEqual({ + data: mockWeight, + success: true, + errorMessage: undefined, + }); + }); + + it("exception", async () => { + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await testMethod.function(testMethod.params); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: "GET", + body: undefined, + query: undefined, + }); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + }); + }); + describe("run update/delete tests", () => { + const api = new WeightsApi(); + [ + { + name: "UpdateWeightByToken", + function: api.UpdateWeightByToken.bind(api), + endpoint: "http://localhost:3000/v1/weights/weight/wt1", + method: "POST", + paramsIn: { + weightToken: "wt1", + coverImageMediaFileToken: "cimft1", + descriptionMarkdown: "descr1", + title: "title1", + visibility: Visibility.Private, + }, + paramsTest: { + cover_image_media_file_token: "cimft1", + description_markdown: "descr1", + title: "title1", + visibility: Visibility.Private, + }, + }, + { + name: "DeleteWeightByToken", + function: api.DeleteWeightByToken.bind(api), + endpoint: "http://localhost:3000/v1/weights/weight/wt1", + method: "DELETE", + paramsIn: { + weightToken: "wt1", + }, + paramsTest: { + as_mod: true, + set_delete: true, + }, + }, + { + name: "UpdateWeightCoverImageByToken", + function: api.UpdateWeightCoverImageByToken.bind(api), + endpoint: "http://localhost:3000/v1/weights/wt1/cover_image", + method: "POST", + paramsIn: { + weightToken: "wt1", + coverImageMediaFileToken: "cimft1", + }, + paramsTest: { + cover_image_media_file_token: "cimft1", + }, + }, + ].forEach((testMethod) => { + describe(testMethod.name, () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it(test.name, async () => { + jest.spyOn(api, "fetch").mockResolvedValue({ + success: true, + }); + const response = await testMethod.function( + testMethod.paramsIn as any, + ); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: testMethod.method, + body: testMethod.paramsTest, + query: undefined, + }); + expect(response).toEqual({ + success: true, + errorMessage: undefined, + }); + }); + + it("failure", async () => { + jest.spyOn(api, "fetch").mockResolvedValue({ BadInput: "bad input" }); + const response = await testMethod.function( + testMethod.paramsIn as any, + ); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: testMethod.method, + body: testMethod.paramsTest, + query: undefined, + }); + expect(response).toEqual({ + success: false, + errorMessage: "bad input", + }); + }); + + it("exception", async () => { + jest.spyOn(api, "fetch").mockRejectedValue(new Error("server error")); + const response = await testMethod.function( + testMethod.paramsIn as any, + ); + expect(api.fetch).toHaveBeenCalledWith(testMethod.endpoint, { + method: testMethod.method, + body: testMethod.paramsTest, + query: undefined, + }); + expect(response).toEqual({ + success: false, + errorMessage: "server error", + }); + }); + }); + }); + }); +});