Skip to content

Commit 72d389c

Browse files
feat: paginated results
1 parent e834148 commit 72d389c

5 files changed

Lines changed: 164 additions & 10 deletions

File tree

src/api-client.ts

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fileTypeFromBuffer } from "file-type";
44
import ky, { HTTPError, type KyInstance, type ResponsePromise } from "ky";
55
import { IMGProcessingAPIError } from "./api-error.js";
66
import { ImageObject } from "./image-object.js";
7+
import { PaginatedImages } from "./paginated-images.js";
78

89
export class IMGProcessingClient {
910
protected readonly client: KyInstance;
@@ -61,7 +62,7 @@ export class IMGProcessingClient {
6162
imageId,
6263
}: IMGProcessingClient.download.Params): Promise<Blob> {
6364
try {
64-
const response = await this.client.get(`v1/images/${imageId}/download`)
65+
const response = await this.client.get(`v1/images/${imageId}/download`);
6566
return await response.blob();
6667
} catch (error) {
6768
if (error instanceof HTTPError) {
@@ -74,6 +75,63 @@ export class IMGProcessingClient {
7475
}
7576
}
7677

78+
/**
79+
* Got to the page in the paginated list of images.
80+
* @internal
81+
*/
82+
async goToPage<
83+
Format extends ImageObject.SupportedFormat = ImageObject.SupportedFormat,
84+
>({
85+
page,
86+
}: IMGProcessingClient.goToPage.Params): Promise<PaginatedImages<Format>> {
87+
const urlWithoutDomain = page.replace(
88+
"https://api.img-processing.com/",
89+
"",
90+
);
91+
const response = await this.request<PaginatedImages<Format>>(() =>
92+
this.client.get(urlWithoutDomain),
93+
);
94+
return new PaginatedImages({
95+
...response,
96+
apiClient: this,
97+
});
98+
}
99+
100+
/**
101+
* Retrieves an image object by its unique identifier.
102+
*/
103+
async getImage({
104+
imageId,
105+
}: IMGProcessingClient.getImage.Params): Promise<ImageObject> {
106+
return this.imageRequest(() => this.client.get(`v1/images/${imageId}`));
107+
}
108+
109+
/**
110+
* Retrieves a list of all the images created by the user.
111+
* The images are returned in descending order of creation date, with the most recent images first
112+
* in the list.
113+
*
114+
* Images are paginated, following the [pagination](https://docs.img-processing/api-reference/pagination) rules.
115+
*/
116+
async listImages<
117+
Format extends ImageObject.SupportedFormat = ImageObject.SupportedFormat,
118+
>({
119+
take,
120+
from,
121+
}: IMGProcessingClient.listImages.Params): Promise<PaginatedImages<Format>> {
122+
const response = await this.request<PaginatedImages<Format>>(() =>
123+
this.client.get("v1/images", {
124+
searchParams: {
125+
take: take?.toString() as never,
126+
from: from as never,
127+
},
128+
}),
129+
);
130+
return new PaginatedImages({
131+
...response,
132+
apiClient: this,
133+
});
134+
}
77135

78136
/**
79137
* -----------------------------------------
@@ -96,7 +154,6 @@ export class IMGProcessingClient {
96154
);
97155
}
98156

99-
100157
/**
101158
* -----------------------------------------
102159
* Creation
@@ -629,4 +686,29 @@ export declare namespace IMGProcessingClient {
629686
imageId: ImageId;
630687
};
631688
}
689+
690+
export namespace getImage {
691+
export type Params = {
692+
/** The unique identifier of the image to retrieve. */
693+
imageId: ImageId;
694+
};
695+
}
696+
697+
export namespace goToPage {
698+
export type Params = {
699+
/** The url of the page to go to. */
700+
page: string;
701+
};
702+
}
703+
704+
export namespace listImages {
705+
export type Params = {
706+
/** The number of items to return per page. */
707+
take?: number;
708+
/** The cursor indicating where to start fetching the next set of results. It corresponds to the ID of the first item on the current page.
709+
* If not provided, the first page of results will be returned.
710+
* */
711+
from?: string;
712+
};
713+
}
632714
}

