From fdfc76098000ca10c28ef76e91f74075c1c17b5c Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:23:57 +0000 Subject: [PATCH 01/16] Added new common.ts within API folder to store generic types against pulpcore serializers --- src/api/common.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/api/common.ts diff --git a/src/api/common.ts b/src/api/common.ts new file mode 100644 index 00000000..c56b1752 --- /dev/null +++ b/src/api/common.ts @@ -0,0 +1,29 @@ +/** + * Generic Resource shared across every Pulp resource. + * + * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/base.py + */ +interface GenericResource { + readonly pulp_href?: string; + readonly prn?: string; + readonly pulp_created?: string; + readonly pulp_last_updated?: string; +} + +/** + * Generic Repository shared across Pulp Repository plugins. + * + * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/repository.py + */ +interface GenericRepository extends GenericResource { + readonly pulp_labels?: Record; + readonly versions_href?: string; + readonly latest_version_href?: string; + readonly name: string; + readonly description: string | null; + readonly retain_repo_versions: string | null; + readonly retain_checkpoints: string | null; + readonly remote: string | null; +} + +export type { GenericRepository } From ea7eeb724f41eeb91d85813280bc984957f10879 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:37:47 +0000 Subject: [PATCH 02/16] Updated types within common.ts and created a new RPM Repository type based off serializer --- src/api/common.ts | 12 ++++++------ src/api/rpm-repository.ts | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/api/common.ts b/src/api/common.ts index c56b1752..fd9615dd 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -16,14 +16,14 @@ interface GenericResource { * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/repository.py */ interface GenericRepository extends GenericResource { - readonly pulp_labels?: Record; + pulp_labels?: Record; readonly versions_href?: string; readonly latest_version_href?: string; - readonly name: string; - readonly description: string | null; - readonly retain_repo_versions: string | null; - readonly retain_checkpoints: string | null; - readonly remote: string | null; + name: string; + description?: string | null; + retain_repo_versions?: number | null; + retain_checkpoints?: number | null; + remote?: string | null; } export type { GenericRepository } diff --git a/src/api/rpm-repository.ts b/src/api/rpm-repository.ts index 9e32c898..cbb2545f 100644 --- a/src/api/rpm-repository.ts +++ b/src/api/rpm-repository.ts @@ -1,7 +1,32 @@ +import type { GenericRepository } from './common'; import { PulpAPI } from './pulp'; +type RPMChecksumType = "unknown" | "md5" | "sha" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"; +type RPMCompressionType = "zstd" | "gz" | "none"; +type RPMLayoutType = "nested_alphabetically" | "flat" | "nested_by_digest"; + +/** + * RPM Repository Type. + * + * @see https://github.com/pulp/pulp_rpm/blob/main/pulp_rpm/app/serializers/repository.py + */ +interface RPMRepositoryType extends GenericRepository { + autopublish?: boolean; + metadata_signing_service?: string | null; + package_signing_service?: string | null; + package_signing_fingerprint?: string | null; + retain_package_versions?: number; + checksum_type?: RPMChecksumType | null; + compression_type?: RPMCompressionType | null; + layout?: RPMLayoutType | null; + repo_config?: Record; + osv_config?: { name: string; releases: unknown }[] | null; +} + const base = new PulpAPI(); export const RPMRepositoryAPI = { list: (params?) => base.list(`repositories/rpm/rpm/`, params), }; + +export type { RPMRepositoryType }; From 41f5e5811e11131fb82d464fe374cc2c0babf15b Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:23:35 +0000 Subject: [PATCH 03/16] Updated ansible repository to new interface, mapped out shared types outside pulp core within common.ts --- src/api/ansible-repository.ts | 29 +++++++++++++--------------- src/api/common.ts | 33 +++++++++++++++++++++++++++++++- src/api/response-types/remote.ts | 6 ++++++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/api/ansible-repository.ts b/src/api/ansible-repository.ts index 7a0758a1..a588f04f 100644 --- a/src/api/ansible-repository.ts +++ b/src/api/ansible-repository.ts @@ -1,22 +1,17 @@ +import type { GenericRepository, LastSyncType } from './common'; import { PulpAPI } from './pulp'; -import { type LastSyncType } from './response-types/remote'; -export class AnsibleRepositoryType { - description: string; - last_sync_task?: LastSyncType; - latest_version_href?: string; - name: string; +/** + * Ansible Repository Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/serializers.py + */ +interface AnsibleRepositoryType extends GenericRepository { + last_synced_metadata_time?: string | null; + gpgkey?: string | null; + readonly last_sync_task?: LastSyncType; private?: boolean; - pulp_created?: string; - pulp_href?: string; - pulp_labels?: Record; - remote?: string; - retain_repo_versions: number; - - // gpgkey - // last_synced_metadata_time - // versions_href - + // NOTE: Not part of the Ansible serializer, populated separately. my_permissions?: string[]; } @@ -91,3 +86,5 @@ export const AnsibleRepositoryAPI = { update: (id: string, data) => base.http.put(`repositories/ansible/ansible/${id}/`, data), }; + +export type { AnsibleRepositoryType } diff --git a/src/api/common.ts b/src/api/common.ts index fd9615dd..ce48be64 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -1,3 +1,15 @@ +import type { PulpStatus } from "./response-types/pulp"; + +/** + * Generic PulpCore Exceptions based on dictionary representation. + * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/exceptions/base.py + */ +interface TaskErrorType { + description: string; + traceback: string | null; + error_code?: string; +} + /** * Generic Resource shared across every Pulp resource. * @@ -26,4 +38,23 @@ interface GenericRepository extends GenericResource { remote?: string | null; } -export type { GenericRepository } +/** + * -------------------- + * Shared types reused across multiple resource types but not part of pulpcore generic types. + * -------------------- + */ + +/** + * Last Sync Task Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py + */ +interface LastSyncType { + pk: string; + state: PulpStatus; + pulp_created: string; + finished_at: string; + error: TaskErrorType; +} + +export type { TaskErrorType, GenericRepository, LastSyncType } diff --git a/src/api/response-types/remote.ts b/src/api/response-types/remote.ts index e88b7a22..aa1da305 100644 --- a/src/api/response-types/remote.ts +++ b/src/api/response-types/remote.ts @@ -1,5 +1,11 @@ import { type PulpStatus } from './pulp'; +/** + * @deprecated + * Use `LastSyncType` from `common.ts` instead. + * + * Known issue: `started_at` doesn't match real API field, and `finished_at` / `error` are actually nullable. + */ export class LastSyncType { state: PulpStatus; started_at: string; From 1e81ca6b3577dddd8fa29437bd193eea9f02fb4b Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:24:22 +0000 Subject: [PATCH 04/16] Updated file repository to new interface adding in new fields from recently updated serializer --- src/api/ansible-repository.ts | 4 +-- src/api/common.ts | 50 ++++++++++++++++---------------- src/api/file-repository.ts | 37 ++++++++++++++--------- src/api/response-types/remote.ts | 2 +- src/api/rpm-repository.ts | 16 +++++++--- 5 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/api/ansible-repository.ts b/src/api/ansible-repository.ts index a588f04f..dddaae84 100644 --- a/src/api/ansible-repository.ts +++ b/src/api/ansible-repository.ts @@ -3,7 +3,7 @@ import { PulpAPI } from './pulp'; /** * Ansible Repository Type. - * + * * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/serializers.py */ interface AnsibleRepositoryType extends GenericRepository { @@ -87,4 +87,4 @@ export const AnsibleRepositoryAPI = { base.http.put(`repositories/ansible/ansible/${id}/`, data), }; -export type { AnsibleRepositoryType } +export type { AnsibleRepositoryType }; diff --git a/src/api/common.ts b/src/api/common.ts index ce48be64..8c4e1d8f 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -1,41 +1,41 @@ -import type { PulpStatus } from "./response-types/pulp"; +import type { PulpStatus } from './response-types/pulp'; /** * Generic PulpCore Exceptions based on dictionary representation. * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/exceptions/base.py */ interface TaskErrorType { - description: string; - traceback: string | null; - error_code?: string; + description: string; + traceback: string | null; + error_code?: string; } /** * Generic Resource shared across every Pulp resource. - * + * * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/base.py */ interface GenericResource { - readonly pulp_href?: string; - readonly prn?: string; - readonly pulp_created?: string; - readonly pulp_last_updated?: string; + readonly pulp_href?: string; + readonly prn?: string; + readonly pulp_created?: string; + readonly pulp_last_updated?: string; } /** * Generic Repository shared across Pulp Repository plugins. - * + * * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/repository.py */ interface GenericRepository extends GenericResource { - pulp_labels?: Record; - readonly versions_href?: string; - readonly latest_version_href?: string; - name: string; - description?: string | null; - retain_repo_versions?: number | null; - retain_checkpoints?: number | null; - remote?: string | null; + pulp_labels?: Record; + readonly versions_href?: string; + readonly latest_version_href?: string; + name: string; + description?: string | null; + retain_repo_versions?: number | null; + retain_checkpoints?: number | null; + remote?: string | null; } /** @@ -46,15 +46,15 @@ interface GenericRepository extends GenericResource { /** * Last Sync Task Type. - * + * * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py */ interface LastSyncType { - pk: string; - state: PulpStatus; - pulp_created: string; - finished_at: string; - error: TaskErrorType; + pk: string; + state: PulpStatus; + pulp_created: string; + finished_at: string; + error: TaskErrorType; } -export type { TaskErrorType, GenericRepository, LastSyncType } +export type { TaskErrorType, GenericRepository, LastSyncType }; diff --git a/src/api/file-repository.ts b/src/api/file-repository.ts index 7481c2f3..59cb65ad 100644 --- a/src/api/file-repository.ts +++ b/src/api/file-repository.ts @@ -1,19 +1,28 @@ +import type { GenericRepository } from './common'; import { PulpAPI } from './pulp'; -export class FileRepositoryType { +/** + * Type for syncing metadata set on FileRepository after each sync. + * @see https://github.com/pulp/pulpcore/blob/main/pulp_file/app/tasks/synchronizing.py + */ +interface FileLastSyncDetailsType { + remote_pk: string; + url: string; + download_policy: string; + mirror: boolean; + most_recent_version: number; + manifest_checksum: string; +} + +/** + * File Repository Type. + * + * @see https://github.com/pulp/pulpcore/blob/main/pulp_file/app/serializers.py + */ +interface FileRepositoryType extends GenericRepository { autopublish?: boolean; - description: string | null; - latest_version_href?: string; - manifest?: string; - name: string; - prn?: string; - pulp_created?: string; - pulp_href?: string; - pulp_labels: Record; - pulp_last_updated?: string; - remote: string | null; - retain_repo_versions: number; - versions_href?: string; + manifest?: string | null; + readonly last_sync_details?: FileLastSyncDetailsType | null; } const base = new PulpAPI(); @@ -39,3 +48,5 @@ export const FileRepositoryAPI = { update: (id: string, data) => base.http.put(`repositories/file/file/${id}/`, data), }; + +export type { FileRepositoryType }; diff --git a/src/api/response-types/remote.ts b/src/api/response-types/remote.ts index aa1da305..47bc86a0 100644 --- a/src/api/response-types/remote.ts +++ b/src/api/response-types/remote.ts @@ -3,7 +3,7 @@ import { type PulpStatus } from './pulp'; /** * @deprecated * Use `LastSyncType` from `common.ts` instead. - * + * * Known issue: `started_at` doesn't match real API field, and `finished_at` / `error` are actually nullable. */ export class LastSyncType { diff --git a/src/api/rpm-repository.ts b/src/api/rpm-repository.ts index cbb2545f..2dd8f24d 100644 --- a/src/api/rpm-repository.ts +++ b/src/api/rpm-repository.ts @@ -1,13 +1,21 @@ import type { GenericRepository } from './common'; import { PulpAPI } from './pulp'; -type RPMChecksumType = "unknown" | "md5" | "sha" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"; -type RPMCompressionType = "zstd" | "gz" | "none"; -type RPMLayoutType = "nested_alphabetically" | "flat" | "nested_by_digest"; +type RPMChecksumType = + | 'unknown' + | 'md5' + | 'sha' + | 'sha1' + | 'sha224' + | 'sha256' + | 'sha384' + | 'sha512'; +type RPMCompressionType = 'zstd' | 'gz' | 'none'; +type RPMLayoutType = 'nested_alphabetically' | 'flat' | 'nested_by_digest'; /** * RPM Repository Type. - * + * * @see https://github.com/pulp/pulp_rpm/blob/main/pulp_rpm/app/serializers/repository.py */ interface RPMRepositoryType extends GenericRepository { From f1221fbf6781bf4da7a283bf26cfeba88f38ca80 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:23:53 +0000 Subject: [PATCH 05/16] Fixed issue with Ansible types. Removed incorrect comment due to plugin confusion for ansible. --- src/api/ansible-repository.ts | 23 ++++++++++++++++++++--- src/api/common.ts | 23 +---------------------- src/api/response-types/remote.ts | 6 ------ 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/api/ansible-repository.ts b/src/api/ansible-repository.ts index dddaae84..53ee60a2 100644 --- a/src/api/ansible-repository.ts +++ b/src/api/ansible-repository.ts @@ -1,5 +1,19 @@ -import type { GenericRepository, LastSyncType } from './common'; +import type { GenericRepository, TaskErrorType } from './common'; import { PulpAPI } from './pulp'; +import type { PulpStatus } from './response-types/pulp'; + +/** + * Last Sync Task Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py + */ +interface LastSyncType { + pk: string; + state: PulpStatus; + pulp_created: string; + finished_at: string | null; + error: TaskErrorType | null; +} /** * Ansible Repository Type. @@ -9,9 +23,12 @@ import { PulpAPI } from './pulp'; interface AnsibleRepositoryType extends GenericRepository { last_synced_metadata_time?: string | null; gpgkey?: string | null; - readonly last_sync_task?: LastSyncType; + readonly last_sync_task?: LastSyncType | null; private?: boolean; - // NOTE: Not part of the Ansible serializer, populated separately. + /** + * NOTE: Not part of the Ansible serializer, populated separately. + * This should be broken out into its own type and extend the interface. + */ my_permissions?: string[]; } diff --git a/src/api/common.ts b/src/api/common.ts index 8c4e1d8f..7c309a24 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -1,5 +1,3 @@ -import type { PulpStatus } from './response-types/pulp'; - /** * Generic PulpCore Exceptions based on dictionary representation. * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/exceptions/base.py @@ -38,23 +36,4 @@ interface GenericRepository extends GenericResource { remote?: string | null; } -/** - * -------------------- - * Shared types reused across multiple resource types but not part of pulpcore generic types. - * -------------------- - */ - -/** - * Last Sync Task Type. - * - * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py - */ -interface LastSyncType { - pk: string; - state: PulpStatus; - pulp_created: string; - finished_at: string; - error: TaskErrorType; -} - -export type { TaskErrorType, GenericRepository, LastSyncType }; +export type { TaskErrorType, GenericRepository }; diff --git a/src/api/response-types/remote.ts b/src/api/response-types/remote.ts index 47bc86a0..e88b7a22 100644 --- a/src/api/response-types/remote.ts +++ b/src/api/response-types/remote.ts @@ -1,11 +1,5 @@ import { type PulpStatus } from './pulp'; -/** - * @deprecated - * Use `LastSyncType` from `common.ts` instead. - * - * Known issue: `started_at` doesn't match real API field, and `finished_at` / `error` are actually nullable. - */ export class LastSyncType { state: PulpStatus; started_at: string; From 63086c48de3243bc97e83a84e2363ca50a192c8f Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:53:53 +0000 Subject: [PATCH 06/16] Defined new distribution generic from pulpcore and built out ansible distribution --- src/api/ansible-distribution.ts | 13 +++++++++++++ src/api/common.ts | 22 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/api/ansible-distribution.ts b/src/api/ansible-distribution.ts index 8a06fe3d..85698bd1 100644 --- a/src/api/ansible-distribution.ts +++ b/src/api/ansible-distribution.ts @@ -1,5 +1,16 @@ +import type { GenericDistribution } from './common'; import { PulpAPI } from './pulp'; +/** + * Ansible Distribution Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/0043923641fc7fd3893f8489fd29ff04addc9d71/pulp_ansible/app/serializers.py#L356 + */ +interface AnsibleDistributionType extends Omit { + readonly client_url: string; + repository_version?: string | null; +} + const base = new PulpAPI(); export const AnsibleDistributionAPI = { @@ -11,3 +22,5 @@ export const AnsibleDistributionAPI = { url: (distro_data) => distro_data.client_url, }; + +export type { AnsibleDistributionType }; diff --git a/src/api/common.ts b/src/api/common.ts index 7c309a24..6b5fe653 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -11,7 +11,7 @@ interface TaskErrorType { /** * Generic Resource shared across every Pulp resource. * - * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/base.py + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/base.py#L448 */ interface GenericResource { readonly pulp_href?: string; @@ -36,4 +36,22 @@ interface GenericRepository extends GenericResource { remote?: string | null; } -export type { TaskErrorType, GenericRepository }; +/** + * Generic Distribution shared across Pulp Distribution plugins. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/publication.py + */ +interface GenericDistribution extends GenericResource { + base_path: string; + readonly base_url?: string; + content_guard?: string | null; + readonly content_guard_prn?: string | null; + readonly no_content_change_since?: string | null; + hidden?: boolean; + pulp_labels?: Record; + name: string; + repository?: string; + repository_version?: string; +} + +export type { TaskErrorType, GenericRepository, GenericDistribution }; From 762de1e29b5e8012a3372fe89930b9db82144c27 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:09:09 +0000 Subject: [PATCH 07/16] Defined new distribution and publication generic for file distribution and publication types --- src/api/common.ts | 12 +++++++++++- src/api/file-distribution.ts | 13 +++++++++++++ src/api/file-publication.ts | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/api/common.ts b/src/api/common.ts index 6b5fe653..3abcd434 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -54,4 +54,14 @@ interface GenericDistribution extends GenericResource { repository_version?: string; } -export type { TaskErrorType, GenericRepository, GenericDistribution }; +/** + * Generic Publication shared across Pulp Publication plugins. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/publication.py#L23 + */ +interface GenericPublication extends GenericResource { + repository_version?: string; + repository?: string; +} + +export type { TaskErrorType, GenericRepository, GenericDistribution, GenericPublication }; diff --git a/src/api/file-distribution.ts b/src/api/file-distribution.ts index ac05996b..336aae5d 100644 --- a/src/api/file-distribution.ts +++ b/src/api/file-distribution.ts @@ -1,5 +1,16 @@ +import type { GenericDistribution } from './common'; import { PulpAPI } from './pulp'; +/** + * File Distribution Type. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L225 + */ +interface FileDistributionType extends GenericDistribution { + publication?: string | null; + checkpoint?: boolean; +} + const base = new PulpAPI(); export const FileDistributionAPI = { @@ -9,3 +20,5 @@ export const FileDistributionAPI = { list: (params?) => base.list(`distributions/file/file/`, params), }; + +export type { FileDistributionType } diff --git a/src/api/file-publication.ts b/src/api/file-publication.ts index b0bcc6b7..624f96c9 100644 --- a/src/api/file-publication.ts +++ b/src/api/file-publication.ts @@ -1,5 +1,17 @@ +import type { GenericPublication } from './common'; import { PulpAPI } from './pulp'; +/** + * File Publication Type. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L200 + */ +interface FilePublicationType extends GenericPublication { + readonly distributions: string[]; + manifest?: string | null; + checkpoint?: boolean; +} + const base = new PulpAPI(); export const FilePublicationAPI = { @@ -9,3 +21,5 @@ export const FilePublicationAPI = { list: (params?) => base.list(`publications/file/file/`, params), }; + +export type { FilePublicationType }; From ee63c1000776aa35e23699497f2d674be19b688a Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:44:39 +0000 Subject: [PATCH 08/16] Defined new Remote generic and updated ansible remote type --- src/api/ansible-remote.ts | 43 +++++++++------------------ src/api/ansible-repository.ts | 20 ++----------- src/api/common.ts | 55 ++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/api/ansible-remote.ts b/src/api/ansible-remote.ts index 98c1ecdc..52c1c2d5 100644 --- a/src/api/ansible-remote.ts +++ b/src/api/ansible-remote.ts @@ -1,36 +1,17 @@ +import type { AnsibleLastSyncType, GenericRemote } from './common'; import { PulpAPI } from './pulp'; -export class AnsibleRemoteType { - auth_url: string; - ca_cert: string; - client_cert: string; - download_concurrency: number; - name: string; - proxy_url: string; - pulp_href?: string; - rate_limit: number; - requirements_file: string; - tls_validation: boolean; - url: string; - signed_only: boolean; +interface AnsibleRemoteType extends GenericRemote { + requirements_file?: string | null; + auth_url?: string | null; + token?: string | null; sync_dependencies?: boolean; - - // connect_timeout - // headers - // max_retries - // policy - // pulp_created - // pulp_labels - // pulp_last_updated - // sock_connect_timeout - // sock_read_timeout - // total_timeout - - hidden_fields: { - is_set: boolean; - name: string; - }[]; - + signed_only?: boolean; + readonly last_sync_task?: AnsibleLastSyncType | null; + /** + * NOTE: Not part of the Ansible serializer, populated separately. + * This should be broken out into its own type and extend the interface. + */ my_permissions?: string[]; } @@ -90,3 +71,5 @@ export const AnsibleRemoteAPI = { smartUpdate(newValue, oldValue), ), }; + +export type { AnsibleRemoteType } diff --git a/src/api/ansible-repository.ts b/src/api/ansible-repository.ts index 53ee60a2..295aa410 100644 --- a/src/api/ansible-repository.ts +++ b/src/api/ansible-repository.ts @@ -1,29 +1,15 @@ -import type { GenericRepository, TaskErrorType } from './common'; +import type { AnsibleLastSyncType, GenericRepository } from './common'; import { PulpAPI } from './pulp'; -import type { PulpStatus } from './response-types/pulp'; - -/** - * Last Sync Task Type. - * - * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py - */ -interface LastSyncType { - pk: string; - state: PulpStatus; - pulp_created: string; - finished_at: string | null; - error: TaskErrorType | null; -} /** * Ansible Repository Type. * - * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/serializers.py + * @see https://github.com/pulp/pulp_ansible/blob/0043923641fc7fd3893f8489fd29ff04addc9d71/pulp_ansible/app/serializers.py#L148 */ interface AnsibleRepositoryType extends GenericRepository { last_synced_metadata_time?: string | null; gpgkey?: string | null; - readonly last_sync_task?: LastSyncType | null; + readonly last_sync_task?: AnsibleLastSyncType | null; private?: boolean; /** * NOTE: Not part of the Ansible serializer, populated separately. diff --git a/src/api/common.ts b/src/api/common.ts index 3abcd434..bdf879c8 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -1,3 +1,5 @@ +import type { PulpStatus } from "./response-types/pulp"; + /** * Generic PulpCore Exceptions based on dictionary representation. * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/exceptions/base.py @@ -64,4 +66,55 @@ interface GenericPublication extends GenericResource { repository?: string; } -export type { TaskErrorType, GenericRepository, GenericDistribution, GenericPublication }; +/** + * Generic Remote shared across Pulp Remote plugins. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/repository.py#L85 + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/base.py#L612 + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/base.py#L367 + */ +interface GenericRemote extends GenericResource { + pulp_labels?: Record; + name: string; + url: string; + policy?: string; + readonly hidden_fields?: { name: string; is_set: boolean; }[]; + ca_cert?: string | null; + client_cert?: string | null; + client_key?: string | null; + tls_validation?: boolean; + proxy_url?: string | null; + proxy_username?: string | null; + proxy_password?: string | null; + username?: string | null; + password?: string | null; + max_retries?: number | null; + total_timeout?: number | null; + connect_timeout?: number | null; + sock_connect_timeout?: number | null; + sock_read_timeout?: number | null; + headers?: unknown[]; + download_concurrency?: number | null; + rate_limit?: number | null; +} + +/** + * -------------------- + * These are shared Plugin Types outside the PulpCore Generics. + * -------------------- + */ + +/** + * Last Sync Task Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py + */ +interface AnsibleLastSyncType { + pk: string; + state: PulpStatus; + pulp_created: string; + finished_at: string | null; + error: TaskErrorType | null; +} + +export type { TaskErrorType, GenericRepository, GenericDistribution, GenericPublication, GenericRemote, AnsibleLastSyncType }; From 4e164e3f10feabe50721bfe12aa24f1c340d29fd Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:01:14 +0000 Subject: [PATCH 09/16] Updated File Remote definition along with commit pinning references. --- src/api/ansible-distribution.ts | 7 ++++-- src/api/ansible-remote.ts | 9 +++++-- src/api/ansible-repository.ts | 2 +- src/api/common.ts | 25 +++++++++++++------- src/api/file-distribution.ts | 4 ++-- src/api/file-publication.ts | 2 +- src/api/file-remote.ts | 42 ++++++++++----------------------- src/api/file-repository.ts | 4 ++-- 8 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/api/ansible-distribution.ts b/src/api/ansible-distribution.ts index 85698bd1..c5def02a 100644 --- a/src/api/ansible-distribution.ts +++ b/src/api/ansible-distribution.ts @@ -3,10 +3,13 @@ import { PulpAPI } from './pulp'; /** * Ansible Distribution Type. - * + * * @see https://github.com/pulp/pulp_ansible/blob/0043923641fc7fd3893f8489fd29ff04addc9d71/pulp_ansible/app/serializers.py#L356 */ -interface AnsibleDistributionType extends Omit { +interface AnsibleDistributionType extends Omit< + GenericDistribution, + 'base_url' +> { readonly client_url: string; repository_version?: string | null; } diff --git a/src/api/ansible-remote.ts b/src/api/ansible-remote.ts index 52c1c2d5..61f7594a 100644 --- a/src/api/ansible-remote.ts +++ b/src/api/ansible-remote.ts @@ -1,6 +1,11 @@ import type { AnsibleLastSyncType, GenericRemote } from './common'; import { PulpAPI } from './pulp'; +/** + * Ansible Remote Type. + * + * @see https://github.com/pulp/pulp_ansible/blob/0043923641fc7fd3893f8489fd29ff04addc9d71/pulp_ansible/app/serializers.py#L213 + */ interface AnsibleRemoteType extends GenericRemote { requirements_file?: string | null; auth_url?: string | null; @@ -9,7 +14,7 @@ interface AnsibleRemoteType extends GenericRemote { signed_only?: boolean; readonly last_sync_task?: AnsibleLastSyncType | null; /** - * NOTE: Not part of the Ansible serializer, populated separately. + * NOTE: Not part of the Ansible serializer, populated separately. * This should be broken out into its own type and extend the interface. */ my_permissions?: string[]; @@ -72,4 +77,4 @@ export const AnsibleRemoteAPI = { ), }; -export type { AnsibleRemoteType } +export type { AnsibleRemoteType }; diff --git a/src/api/ansible-repository.ts b/src/api/ansible-repository.ts index 295aa410..5c73681c 100644 --- a/src/api/ansible-repository.ts +++ b/src/api/ansible-repository.ts @@ -12,7 +12,7 @@ interface AnsibleRepositoryType extends GenericRepository { readonly last_sync_task?: AnsibleLastSyncType | null; private?: boolean; /** - * NOTE: Not part of the Ansible serializer, populated separately. + * NOTE: Not part of the Ansible serializer, populated separately. * This should be broken out into its own type and extend the interface. */ my_permissions?: string[]; diff --git a/src/api/common.ts b/src/api/common.ts index bdf879c8..a5f64b81 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -1,8 +1,8 @@ -import type { PulpStatus } from "./response-types/pulp"; +import type { PulpStatus } from './response-types/pulp'; /** * Generic PulpCore Exceptions based on dictionary representation. - * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/exceptions/base.py + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/exceptions/base.py#L37 */ interface TaskErrorType { description: string; @@ -25,7 +25,7 @@ interface GenericResource { /** * Generic Repository shared across Pulp Repository plugins. * - * @see https://github.com/pulp/pulpcore/blob/main/pulpcore/app/serializers/repository.py + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/repository.py#L26 */ interface GenericRepository extends GenericResource { pulp_labels?: Record; @@ -40,7 +40,7 @@ interface GenericRepository extends GenericResource { /** * Generic Distribution shared across Pulp Distribution plugins. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/publication.py */ interface GenericDistribution extends GenericResource { @@ -58,7 +58,7 @@ interface GenericDistribution extends GenericResource { /** * Generic Publication shared across Pulp Publication plugins. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/publication.py#L23 */ interface GenericPublication extends GenericResource { @@ -68,7 +68,7 @@ interface GenericPublication extends GenericResource { /** * Generic Remote shared across Pulp Remote plugins. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/repository.py#L85 * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/base.py#L612 * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/serializers/base.py#L367 @@ -78,7 +78,7 @@ interface GenericRemote extends GenericResource { name: string; url: string; policy?: string; - readonly hidden_fields?: { name: string; is_set: boolean; }[]; + readonly hidden_fields?: { name: string; is_set: boolean }[]; ca_cert?: string | null; client_cert?: string | null; client_key?: string | null; @@ -107,7 +107,7 @@ interface GenericRemote extends GenericResource { /** * Last Sync Task Type. * - * @see https://github.com/pulp/pulp_ansible/blob/main/pulp_ansible/app/utils.py + * @see https://github.com/pulp/pulp_ansible/blob/0043923641fc7fd3893f8489fd29ff04addc9d71/pulp_ansible/app/utils.py#L47 */ interface AnsibleLastSyncType { pk: string; @@ -117,4 +117,11 @@ interface AnsibleLastSyncType { error: TaskErrorType | null; } -export type { TaskErrorType, GenericRepository, GenericDistribution, GenericPublication, GenericRemote, AnsibleLastSyncType }; +export type { + TaskErrorType, + GenericRepository, + GenericDistribution, + GenericPublication, + GenericRemote, + AnsibleLastSyncType, +}; diff --git a/src/api/file-distribution.ts b/src/api/file-distribution.ts index 336aae5d..179f1505 100644 --- a/src/api/file-distribution.ts +++ b/src/api/file-distribution.ts @@ -3,7 +3,7 @@ import { PulpAPI } from './pulp'; /** * File Distribution Type. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L225 */ interface FileDistributionType extends GenericDistribution { @@ -21,4 +21,4 @@ export const FileDistributionAPI = { list: (params?) => base.list(`distributions/file/file/`, params), }; -export type { FileDistributionType } +export type { FileDistributionType }; diff --git a/src/api/file-publication.ts b/src/api/file-publication.ts index 624f96c9..30f7a071 100644 --- a/src/api/file-publication.ts +++ b/src/api/file-publication.ts @@ -3,7 +3,7 @@ import { PulpAPI } from './pulp'; /** * File Publication Type. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L200 */ interface FilePublicationType extends GenericPublication { diff --git a/src/api/file-remote.ts b/src/api/file-remote.ts index fc8c486e..ef4f2520 100644 --- a/src/api/file-remote.ts +++ b/src/api/file-remote.ts @@ -1,34 +1,16 @@ +import type { GenericRemote } from './common'; import { PulpAPI } from './pulp'; -export class FileRemoteType { - ca_cert: string; - client_cert: string; - download_concurrency: number; - name: string; - proxy_url: string; - pulp_href?: string; - rate_limit: number; - tls_validation: boolean; - url: string; - sync_dependencies?: boolean; - - // connect_timeout - // headers - // max_retries - // policy - // prn - // pulp_created - // pulp_labels - // pulp_last_updated - // sock_connect_timeout - // sock_read_timeout - // total_timeout - - hidden_fields: { - is_set: boolean; - name: string; - }[]; - +/** + * File Remote Type. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L165 + */ +interface FileRemoteType extends GenericRemote { + /** + * NOTE: Not part of the Ansible serializer, populated separately. + * This should be broken out into its own type and extend the interface. + */ my_permissions?: string[]; } @@ -62,3 +44,5 @@ export const FileRemoteAPI = { smartUpdate: (id, newValue: FileRemoteType, oldValue: FileRemoteType) => base.http.put(`remotes/file/file/${id}/`, smartUpdate(newValue, oldValue)), }; + +export type { FileRemoteType }; diff --git a/src/api/file-repository.ts b/src/api/file-repository.ts index 59cb65ad..44fffbeb 100644 --- a/src/api/file-repository.ts +++ b/src/api/file-repository.ts @@ -3,7 +3,7 @@ import { PulpAPI } from './pulp'; /** * Type for syncing metadata set on FileRepository after each sync. - * @see https://github.com/pulp/pulpcore/blob/main/pulp_file/app/tasks/synchronizing.py + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/tasks/synchronizing.py#L97 */ interface FileLastSyncDetailsType { remote_pk: string; @@ -17,7 +17,7 @@ interface FileLastSyncDetailsType { /** * File Repository Type. * - * @see https://github.com/pulp/pulpcore/blob/main/pulp_file/app/serializers.py + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulp_file/app/serializers.py#L122 */ interface FileRepositoryType extends GenericRepository { autopublish?: boolean; From 8ccab4074ef4718f041f31b3e53cce1462e49377 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 2 Jul 2026 19:59:00 +0000 Subject: [PATCH 10/16] Updated Container distributions to have new interface against plugin --- src/api/container-distribution.ts | 40 +++++++++++++++++++++++++++++++ src/api/file-remote.ts | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/api/container-distribution.ts b/src/api/container-distribution.ts index 7bd1da77..b1213a15 100644 --- a/src/api/container-distribution.ts +++ b/src/api/container-distribution.ts @@ -1,5 +1,43 @@ +import type { GenericDistribution } from './common'; import { PulpAPI } from './pulp'; +/** + * Container Distribution Type. + * + * @see https://github.com/pulp/pulp_container/blob/b109af03cb7aae3431f7a73204e6a73a8457694c/pulp_container/app/serializers.py#L424 + */ +interface ContainerDistributionType extends Omit< + GenericDistribution, + 'base_url' +> { + readonly registry_path: string; + content_guard?: string; + readonly namespace?: string; + description?: string | null; + repository_version?: string | null; + readonly remote?: string; + private?: boolean; + readonly pulp_domain?: string; +} + +/** + * Container Pull Through Type. + * + * @see https://github.com/pulp/pulp_container/blob/b109af03cb7aae3431f7a73204e6a73a8457694c/pulp_container/app/serializers.py#L520 + */ +interface ContainerPullThroughDistributionType extends Omit< + GenericDistribution, + 'base_url' +> { + remote: string; + readonly namespace?: string; + content_guard?: string; + distributions?: string[]; + description?: string | null; + private?: boolean; + readonly pulp_domain?: string; +} + const base = new PulpAPI(); export const ContainerDistributionAPI = { @@ -17,3 +55,5 @@ export const ContainerPullThroughDistributionAPI = { // We should probably put this into a field on the serializer url: (distro_data) => `${window.location.host}/${distro_data.base_path}/`, }; + +export type { ContainerDistributionType, ContainerPullThroughDistributionType }; diff --git a/src/api/file-remote.ts b/src/api/file-remote.ts index ef4f2520..f5a1d7d2 100644 --- a/src/api/file-remote.ts +++ b/src/api/file-remote.ts @@ -8,7 +8,7 @@ import { PulpAPI } from './pulp'; */ interface FileRemoteType extends GenericRemote { /** - * NOTE: Not part of the Ansible serializer, populated separately. + * NOTE: Not part of the File serializer, populated separately. * This should be broken out into its own type and extend the interface. */ my_permissions?: string[]; From 8f80c3d64f066a8f7f6d629864c641af7ec76da5 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:56:19 +0000 Subject: [PATCH 11/16] Built out RPM Repository API Client with Typed List endpoint --- src/api/common.ts | 86 +++++++++++++++++++++++++++++++ src/api/plugins/rpm/client.ts | 8 +++ src/api/plugins/rpm/repository.ts | 67 ++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 src/api/plugins/rpm/client.ts create mode 100644 src/api/plugins/rpm/repository.ts diff --git a/src/api/common.ts b/src/api/common.ts index a5f64b81..c3939ef7 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -98,6 +98,90 @@ interface GenericRemote extends GenericResource { rate_limit?: number | null; } +/** + * Generic Filters shared across PulpCore & Plugin Endpoints. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/filters.py#L290 + */ +interface GenericFilterParams { + pulp_id__in?: string; + pulp_href__in?: string; + prn__in?: string; + q?: string; + exclude_fields?: string; + fields?: string; + limit?: number; + minimal?: boolean; + offset?: number; + page_size?: number; + ordering?: string; + format?: string; +} + +type LookupFilterParams< + Field extends string, + Lookup extends string, + Value, +> = Partial>; +type NameFilterOptions = + | 'iexact' + | 'in' + | 'contains' + | 'icontains' + | 'startswith' + | 'istartswith' + | 'regex' + | 'iregex'; +type NullableNumericFilterOptions = + | 'ne' + | 'lt' + | 'lte' + | 'gt' + | 'gte' + | 'range' + | 'isnull'; +type NameFilterParams = LookupFilterParams<'name', NameFilterOptions, string>; +type RetainRepoVersionsFilterParams = LookupFilterParams< + 'retain_repo_versions', + NullableNumericFilterOptions, + number | string +>; +type RetainCheckpointsFilterParams = LookupFilterParams< + 'retain_checkpoints', + NullableNumericFilterOptions, + number | string +>; + +/** + * Generic Repository Filters shared across PulpCore & Plugin Endpoints. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/viewsets/repository.py#L88 + */ +interface GenericRepositoryFilterParams + extends + GenericFilterParams, + NameFilterParams, + RetainRepoVersionsFilterParams, + RetainCheckpointsFilterParams { + pulp_label_select?: string; + remote?: string | null; + with_content?: string; + latest_with_content?: string; +} + +/** + * Generic Paginated Response shared across PulpCore & Plugin Endpoints. + * + * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/settings.py#L186 + * @see https://github.com/encode/django-rest-framework/blob/6f0b74def3fcc81e126b87b08e59abdb6c2ad056/rest_framework/pagination.py#L364 + */ +interface GenericPaginatedResponse { + count: number; + next: string | null; + previous: string | null; + results: TResult; +} + /** * -------------------- * These are shared Plugin Types outside the PulpCore Generics. @@ -123,5 +207,7 @@ export type { GenericDistribution, GenericPublication, GenericRemote, + GenericRepositoryFilterParams, + GenericPaginatedResponse, AnsibleLastSyncType, }; diff --git a/src/api/plugins/rpm/client.ts b/src/api/plugins/rpm/client.ts new file mode 100644 index 00000000..ca9694bf --- /dev/null +++ b/src/api/plugins/rpm/client.ts @@ -0,0 +1,8 @@ +import { PulpAPI } from "src/api/pulp"; +import { createRepositoryAPI } from "./repository"; + +class RpmClient extends PulpAPI { + repository = createRepositoryAPI(this); +} + +export { RpmClient }; diff --git a/src/api/plugins/rpm/repository.ts b/src/api/plugins/rpm/repository.ts new file mode 100644 index 00000000..9af76e3c --- /dev/null +++ b/src/api/plugins/rpm/repository.ts @@ -0,0 +1,67 @@ +import type { AxiosResponse } from 'axios'; +import type { + GenericPaginatedResponse, + GenericRepository, + GenericRepositoryFilterParams, +} from '../../common'; +import type { PulpAPI } from '../../pulp'; + +type RPMChecksumType = + | 'unknown' + | 'md5' + | 'sha' + | 'sha1' + | 'sha224' + | 'sha256' + | 'sha384' + | 'sha512'; +type RPMCompressionType = 'zstd' | 'gz' | 'none'; +type RPMLayoutType = 'nested_alphabetically' | 'flat' | 'nested_by_digest'; + +/** + * RPM Repository Type. + * + * @see https://github.com/pulp/pulp_rpm/blob/dc333a99db6c44d70d6103540cb592f6e55a8682/pulp_rpm/app/serializers/repository.py#L177 + */ +interface RPMRepositoryType extends GenericRepository { + autopublish?: boolean; + metadata_signing_service?: string | null; + package_signing_service?: string | null; + package_signing_fingerprint?: string | null; + retain_package_versions?: number; + checksum_type?: RPMChecksumType | null; + compression_type?: RPMCompressionType | null; + layout?: RPMLayoutType | null; + repo_config?: Record; + osv_config?: { name: string; releases: unknown }[] | null; +} + +interface RPMRepositoryClient { + list: ( + params?: GenericRepositoryFilterParams, + // FIXME: Move AxiosResponse type to PulpAPI Base Class. + ) => Promise>>; + // retrieve: () => Promise; + // create: () => Promise; + // update: () => Promise; +} + +/** + * RPM Repository API Client + * @param {PulpAPI} base + * @returns {RPMRepositoryClient} + * + * @see https://github.com/pulp/pulp_rpm/blob/dc333a99db6c44d70d6103540cb592f6e55a8682/pulp_rpm/app/viewsets/repository.py#L76 + */ +function createRepositoryAPI(base: PulpAPI): RPMRepositoryClient { + return { + list: (params?: GenericRepositoryFilterParams) => + base.list(`repositories/rpm/rpm/`, params), + // retrieve: () => {}, + // create: () => {}, + // update: () => {}, + }; +} + +export { createRepositoryAPI }; +export type { RPMRepositoryType }; From eb4b32618e458310b13f33c8f7a349b703b08d97 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:47:12 +0000 Subject: [PATCH 12/16] Added integration testing against RPM Repository API Client using node:test --- package.json | 4 ++ src/api/plugins/rpm/repository.test.ts | 91 ++++++++++++++++++++++++++ tsconfig.json | 1 + 3 files changed, 96 insertions(+) create mode 100644 src/api/plugins/rpm/repository.test.ts diff --git a/package.json b/package.json index 97f553a9..3b399f49 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,10 @@ "sort-exports": "perl -i -pe 's/^export/import/' src/**/index.ts ; npm run prettier ; perl -i -pe 's/^import/export/' src/**/index.ts", "start": "NODE_ENV=development webpack serve --config config/start.config.js", "test": "true", + "test:check-ts": "node -e \"process.versions.node.localeCompare('22.10.0',undefined,{numeric:true})>=0||(console.error('Testing uses feature flags requiring Node >= 22.10.0, but is using '+process.versions.node),process.exit(1))\"", + "test:unit": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Unit: '", + "test:integration": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Integration: '", + "test:coverage": "npm run test:check-ts && node --test --experimental-strip-types --experimental-test-coverage --test-coverage-exclude '**/*.test.ts'", "upgrade": "npx npm-check-updates -u -t minor" }, "engines": { diff --git a/src/api/plugins/rpm/repository.test.ts b/src/api/plugins/rpm/repository.test.ts new file mode 100644 index 00000000..f19b7fb7 --- /dev/null +++ b/src/api/plugins/rpm/repository.test.ts @@ -0,0 +1,91 @@ +import assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; +import type { PulpAPI } from 'src/api/pulp'; +import { createRepositoryAPI } from './repository.ts'; + +const baseUrl: string = + process.env.PULP_BASE_URL ?? 'http://localhost:8080/pulp/api/v3/'; +const testUsername: string = process.env.PULP_USERNAME ?? 'admin'; +const testPassword: string = process.env.PULP_PASSWORD ?? 'admin'; +const authorization: string = + 'Basic ' + Buffer.from(`${testUsername}:${testPassword}`).toString('base64'); + +async function testFetch( + path: string, + init: RequestInit = {}, +): Promise { + const url: URL = new URL(path, baseUrl); + const res: Response = await fetch(url, { + ...init, + headers: { + authorization, + 'content-type': 'application/json', + ...init.headers, + }, + }); + + return res.json(); +} + +function buildQueryParams(params?: Record): string { + const search = new URLSearchParams(); + + for (const [key, value] of Object.entries(params ?? {})) { + if (value !== undefined) { + search.set(key, String(value)); + } + } + + const queryString = search.toString(); + return queryString ? `?${queryString}` : ''; +} + +function testClient(): PulpAPI { + return { + list: (url: string, params?: Record) => + testFetch(`${url}${buildQueryParams(params)}`).then((data) => ({ data })), + } as unknown as PulpAPI; +} + +describe('Integration: RPM Repository API Client', () => { + let createdHref: string; + + before(async () => { + const repo = await testFetch('repositories/rpm/rpm/', { + method: 'POST', + body: JSON.stringify({ name: 'test-rpm-repo-existent' }), + }); + // @ts-ignore + createdHref = repo.pulp_href; + + if (!createdHref) { + throw new Error() + } + }); + + after(async () => { + await testFetch(createdHref, { + method: 'DELETE', + }); + }); + + it('list() finds the created repository by exact name', async () => { + const client = createRepositoryAPI(testClient()); + + const res = await client.list({ name: 'test-rpm-repo-existent' }); + + assert.strictEqual(res.data.count, 1); + assert.strictEqual(res.data.count, res.data.results.length); + assert.strictEqual(res.data.results[0].name, 'test-rpm-repo-existent'); + assert.strictEqual(res.data.results[0].pulp_href, createdHref); + }); + + it('list() when passed a non-existent repository name returns empty results array', async () => { + const client = createRepositoryAPI(testClient()); + + const res = await client.list({ name: 'test-rpm-repo-non-existent' }); + + assert.strictEqual(res.data.count, 0); + assert.strictEqual(res.data.count, res.data.results.length); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 934db47d..00ff66ef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "lib": ["es2021", "dom"], "module": "es2020", "moduleResolution": "node", + "allowImportingTsExtensions": true, "noImplicitAny": false, "outDir": "./dist/", "paths": { From dfa58c66bf0401763906f8a861db56eba5c54f43 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:33:06 +0000 Subject: [PATCH 13/16] Refined test suite to using Axios instead of node:fetch, Fixed minor issues with types --- src/api/common.ts | 2 +- src/api/plugins/rpm/repository.test.ts | 155 +++++++++++++---------- src/api/plugins/rpm/repository.ts | 12 +- src/api/test-utils/integration-client.ts | 58 +++++++++ 4 files changed, 153 insertions(+), 74 deletions(-) create mode 100644 src/api/test-utils/integration-client.ts diff --git a/src/api/common.ts b/src/api/common.ts index c3939ef7..c6a2a5ef 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -179,7 +179,7 @@ interface GenericPaginatedResponse { count: number; next: string | null; previous: string | null; - results: TResult; + results: TResult[]; } /** diff --git a/src/api/plugins/rpm/repository.test.ts b/src/api/plugins/rpm/repository.test.ts index f19b7fb7..e51c6d80 100644 --- a/src/api/plugins/rpm/repository.test.ts +++ b/src/api/plugins/rpm/repository.test.ts @@ -1,91 +1,110 @@ +import { isAxiosError } from 'axios'; import assert from 'node:assert/strict'; import { after, before, describe, it } from 'node:test'; -import type { PulpAPI } from 'src/api/pulp'; +import { + testAxiosClient, + testPulpAPI, +} from 'src/api/test-utils/integration-client.ts'; import { createRepositoryAPI } from './repository.ts'; -const baseUrl: string = - process.env.PULP_BASE_URL ?? 'http://localhost:8080/pulp/api/v3/'; -const testUsername: string = process.env.PULP_USERNAME ?? 'admin'; -const testPassword: string = process.env.PULP_PASSWORD ?? 'admin'; -const authorization: string = - 'Basic ' + Buffer.from(`${testUsername}:${testPassword}`).toString('base64'); - -async function testFetch( - path: string, - init: RequestInit = {}, -): Promise { - const url: URL = new URL(path, baseUrl); - const res: Response = await fetch(url, { - ...init, - headers: { - authorization, - 'content-type': 'application/json', - ...init.headers, - }, - }); +describe('Integration: RPM Repository API Client', () => { + describe('RPM Repository - list()', () => { + const testRpmRepoName = 'test-rpm-repo-existent-list'; + let repositoryHref: string; + + before(async () => { + const res = await testAxiosClient('repositories/rpm/rpm/', { + method: 'POST', + data: JSON.stringify({ name: testRpmRepoName }), + }); + + // TODO: Update Error Message and Error Check + if (res.status === 500 || !res.data.pulp_href) { + throw new Error(); + } + + repositoryHref = res.data.pulp_href; + }); - return res.json(); -} + after(async () => { + await testAxiosClient(repositoryHref, { method: 'DELETE' }); + }); -function buildQueryParams(params?: Record): string { - const search = new URLSearchParams(); + it('list() find the created repository by exact name', async () => { + const client = createRepositoryAPI(testPulpAPI()); - for (const [key, value] of Object.entries(params ?? {})) { - if (value !== undefined) { - search.set(key, String(value)); - } - } + const res = await client.list({ name: testRpmRepoName }); - const queryString = search.toString(); - return queryString ? `?${queryString}` : ''; -} + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.count, 1); + assert.strictEqual(res.data.count, res.data.results.length); + assert.strictEqual(res.data.results[0].name, testRpmRepoName); + assert.strictEqual(res.data.results[0].pulp_href, repositoryHref); + }); -function testClient(): PulpAPI { - return { - list: (url: string, params?: Record) => - testFetch(`${url}${buildQueryParams(params)}`).then((data) => ({ data })), - } as unknown as PulpAPI; -} + it('list() when passed a non-existent repository name returns empty result', async () => { + const nonExistentRepositoryName = 'test-rpm-repo-non-existent-list'; + const client = createRepositoryAPI(testPulpAPI()); -describe('Integration: RPM Repository API Client', () => { - let createdHref: string; + const res = await client.list({ name: nonExistentRepositoryName }); - before(async () => { - const repo = await testFetch('repositories/rpm/rpm/', { - method: 'POST', - body: JSON.stringify({ name: 'test-rpm-repo-existent' }), + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.count, 0); + assert.strictEqual(res.data.results.length, 0); + assert.strictEqual(res.data.count, res.data.results.length); }); - // @ts-ignore - createdHref = repo.pulp_href; - - if (!createdHref) { - throw new Error() - } }); - after(async () => { - await testFetch(createdHref, { - method: 'DELETE', + describe('RPM Repository - retrieve()', () => { + const testRpmRepoName = 'test-rpm-repo-existent-retrieve'; + let repositoryPrn: string; + let repositoryHref: string; + + before(async () => { + const res = await testAxiosClient('repositories/rpm/rpm/', { + method: 'POST', + data: JSON.stringify({ name: testRpmRepoName }), + }); + + // TODO: Update Error Message and Error Check + if (res.status === 500 || !res.data.prn) { + throw new Error(); + } + + repositoryPrn = res.data.prn; + repositoryHref = res.data.pulp_href; }); - }); - it('list() finds the created repository by exact name', async () => { - const client = createRepositoryAPI(testClient()); + after(async () => { + await testAxiosClient(repositoryHref, { method: 'DELETE' }); + }); - const res = await client.list({ name: 'test-rpm-repo-existent' }); + it('retrieve() when passed correct identifier returns expected repository', async () => { + const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; + assert.strictEqual(typeof repositoryIdentifier, 'string'); - assert.strictEqual(res.data.count, 1); - assert.strictEqual(res.data.count, res.data.results.length); - assert.strictEqual(res.data.results[0].name, 'test-rpm-repo-existent'); - assert.strictEqual(res.data.results[0].pulp_href, createdHref); - }); + const client = createRepositoryAPI(testPulpAPI()); - it('list() when passed a non-existent repository name returns empty results array', async () => { - const client = createRepositoryAPI(testClient()); + const res = await client.retrieve(repositoryIdentifier); - const res = await client.list({ name: 'test-rpm-repo-non-existent' }); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.data.prn, repositoryPrn); + assert.strictEqual(res.data.name, testRpmRepoName); + }); + + it('retrieve() when passed an non-existent identifier returns empty object', async () => { + const nonExistentRepositoryIdentifier = '1234567890'; - assert.strictEqual(res.data.count, 0); - assert.strictEqual(res.data.count, res.data.results.length); + const client = createRepositoryAPI(testPulpAPI()); + + await assert.rejects( + () => client.retrieve(nonExistentRepositoryIdentifier), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 404); + return true; + }, + ); + }); }); }); diff --git a/src/api/plugins/rpm/repository.ts b/src/api/plugins/rpm/repository.ts index 9af76e3c..0359e274 100644 --- a/src/api/plugins/rpm/repository.ts +++ b/src/api/plugins/rpm/repository.ts @@ -36,12 +36,14 @@ interface RPMRepositoryType extends GenericRepository { osv_config?: { name: string; releases: unknown }[] | null; } +// FIXME: Move AxiosResponse type to PulpAPI Base Class. interface RPMRepositoryClient { list: ( params?: GenericRepositoryFilterParams, - // FIXME: Move AxiosResponse type to PulpAPI Base Class. - ) => Promise>>; - // retrieve: () => Promise; + ) => Promise>>; + retrieve: ( + id: string, + ) => Promise>; // create: () => Promise; // update: () => Promise; } @@ -55,9 +57,9 @@ interface RPMRepositoryClient { */ function createRepositoryAPI(base: PulpAPI): RPMRepositoryClient { return { - list: (params?: GenericRepositoryFilterParams) => + list: (params?) => base.list(`repositories/rpm/rpm/`, params), - // retrieve: () => {}, + retrieve: (id) => base.http.get(`repositories/rpm/rpm/${id}`), // create: () => {}, // update: () => {}, }; diff --git a/src/api/test-utils/integration-client.ts b/src/api/test-utils/integration-client.ts new file mode 100644 index 00000000..5fa92855 --- /dev/null +++ b/src/api/test-utils/integration-client.ts @@ -0,0 +1,58 @@ +import axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios'; +import { type PulpAPI } from '../pulp'; + +const baseUrl: string = + process.env.PULP_BASE_URL ?? 'http://localhost:8080/pulp/api/v3/'; +const testUsername: string = process.env.PULP_USERNAME ?? 'admin'; +const testPassword: string = process.env.PULP_PASSWORD ?? 'admin'; +const authorization: string = + 'Basic ' + Buffer.from(`${testUsername}:${testPassword}`).toString('base64'); + +async function testAxiosClient( + path: string, + config: AxiosRequestConfig = {}, +): Promise { + const url: string = new URL(path, baseUrl).toString(); + const axiosRequest = { + ...config, + url, + headers: { + Authorization: authorization, + 'Content-Type': 'application/json', + ...config.headers, + }, + } satisfies AxiosRequestConfig; + const res = await axios.request(axiosRequest); + + return res; +} + +function testPulpAPI(): PulpAPI { + return { + list: (url: string, params?: Record) => + testAxiosClient(`${url}${buildQueryParams(params)}`, { + method: 'GET', + }), + http: { + get: (url: string, config: { params?: Record }) => + testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { + method: 'GET', + }), + }, + } as unknown as PulpAPI; +} + +function buildQueryParams(params?: Record): string { + const search = new URLSearchParams(); + + for (const [key, value] of Object.entries(params ?? {})) { + if (value !== undefined) { + search.set(key, String(value)); + } + } + + const queryString = search.toString(); + return queryString ? `?${queryString}` : ''; +} + +export { testPulpAPI, testAxiosClient }; From c92abffb212aa2547f95cf2a8a22f504387176fc Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:56:36 +0000 Subject: [PATCH 14/16] Added create & update on RPM repositories with associated testing --- src/api/plugins/rpm/repository.test.ts | 227 ++++++++++++++++++++++- src/api/plugins/rpm/repository.ts | 39 +++- src/api/test-utils/integration-client.ts | 12 +- 3 files changed, 263 insertions(+), 15 deletions(-) diff --git a/src/api/plugins/rpm/repository.test.ts b/src/api/plugins/rpm/repository.test.ts index e51c6d80..2f3381b8 100644 --- a/src/api/plugins/rpm/repository.test.ts +++ b/src/api/plugins/rpm/repository.test.ts @@ -1,11 +1,15 @@ import { isAxiosError } from 'axios'; import assert from 'node:assert/strict'; -import { after, before, describe, it } from 'node:test'; +import { after, afterEach, before, beforeEach, describe, it } from 'node:test'; import { testAxiosClient, testPulpAPI, -} from 'src/api/test-utils/integration-client.ts'; -import { createRepositoryAPI } from './repository.ts'; +} from '../../test-utils/integration-client.ts'; +import { + type RPMRepositoryType, + type RPMRepositoryUpsertType, + createRepositoryAPI, +} from './repository.ts'; describe('Integration: RPM Repository API Client', () => { describe('RPM Repository - list()', () => { @@ -19,7 +23,7 @@ describe('Integration: RPM Repository API Client', () => { }); // TODO: Update Error Message and Error Check - if (res.status === 500 || !res.data.pulp_href) { + if (!res.data.pulp_href) { throw new Error(); } @@ -67,7 +71,7 @@ describe('Integration: RPM Repository API Client', () => { }); // TODO: Update Error Message and Error Check - if (res.status === 500 || !res.data.prn) { + if (!res.data.prn || !res.data.pulp_href) { throw new Error(); } @@ -107,4 +111,217 @@ describe('Integration: RPM Repository API Client', () => { ); }); }); + + describe('RPM Repository - create()', () => { + let repositoryHref: string | undefined; + + afterEach(async () => { + if (repositoryHref) { + await testAxiosClient(repositoryHref, { method: 'DELETE' }); + repositoryHref = undefined; + } + }); + + it('create() when giving only the repository name returns with the created repository defaults', async () => { + const testRpmRepoName = 'test-rpm-repo-existent-create-minimal'; + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.create({ name: testRpmRepoName }); + assert.notStrictEqual(res.data.pulp_href, undefined); + repositoryHref = res.data.pulp_href; + + assert.strictEqual(res.status, 201); + assert.strictEqual(res.data.name, testRpmRepoName); + assert.strictEqual(res.data.autopublish, false); + assert.strictEqual(res.data.retain_package_versions, 0); + assert.strictEqual(res.data.checksum_type, null); + }); + + it('create() when giving additional data fields from required returns them back on created repository', async () => { + const testRpmRepoName = 'test-rpm-existent-create-full'; + const client = createRepositoryAPI(testPulpAPI()); + const payload = { + name: testRpmRepoName, + autopublish: true, + retain_package_versions: 3, + checksum_type: 'sha256', + description: 'This is a test description', + } satisfies RPMRepositoryType; + + const res = await client.create(payload); + assert.notStrictEqual(res.data.pulp_href, repositoryHref); + repositoryHref = res.data.pulp_href; + + assert.strictEqual(res.status, 201); + assert.strictEqual(res.data.autopublish, true); + assert.strictEqual(res.data.name, testRpmRepoName); + assert.strictEqual(res.data.retain_package_versions, 3); + assert.strictEqual(res.data.checksum_type, 'sha256'); + assert.strictEqual(res.data.description, 'This is a test description'); + }); + + it('create() when passed with a duplicate name returns 400', async () => { + const testRpmRepoName = 'test-rpm-existent-create-duplicate'; + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.create({ name: testRpmRepoName }); + assert.notStrictEqual(res.data.pulp_href, undefined); + repositoryHref = res.data.pulp_href; + + await assert.rejects( + () => client.create({ name: testRpmRepoName }), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 400); + return true; + }, + ); + }); + + it('create() when missing the required name field returns 400', async () => { + const client = createRepositoryAPI(testPulpAPI()); + + await assert.rejects( + () => client.create({} as RPMRepositoryUpsertType), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 400); + return true; + }, + ); + }); + + it('create() when an invalid field is passed returns 400', async () => { + const testRpmRepoName = 'test-rpm-existent-create-duplicate'; + const client = createRepositoryAPI(testPulpAPI()); + const invalidPayload = { + name: testRpmRepoName, + compression_type: + 'I am a test' as RPMRepositoryUpsertType['compression_type'], + }; + + await assert.rejects( + () => client.create(invalidPayload), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 400); + return true; + }, + ); + }); + + it('create() when a not allowed checksum_type is passed returns 400', async () => { + const testRpmRepoName = 'test-rpm-existent-create-duplicate'; + const client = createRepositoryAPI(testPulpAPI()); + const invalidPayload = { + name: testRpmRepoName, + checksum_type: 'md5' as RPMRepositoryUpsertType['checksum_type'], + }; + + await assert.rejects( + () => client.create(invalidPayload), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 400); + return true; + }, + ); + }); + }); + + describe('RPM Repository - update()', () => { + let repositoryHref: string | undefined; + let repositoryPrn: string | undefined; + + beforeEach(async () => { + const res = await testAxiosClient('repositories/rpm/rpm/', { + method: 'POST', + data: JSON.stringify({ name: 'test-rpm-repo-update' }), + }); + + // TODO: Update Error Message and Error Check + if (!res.data.prn || !res.data.pulp_href) { + throw new Error(); + } + + repositoryHref = res.data.pulp_href; + repositoryPrn = res.data.prn; + }); + + afterEach(async () => { + await testAxiosClient(repositoryHref, { method: 'DELETE' }); + repositoryHref = undefined; + repositoryPrn = undefined; + }); + + it('update() with a single changed field updates only that field', async () => { + const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; + assert.strictEqual(typeof repositoryIdentifier, 'string'); + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.update(repositoryIdentifier, { + description: 'Updated description', + }); + const expected = await client.retrieve(repositoryIdentifier); + + assert.strictEqual(res.status, 202); + assert.strictEqual(expected.data.description, 'Updated description'); + }); + + it('update() with multiple changed fields updates all associated', async () => { + const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; + assert.strictEqual(typeof repositoryPrn, 'string'); + const client = createRepositoryAPI(testPulpAPI()); + const payload = { + autopublish: true, + retain_package_versions: 5, + checksum_type: 'sha256', + compression_type: 'zstd', + } satisfies Partial; + + const res = await client.update(repositoryIdentifier, payload); + const actual = await client.retrieve(repositoryIdentifier); + + assert.strictEqual(res.status, 202); + assert.strictEqual(actual.data.autopublish, true); + assert.strictEqual(actual.data.retain_package_versions, 5); + assert.strictEqual(actual.data.checksum_type, 'sha256'); + assert.strictEqual(actual.data.compression_type, 'zstd'); + }); + + it('update() when a not allowed checksum_type is passed returns 400', async () => { + const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; + assert.strictEqual(typeof repositoryPrn, 'string'); + const client = createRepositoryAPI(testPulpAPI()); + const invalidPayload = { + checksum_type: 'sha1' as RPMRepositoryUpsertType['checksum_type'], + }; + + await assert.rejects( + () => client.update(repositoryIdentifier, invalidPayload), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 400); + return true; + }, + ); + }); + + it('update() when passed a non-existent identifier returns 404', async () => { + const nonExistentRepositoryIdentifier = '1234567890'; + const client = createRepositoryAPI(testPulpAPI()); + + await assert.rejects( + () => + client.update(nonExistentRepositoryIdentifier, { + description: 'This will not update', + }), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 404); + return true; + }, + ); + }); + }); }); diff --git a/src/api/plugins/rpm/repository.ts b/src/api/plugins/rpm/repository.ts index 0359e274..0512f485 100644 --- a/src/api/plugins/rpm/repository.ts +++ b/src/api/plugins/rpm/repository.ts @@ -15,6 +15,7 @@ type RPMChecksumType = | 'sha256' | 'sha384' | 'sha512'; +type RPMAllowedUpsertChecksumsType = 'sha256' | 'sha384' | 'sha512'; type RPMCompressionType = 'zstd' | 'gz' | 'none'; type RPMLayoutType = 'nested_alphabetically' | 'flat' | 'nested_by_digest'; @@ -36,16 +37,34 @@ interface RPMRepositoryType extends GenericRepository { osv_config?: { name: string; releases: unknown }[] | null; } +/** + * RPM Create / Update Type. + * + * @see https://github.com/pulp/pulp_rpm/blob/dc333a99db6c44d70d6103540cb592f6e55a8682/pulp_rpm/app/serializers/repository.py#L326 + */ +interface RPMRepositoryUpsertType extends Omit< + RPMRepositoryType, + 'checksum_type' +> { + checksum_type?: RPMAllowedUpsertChecksumsType | null; +} + // FIXME: Move AxiosResponse type to PulpAPI Base Class. interface RPMRepositoryClient { list: ( params?: GenericRepositoryFilterParams, ) => Promise>>; - retrieve: ( + retrieve: (id: string) => Promise>; + create: ( + data: RPMRepositoryUpsertType, + ) => Promise>; + update: ( id: string, - ) => Promise>; - // create: () => Promise; - // update: () => Promise; + data: Partial, + // TODO: Update Reponse Type + ) => Promise>; + // delete: () => Promise; + // sync: () => Promise; } /** @@ -57,13 +76,15 @@ interface RPMRepositoryClient { */ function createRepositoryAPI(base: PulpAPI): RPMRepositoryClient { return { - list: (params?) => - base.list(`repositories/rpm/rpm/`, params), + list: (params?) => base.list(`repositories/rpm/rpm/`, params), retrieve: (id) => base.http.get(`repositories/rpm/rpm/${id}`), - // create: () => {}, - // update: () => {}, + create: (data) => base.http.post(`repositories/rpm/rpm/`, data), + update: (id, data) => + base.http.patch(`repositories/rpm/rpm/${id}`, { data }), + // delete: () => {}, + // sync: () => {}, }; } export { createRepositoryAPI }; -export type { RPMRepositoryType }; +export type { RPMRepositoryType, RPMRepositoryUpsertType }; diff --git a/src/api/test-utils/integration-client.ts b/src/api/test-utils/integration-client.ts index 5fa92855..07f77a81 100644 --- a/src/api/test-utils/integration-client.ts +++ b/src/api/test-utils/integration-client.ts @@ -1,5 +1,5 @@ import axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios'; -import { type PulpAPI } from '../pulp'; +import type { PulpAPI } from '../pulp.ts'; const baseUrl: string = process.env.PULP_BASE_URL ?? 'http://localhost:8080/pulp/api/v3/'; @@ -38,6 +38,16 @@ function testPulpAPI(): PulpAPI { testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { method: 'GET', }), + post: (url: string, data: unknown) => + testAxiosClient(url, { method: 'POST', data }), + patch: ( + url: string, + config: { params?: Record; data: unknown }, + ) => + testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { + method: 'PATCH', + data: config.data, + }), }, } as unknown as PulpAPI; } From a094275826daefe797b01b1b81519b1c4c7c91ad Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Fri, 10 Jul 2026 13:26:09 +0000 Subject: [PATCH 15/16] Added repository delete with testing helpers to handle dispatch task from pulp core --- package.json | 2 +- src/api/plugins/rpm/repository.test.ts | 100 +++++++++++++++++++++-- src/api/plugins/rpm/repository.ts | 14 ++-- src/api/test-utils/integration-client.ts | 37 ++++++++- 4 files changed, 135 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 3b399f49..429b78c1 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "test:check-ts": "node -e \"process.versions.node.localeCompare('22.10.0',undefined,{numeric:true})>=0||(console.error('Testing uses feature flags requiring Node >= 22.10.0, but is using '+process.versions.node),process.exit(1))\"", "test:unit": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Unit: '", "test:integration": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Integration: '", - "test:coverage": "npm run test:check-ts && node --test --experimental-strip-types --experimental-test-coverage --test-coverage-exclude '**/*.test.ts'", + "test:coverage": "npm run test:check-ts && node --test --experimental-strip-types --experimental-test-coverage --test-coverage-exclude '**/*.test.ts' --test-coverage-exclude 'src/api/test-utils/**'", "upgrade": "npx npm-check-updates -u -t minor" }, "engines": { diff --git a/src/api/plugins/rpm/repository.test.ts b/src/api/plugins/rpm/repository.test.ts index 2f3381b8..527ead0c 100644 --- a/src/api/plugins/rpm/repository.test.ts +++ b/src/api/plugins/rpm/repository.test.ts @@ -4,6 +4,7 @@ import { after, afterEach, before, beforeEach, describe, it } from 'node:test'; import { testAxiosClient, testPulpAPI, + waitForTaskCompletion, } from '../../test-utils/integration-client.ts'; import { type RPMRepositoryType, @@ -254,21 +255,43 @@ describe('Integration: RPM Repository API Client', () => { repositoryPrn = undefined; }); - it('update() with a single changed field updates only that field', async () => { + it('update() when updating a field with the same value returns 200', async () => { + const sameNameValue = 'test-rpm-repo-update'; const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; assert.strictEqual(typeof repositoryIdentifier, 'string'); const client = createRepositoryAPI(testPulpAPI()); const res = await client.update(repositoryIdentifier, { - description: 'Updated description', + name: sameNameValue, }); const expected = await client.retrieve(repositoryIdentifier); + assert.strictEqual(res.status, 200); + assert.strictEqual(expected.data.name, sameNameValue); + }); + + it('update() with a single changed field returns 202 and updates only that field once the task completes', async () => { + const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; + assert.strictEqual(typeof repositoryIdentifier, 'string'); + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.update(repositoryIdentifier, { + description: 'Updated description', + }); + + let actual: RPMRepositoryType; + assert.strictEqual(res.status, 202); + if (res.status === 202) { + // TODO: Fix broken return type on Update + await waitForTaskCompletion(res.data.task); + actual = (await client.retrieve(repositoryIdentifier)).data; + } + assert.strictEqual(res.status, 202); - assert.strictEqual(expected.data.description, 'Updated description'); + assert.strictEqual(actual.description, 'Updated description'); }); - it('update() with multiple changed fields updates all associated', async () => { + it('update() when changing multiple fields returns 202, and all changes are reflected after the task completes', async () => { const repositoryIdentifier = repositoryPrn.split(':', 3)[2]; assert.strictEqual(typeof repositoryPrn, 'string'); const client = createRepositoryAPI(testPulpAPI()); @@ -280,13 +303,19 @@ describe('Integration: RPM Repository API Client', () => { } satisfies Partial; const res = await client.update(repositoryIdentifier, payload); - const actual = await client.retrieve(repositoryIdentifier); + let actual: RPMRepositoryType; assert.strictEqual(res.status, 202); - assert.strictEqual(actual.data.autopublish, true); - assert.strictEqual(actual.data.retain_package_versions, 5); - assert.strictEqual(actual.data.checksum_type, 'sha256'); - assert.strictEqual(actual.data.compression_type, 'zstd'); + if (res.status === 202) { + // TODO: Fix broken return type on Update + await waitForTaskCompletion(res.data.task); + actual = (await client.retrieve(repositoryIdentifier)).data; + } + + assert.strictEqual(actual.autopublish, true); + assert.strictEqual(actual.retain_package_versions, 5); + assert.strictEqual(actual.checksum_type, 'sha256'); + assert.strictEqual(actual.compression_type, 'zstd'); }); it('update() when a not allowed checksum_type is passed returns 400', async () => { @@ -324,4 +353,57 @@ describe('Integration: RPM Repository API Client', () => { ); }); }); + + describe('RPM Repository - delete()', () => { + it('delete() returns 202 and dispatches a task', async () => { + const created = await testAxiosClient('repositories/rpm/rpm/', { + method: 'POST', + data: JSON.stringify({ name: 'test-rpm-repo-delete-valid' }), + }); + const repositoryIdentifier = created.data.prn.split(':', 3)[2]; + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.delete(repositoryIdentifier); + + assert.strictEqual(res.status, 202); + // TODO: Fix broken return type on Delete + assert.notStrictEqual(res.data.task, undefined); + }); + + it('delete() when passed a non-existent identifier returns 404', async () => { + const nonExistentRepositoryIdentifier = '1234567890'; + const client = createRepositoryAPI(testPulpAPI()); + + await assert.rejects( + () => client.delete(nonExistentRepositoryIdentifier), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 404); + return true; + }, + ); + }); + + it('delete() removes the repository once the dispatched task completes', async () => { + const created = await testAxiosClient('repositories/rpm/rpm/', { + method: 'POST', + data: JSON.stringify({ name: 'test-rpm-repo-delete-completion' }), + }); + const repositoryIdentifier = created.data.prn.split(':', 3)[2]; + const client = createRepositoryAPI(testPulpAPI()); + + const res = await client.delete(repositoryIdentifier); + // TODO: Fix broken return type on Delete + await waitForTaskCompletion(res.data.task); + + await assert.rejects( + () => client.retrieve(repositoryIdentifier), + (err: unknown) => { + assert.ok(isAxiosError(err)); + assert.strictEqual(err.response?.status, 404); + return true; + }, + ); + }); + }); }); diff --git a/src/api/plugins/rpm/repository.ts b/src/api/plugins/rpm/repository.ts index 0512f485..853198af 100644 --- a/src/api/plugins/rpm/repository.ts +++ b/src/api/plugins/rpm/repository.ts @@ -39,7 +39,7 @@ interface RPMRepositoryType extends GenericRepository { /** * RPM Create / Update Type. - * + * * @see https://github.com/pulp/pulp_rpm/blob/dc333a99db6c44d70d6103540cb592f6e55a8682/pulp_rpm/app/serializers/repository.py#L326 */ interface RPMRepositoryUpsertType extends Omit< @@ -49,7 +49,8 @@ interface RPMRepositoryUpsertType extends Omit< checksum_type?: RPMAllowedUpsertChecksumsType | null; } -// FIXME: Move AxiosResponse type to PulpAPI Base Class. +// FIXME: Move AxiosResponse type to PulpAPI Base Class. +// NOTE: The FIXME implementation is not easy as to avoid major type issues with legacy API calls. interface RPMRepositoryClient { list: ( params?: GenericRepositoryFilterParams, @@ -61,10 +62,10 @@ interface RPMRepositoryClient { update: ( id: string, data: Partial, - // TODO: Update Reponse Type + // TODO: Update Response Type to union of Upsert Type & Task Type ) => Promise>; - // delete: () => Promise; - // sync: () => Promise; + // TOOO: Update Response Type to Task Type Response + delete: (id: string) => Promise>; } /** @@ -81,8 +82,7 @@ function createRepositoryAPI(base: PulpAPI): RPMRepositoryClient { create: (data) => base.http.post(`repositories/rpm/rpm/`, data), update: (id, data) => base.http.patch(`repositories/rpm/rpm/${id}`, { data }), - // delete: () => {}, - // sync: () => {}, + delete: (id) => base.http.delete(`repositories/rpm/rpm/${id}`), }; } diff --git a/src/api/test-utils/integration-client.ts b/src/api/test-utils/integration-client.ts index 07f77a81..2a841afb 100644 --- a/src/api/test-utils/integration-client.ts +++ b/src/api/test-utils/integration-client.ts @@ -48,6 +48,10 @@ function testPulpAPI(): PulpAPI { method: 'PATCH', data: config.data, }), + delete: (url: string, config: { params?: Record }) => + testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { + method: 'DELETE', + }), }, } as unknown as PulpAPI; } @@ -65,4 +69,35 @@ function buildQueryParams(params?: Record): string { return queryString ? `?${queryString}` : ''; } -export { testPulpAPI, testAxiosClient }; +async function waitForTaskCompletion( + taskHref: string, + { + waitMs = 500, + attemptsLeft = 10, + }: { waitMs?: number; attemptsLeft?: number } = {}, +): Promise { + const res = await testAxiosClient(taskHref, { method: 'GET' }); + const state: string = res.data.state; + + // TODO: Update Error Message + if (['skipped', 'failed', 'canceled'].includes(state)) { + throw new Error('Another Error'); + } + + if (state === 'completed') { + return; + } + + // TODO: Update Error Message + if (attemptsLeft <= 0) { + throw new Error(`Task did not complete`); + } + + await new Promise((r) => setTimeout(r, waitMs)); + return waitForTaskCompletion(taskHref, { + waitMs: Math.round(waitMs * 1.5), + attemptsLeft: attemptsLeft - 1, + }); +} + +export { testPulpAPI, testAxiosClient, waitForTaskCompletion }; From e6524b10710489fd1eccf06ab4bf360eecb726f6 Mon Sep 17 00:00:00 2001 From: Wes <76014409+Redtigercod4@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:16:49 +0000 Subject: [PATCH 16/16] Updated types for task dispatch response, updated error messaging on test setup --- package.json | 8 +++--- src/api/common.ts | 20 ++++++++++---- src/api/plugins/rpm/client.ts | 6 ++--- src/api/plugins/rpm/repository.test.ts | 17 ++++-------- src/api/plugins/rpm/repository.ts | 20 +++++++------- src/api/rpm-repository.ts | 34 ++---------------------- src/api/test-utils/integration-client.ts | 13 ++++----- 7 files changed, 46 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 429b78c1..ed8eba92 100644 --- a/package.json +++ b/package.json @@ -94,9 +94,11 @@ "start": "NODE_ENV=development webpack serve --config config/start.config.js", "test": "true", "test:check-ts": "node -e \"process.versions.node.localeCompare('22.10.0',undefined,{numeric:true})>=0||(console.error('Testing uses feature flags requiring Node >= 22.10.0, but is using '+process.versions.node),process.exit(1))\"", - "test:unit": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Unit: '", - "test:integration": "npm run test:check-ts && node --test --experimental-strip-types --test-name-pattern '^Integration: '", - "test:coverage": "npm run test:check-ts && node --test --experimental-strip-types --experimental-test-coverage --test-coverage-exclude '**/*.test.ts' --test-coverage-exclude 'src/api/test-utils/**'", + "test:check-api-status": "curl -sf \"${PULP_BASE_URL:-http://localhost:8080/pulp/api/v3/}status/\" -o /dev/null || (echo 'Integration tests require the Pulp API, is it running?' >&2; exit 1)", + "test:check": "npm run test:check-ts && npm run test:check-api-status", + "test:unit": "npm run test:check && node --test --experimental-strip-types --test-name-pattern '^Unit: '", + "test:integration": "npm run test:check && node --test --experimental-strip-types --test-name-pattern '^Integration: '", + "test:coverage": "npm run test:check && node --test --experimental-strip-types --experimental-test-coverage --test-coverage-exclude '**/*.test.ts' --test-coverage-exclude 'src/api/test-utils/**'", "upgrade": "npx npm-check-updates -u -t minor" }, "engines": { diff --git a/src/api/common.ts b/src/api/common.ts index c6a2a5ef..8c4064ab 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -154,7 +154,7 @@ type RetainCheckpointsFilterParams = LookupFilterParams< /** * Generic Repository Filters shared across PulpCore & Plugin Endpoints. - * + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/viewsets/repository.py#L88 */ interface GenericRepositoryFilterParams @@ -170,18 +170,27 @@ interface GenericRepositoryFilterParams } /** - * Generic Paginated Response shared across PulpCore & Plugin Endpoints. - * + * Paginated Response shared across PulpCore & Plugin Endpoints. + * * @see https://github.com/pulp/pulpcore/blob/934c752dae916857b2005e1fe0ef75496accc082/pulpcore/app/settings.py#L186 * @see https://github.com/encode/django-rest-framework/blob/6f0b74def3fcc81e126b87b08e59abdb6c2ad056/rest_framework/pagination.py#L364 */ -interface GenericPaginatedResponse { +interface PaginatedResponse { count: number; next: string | null; previous: string | null; results: TResult[]; } +/** + * Async Task Dispatch Response shared across PulpCore & Plugin Endpoints. + * + * @see https://github.com/pulp/pulpcore/blob/dea04fa79a6ca590f2943a0a7754c219061be10c/pulpcore/app/response.py#L6 + */ +interface DispatchedTaskResponse { + task: string; +} + /** * -------------------- * These are shared Plugin Types outside the PulpCore Generics. @@ -208,6 +217,7 @@ export type { GenericPublication, GenericRemote, GenericRepositoryFilterParams, - GenericPaginatedResponse, + PaginatedResponse, + DispatchedTaskResponse, AnsibleLastSyncType, }; diff --git a/src/api/plugins/rpm/client.ts b/src/api/plugins/rpm/client.ts index ca9694bf..71184973 100644 --- a/src/api/plugins/rpm/client.ts +++ b/src/api/plugins/rpm/client.ts @@ -1,8 +1,8 @@ -import { PulpAPI } from "src/api/pulp"; -import { createRepositoryAPI } from "./repository"; +import { PulpAPI } from 'src/api/pulp'; +import { createRepositoryAPI } from './repository'; class RpmClient extends PulpAPI { - repository = createRepositoryAPI(this); + repository = createRepositoryAPI(this); } export { RpmClient }; diff --git a/src/api/plugins/rpm/repository.test.ts b/src/api/plugins/rpm/repository.test.ts index 527ead0c..0cc131ff 100644 --- a/src/api/plugins/rpm/repository.test.ts +++ b/src/api/plugins/rpm/repository.test.ts @@ -23,9 +23,8 @@ describe('Integration: RPM Repository API Client', () => { data: JSON.stringify({ name: testRpmRepoName }), }); - // TODO: Update Error Message and Error Check if (!res.data.pulp_href) { - throw new Error(); + throw new Error('Failed to create test repository'); } repositoryHref = res.data.pulp_href; @@ -71,9 +70,8 @@ describe('Integration: RPM Repository API Client', () => { data: JSON.stringify({ name: testRpmRepoName }), }); - // TODO: Update Error Message and Error Check if (!res.data.prn || !res.data.pulp_href) { - throw new Error(); + throw new Error('Failed to create test repository'); } repositoryPrn = res.data.prn; @@ -240,9 +238,8 @@ describe('Integration: RPM Repository API Client', () => { data: JSON.stringify({ name: 'test-rpm-repo-update' }), }); - // TODO: Update Error Message and Error Check if (!res.data.prn || !res.data.pulp_href) { - throw new Error(); + throw new Error('Failed to create test repository'); } repositoryHref = res.data.pulp_href; @@ -281,8 +278,7 @@ describe('Integration: RPM Repository API Client', () => { let actual: RPMRepositoryType; assert.strictEqual(res.status, 202); - if (res.status === 202) { - // TODO: Fix broken return type on Update + if (res.status === 202 && 'task' in res.data) { await waitForTaskCompletion(res.data.task); actual = (await client.retrieve(repositoryIdentifier)).data; } @@ -306,8 +302,7 @@ describe('Integration: RPM Repository API Client', () => { let actual: RPMRepositoryType; assert.strictEqual(res.status, 202); - if (res.status === 202) { - // TODO: Fix broken return type on Update + if (res.status === 202 && 'task' in res.data) { await waitForTaskCompletion(res.data.task); actual = (await client.retrieve(repositoryIdentifier)).data; } @@ -366,7 +361,6 @@ describe('Integration: RPM Repository API Client', () => { const res = await client.delete(repositoryIdentifier); assert.strictEqual(res.status, 202); - // TODO: Fix broken return type on Delete assert.notStrictEqual(res.data.task, undefined); }); @@ -393,7 +387,6 @@ describe('Integration: RPM Repository API Client', () => { const client = createRepositoryAPI(testPulpAPI()); const res = await client.delete(repositoryIdentifier); - // TODO: Fix broken return type on Delete await waitForTaskCompletion(res.data.task); await assert.rejects( diff --git a/src/api/plugins/rpm/repository.ts b/src/api/plugins/rpm/repository.ts index 853198af..d8b047ee 100644 --- a/src/api/plugins/rpm/repository.ts +++ b/src/api/plugins/rpm/repository.ts @@ -1,8 +1,9 @@ import type { AxiosResponse } from 'axios'; import type { - GenericPaginatedResponse, + DispatchedTaskResponse, GenericRepository, GenericRepositoryFilterParams, + PaginatedResponse, } from '../../common'; import type { PulpAPI } from '../../pulp'; @@ -49,12 +50,12 @@ interface RPMRepositoryUpsertType extends Omit< checksum_type?: RPMAllowedUpsertChecksumsType | null; } -// FIXME: Move AxiosResponse type to PulpAPI Base Class. +// FIXME: Move AxiosResponse type to PulpAPI Base Class. // NOTE: The FIXME implementation is not easy as to avoid major type issues with legacy API calls. interface RPMRepositoryClient { list: ( params?: GenericRepositoryFilterParams, - ) => Promise>>; + ) => Promise>>; retrieve: (id: string) => Promise>; create: ( data: RPMRepositoryUpsertType, @@ -62,10 +63,8 @@ interface RPMRepositoryClient { update: ( id: string, data: Partial, - // TODO: Update Response Type to union of Upsert Type & Task Type - ) => Promise>; - // TOOO: Update Response Type to Task Type Response - delete: (id: string) => Promise>; + ) => Promise>; + delete: (id: string) => Promise>; } /** @@ -78,11 +77,10 @@ interface RPMRepositoryClient { function createRepositoryAPI(base: PulpAPI): RPMRepositoryClient { return { list: (params?) => base.list(`repositories/rpm/rpm/`, params), - retrieve: (id) => base.http.get(`repositories/rpm/rpm/${id}`), + retrieve: (id) => base.http.get(`repositories/rpm/rpm/${id}/`), create: (data) => base.http.post(`repositories/rpm/rpm/`, data), - update: (id, data) => - base.http.patch(`repositories/rpm/rpm/${id}`, { data }), - delete: (id) => base.http.delete(`repositories/rpm/rpm/${id}`), + update: (id, data) => base.http.patch(`repositories/rpm/rpm/${id}/`, data), + delete: (id) => base.http.delete(`repositories/rpm/rpm/${id}/`), }; } diff --git a/src/api/rpm-repository.ts b/src/api/rpm-repository.ts index 2dd8f24d..c553f25d 100644 --- a/src/api/rpm-repository.ts +++ b/src/api/rpm-repository.ts @@ -1,40 +1,10 @@ -import type { GenericRepository } from './common'; import { PulpAPI } from './pulp'; -type RPMChecksumType = - | 'unknown' - | 'md5' - | 'sha' - | 'sha1' - | 'sha224' - | 'sha256' - | 'sha384' - | 'sha512'; -type RPMCompressionType = 'zstd' | 'gz' | 'none'; -type RPMLayoutType = 'nested_alphabetically' | 'flat' | 'nested_by_digest'; +const base = new PulpAPI(); /** - * RPM Repository Type. - * - * @see https://github.com/pulp/pulp_rpm/blob/main/pulp_rpm/app/serializers/repository.py + * @deprecated Use `RpmClient` from `src/api/plugins/rpm/client.ts` instead. */ -interface RPMRepositoryType extends GenericRepository { - autopublish?: boolean; - metadata_signing_service?: string | null; - package_signing_service?: string | null; - package_signing_fingerprint?: string | null; - retain_package_versions?: number; - checksum_type?: RPMChecksumType | null; - compression_type?: RPMCompressionType | null; - layout?: RPMLayoutType | null; - repo_config?: Record; - osv_config?: { name: string; releases: unknown }[] | null; -} - -const base = new PulpAPI(); - export const RPMRepositoryAPI = { list: (params?) => base.list(`repositories/rpm/rpm/`, params), }; - -export type { RPMRepositoryType }; diff --git a/src/api/test-utils/integration-client.ts b/src/api/test-utils/integration-client.ts index 2a841afb..a52311e7 100644 --- a/src/api/test-utils/integration-client.ts +++ b/src/api/test-utils/integration-client.ts @@ -42,11 +42,12 @@ function testPulpAPI(): PulpAPI { testAxiosClient(url, { method: 'POST', data }), patch: ( url: string, - config: { params?: Record; data: unknown }, + data: unknown, + config: { params?: Record }, ) => testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { method: 'PATCH', - data: config.data, + data, }), delete: (url: string, config: { params?: Record }) => testAxiosClient(`${url}${buildQueryParams(config?.params)}`, { @@ -79,18 +80,18 @@ async function waitForTaskCompletion( const res = await testAxiosClient(taskHref, { method: 'GET' }); const state: string = res.data.state; - // TODO: Update Error Message if (['skipped', 'failed', 'canceled'].includes(state)) { - throw new Error('Another Error'); + throw new Error(`Task ${taskHref} ended with state "${state}"`); } if (state === 'completed') { return; } - // TODO: Update Error Message if (attemptsLeft <= 0) { - throw new Error(`Task did not complete`); + throw new Error( + `Task ${taskHref} did not complete within the allowed attempts`, + ); } await new Promise((r) => setTimeout(r, waitMs));