Add RawPathSegment class to preserve slashes in API paths

This commit is contained in:
Bryan Lee 2025-04-15 20:44:33 +08:00 committed by Justin Dalrymple
parent 96753b28be
commit 751fca68cf
3 changed files with 47 additions and 17 deletions

View File

@ -41,15 +41,39 @@ export function appendFormFromObject(object: Record<string, OptionValueType>): F
}
/**
* Normalize GitLab API endpoint by encoding route parameters.
* @param strings
* @param values
* A special class to mark path segments that should not be URL-encoded.
* Used for paths that already contain special characters like '/' that should be preserved.
* For example, in API paths like 'repository/commits', the slash should not be encoded to '%2F'.
*/
export function endpoint(strings: TemplateStringsArray, ...values: (string | number)[]): string {
return values.reduce<string>(
(string, value, index) => string + encodeURIComponent(value) + strings[index + 1],
strings[0],
);
export class RawPathSegment {
public readonly value: string;
constructor(value: string) {
this.value = value;
}
toString(): string {
return this.value;
}
}
/**
* Normalize GitLab API endpoint by encoding route parameters.
* Handles special cases where certain path segments (wrapped in RawPathSegment)
* should not be URL-encoded.
* @param strings Template string parts
* @param values Values to be encoded and inserted between string parts
*/
export function endpoint(
strings: TemplateStringsArray,
...values: (string | number | RawPathSegment)[]
): string {
return values.reduce<string>((result, value, index) => {
const encodedValue =
value instanceof RawPathSegment ? value.value : encodeURIComponent(String(value));
return result + encodedValue + strings[index + 1];
}, strings[0]);
}
/**

View File

@ -1,11 +1,12 @@
import type { BaseResourceOptions } from '@gitbeaker/requester-utils';
import { ResourceDiscussions } from '../templates';
import type {
GitlabAPIResponse,
PaginationRequestOptions,
PaginationTypes,
ShowExpanded,
Sudo,
import {
type GitlabAPIResponse,
type PaginationRequestOptions,
type PaginationTypes,
RawPathSegment,
type ShowExpanded,
type Sudo,
} from '../infrastructure';
import type {
DiscussionNotePositionOptions,
@ -68,6 +69,6 @@ export interface CommitDiscussions<C extends boolean = false> extends ResourceDi
export class CommitDiscussions<C extends boolean = false> extends ResourceDiscussions<C> {
constructor(options: BaseResourceOptions<C>) {
/* istanbul ignore next */
super('projects', 'repository/commits', options);
super('projects', new RawPathSegment('repository/commits'), options);
}
}

View File

@ -7,6 +7,7 @@ import type {
MappedOmit,
PaginationRequestOptions,
PaginationTypes,
RawPathSegment,
ShowExpanded,
Sudo,
} from '../infrastructure';
@ -76,9 +77,13 @@ export interface DiscussionSchema extends Record<string, unknown> {
export type DiscussionNotePositionOptions = Camelize<DiscussionNotePositionSchema>;
export class ResourceDiscussions<C extends boolean = false> extends BaseResource<C> {
protected resource2Type: string;
protected resource2Type: string | RawPathSegment;
constructor(resourceType: string, resource2Type: string, options: BaseResourceOptions<C>) {
constructor(
resourceType: string,
resource2Type: string | RawPathSegment,
options: BaseResourceOptions<C>,
) {
super({ prefixUrl: resourceType, ...options });
this.resource2Type = resource2Type;