src/image-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import type { Blob } from "node:buffer";
12
import type { IMGProcessingClient } from "./api-client.js";
23
import type { WithoutImageId } from "./types.js";
3-
import { type Blob } from "node:buffer";
44

55
/**
66
* The Image object represents an image processed using the IMG Processing API. The object contains

src/paginated-images.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type { IMGProcessingClient } from "./api-client.js";
2+
import type { ImageObject } from "./image-object.js";
3+
import type { Prettify } from "./types.js";
4+
5+
export class PaginatedImages<
6+
Format extends ImageObject.SupportedFormat = ImageObject.SupportedFormat,
7+
> implements PaginatedImages.PaginatedImage<Format>
8+
{
9+
data: ImageObject.Image<Format>[];
10+
links: {
11+
previous: string | null;
12+
next: string | null;
13+
};
14+
protected client: IMGProcessingClient;
15+
16+
constructor({
17+
data,
18+
links,
19+
apiClient,
20+
}: PaginatedImages.constructor.Params<Format>) {
21+
this.data = data;
22+
this.links = links;
23+
this.client = apiClient;
24+
}
25+
26+
/**
27+
* Returns the next page of results, if available. Otherwise, returns `undefined`.
28+
*/
29+
public async next(): Promise<PaginatedImages<Format> | undefined> {
30+
if (!this.links.next) {
31+
return;
32+
}
33+
return this.client.goToPage({
34+
page: this.links.next,
35+
});
36+
}
37+
38+
/**
39+
* Returns the previous page of results, if available. Otherwise, returns `undefined`.
40+
*/
41+
public async previous(): Promise<PaginatedImages<Format> | undefined> {
42+
if (!this.links.previous) {
43+
return;
44+
}
45+
return this.client.goToPage({
46+
page: this.links.previous,
47+
});
48+
}
49+
}
50+
51+
/**
52+
* All endpoints that list objects provide support for pagination. This allows you to retrieve a subset of the results at a time,
53+
* making it easier to manage large datasets.
54+
*/
55+
export namespace PaginatedImages {
56+
export type PaginatedImage<Format extends ImageObject.SupportedFormat> = {
57+
/** The list of items for the current page. */
58+
data: ImageObject.Image<Format>[];
59+
/** Links to the previous and next pages. */
60+
links: {
61+
/** The URL to fetch the previous page of results. */
62+
previous: string | null;
63+
/** The URL to fetch the next page of results. */
64+
next: string | null;
65+
};
66+
};
67+
export namespace constructor {
68+
export type Params<Format extends ImageObject.SupportedFormat> = Prettify<
69+
PaginatedImage<Format> & {
70+
apiClient: IMGProcessingClient;
71+
}
72+
>;
73+
}
74+
}

test/create-image-from-url.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { describe, expect, test } from "vitest";
22
import { IMGProcessingClient } from "../src/api-client.js";
33

44
describe("createImageFromUrl", () => {
5-
const apiKey = process.env
6-
.IMG_PROCESSING_API_KEY;
5+
const apiKey = process.env.IMG_PROCESSING_API_KEY;
76
if (!apiKey) {
87
throw new Error(
98
"IMG_PROCESSING_API_KEY environment variable is required to run the tests",
@@ -17,10 +16,10 @@ describe("createImageFromUrl", () => {
1716
expect(image.id).toMatch(/^image_[a-zA-Z0-9]{24}$/);
1817
expect(image.name).toBe("test_image");
1918
client.resize({
20-
imageId: 'image_id',
19+
imageId: "image_id",
2120
width: 100,
22-
height: 100
23-
})
21+
height: 100,
22+
});
2423
});
2524

2625
test("should throw error if no name is provided", async () => {

test/upload-image.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { IMGProcessingClient } from "../src/api-client.js";
66
import { IMGProcessingAPIError } from "../src/api-error.js";
77

88
describe("uploadImage", () => {
9-
const apiKey = process.env
10-
.IMG_PROCESSING_API_KEY;
9+
const apiKey = process.env.IMG_PROCESSING_API_KEY;
1110
if (!apiKey) {
1211
throw new Error(
1312
"IMG_PROCESSING_API_KEY environment variable is required to run the tests",

0 commit comments

Comments
 (0)