Skip to content
This repository has been archived by the owner on Dec 30, 2021. It is now read-only.

Commit

Permalink
feat: initial reimplementation of the upload method
Browse files Browse the repository at this point in the history
  • Loading branch information
kaimallea committed Mar 22, 2021
1 parent 4a34515 commit eeba909
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 30 deletions.
67 changes: 64 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
### Installation
## Installation

```shell
npm install imgur
```

### Usage
## Usage

Require and instantiate with credentials:
### Import and instantiate with credentials:

```ts
// ESModule
import { ImgurClient } from 'imgur';

// CommonJS
const { ImgurClient } = require('imgur');

let client;

// if you already have an access token acquired
Expand All @@ -31,3 +35,60 @@ If you don't have any credentials, you'll need to:

1. [Create an Imgur account](https://imgur.com/register)
1. [Register an application](https://api.imgur.com/#registerapp)

### Upload one or more images and videos

You can upload one or more files by simply passing a path to a file or array of paths to multiple files.

```ts
// a single image via an absolute path
const response = await client.upload('/home/kai/dank-meme.jpg');
console.log(response.link);

// multiple images via an array of absolute paths
const responses = await client.upload([
'/home/kai/dank-meme.jpg',
'/home/kai/another-dank-meme',
]);
responses.forEach((r) => console.log(r.link));
```

If you want to provide metadata, such as a title, description, etc., then pass an object instead of a string:

```ts
// a single image via an absolute path
const response = await client.upload({
image: '/home/kai/dank-meme.jpg',
title: 'Meme',
description: 'Dank Meme',
});
console.log(response.link);

// multiple images via an array of absolute paths
const responses = await client.upload([
{
image: '/home/kai/dank-meme.jpg',
title: 'Meme',
description: 'Dank Meme',
},
{
image: '/home/kai/cat.mp4',
title: 'A Cat Movie',
description: 'Caturday',
},
]);
responses.forEach((r) => console.log(r.link));
```

Acceptable key/values match what [the Imgur API expects](https://apidocs.imgur.com/#c85c9dfc-7487-4de2-9ecd-66f727cf3139):

| Key | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `image` | A binary file, base64 data, or a URL for an image. (up to 10MB) |
| `video` | A binary file (up to 200MB) |
| `album` | The id of the album you want to add the image to. For anonymous albums, album should be the deletehash that is returned at creation |
| `type` | The type of the file that's being sent; `file`, `base64` or `url` |
| `name` | The name of the file. This is automatically detected, but you can override |
| `title` | The title of the image |
| `description` | The description of the image |
| `disable_audio` | `1` will remove the audio track from a video file |
1 change: 1 addition & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ beforeEach(() => {
mockfs({
'/home/user/meme.jpg': Buffer.from([8, 6, 7, 5, 3, 0, 9]),
'/home/user/lol.jpg': Buffer.from([9, 0, 3, 5, 7, 6, 8]),
'/home/user/trailer.mp4': Buffer.from([9, 0, 3, 5, 7, 6, 8, 1, 2, 3, 4, 5]),
});
});

Expand Down
47 changes: 32 additions & 15 deletions src/__tests__/mocks/handlers/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,51 @@ const BadRequestErrorResponse = {
},
};

const SuccessfulUploadResponse = {
data: {
id: 'JK9ybyj',
deletehash: 'j83zimv4VtDA0Xp',
link: 'https://i.imgur.com/JK9ybyj.jpg',
},
success: true,
status: 200,
};
function createResponse({
id = 'JK9ybyj',
type = null,
title = null,
description = null,
}) {
return {
data: {
id,
deletehash: Array.from(id).reverse().join(''),
title,
description,
link: `https://i.imgur.com/${id}.${type === 'video' ? 'mp4' : 'jpg'}`,
},
success: true,
status: 200,
};
}

export function postHandler(req, res, ctx) {
// image field is always required
if (!('image' in req.body)) {
const {
image = null,
video = null,
type = null,
title = null,
description = null,
} = req.body;

// image or video field is always required
if (image !== null && video !== null) {
return res(ctx.status(400), ctx.json(BadRequestErrorResponse));
}

// type is optional when uploading a file, but required
// for any other type
if ('type' in req.body) {
if (type !== null) {
// only these types are allowed
if (!['file', 'url', 'base64'].includes(req.body.type)) {
if (!['file', 'url', 'base64'].includes(type)) {
return res(ctx.status(400), ctx.json(BadRequestErrorResponse));
}
// if type is not specified we assume we're uploading a file.
// but we need to make sure a file was sent in the image field
} else if (typeof req.body.image !== 'object') {
} else if (typeof image !== 'object') {
return res(ctx.status(400), ctx.json(BadRequestErrorResponse));
}

return res(ctx.json(SuccessfulUploadResponse));
return res(ctx.json(createResponse({ image, video, title, description })));
}
12 changes: 9 additions & 3 deletions src/__tests__/uploadFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ test('upload one image image and receive response', async () => {
const resp = await imgur.uploadFile('/home/user/meme.jpg');
expect(resp).toMatchInlineSnapshot(`
Object {
"deletehash": "j83zimv4VtDA0Xp",
"deletehash": "jyby9KJ",
"description": null,
"id": "JK9ybyj",
"link": "https://i.imgur.com/JK9ybyj.jpg",
"title": null,
}
`);
});
Expand All @@ -21,14 +23,18 @@ test('upload multiple images and receive response', async () => {
expect(resp).toMatchInlineSnapshot(`
Array [
Object {
"deletehash": "j83zimv4VtDA0Xp",
"deletehash": "jyby9KJ",
"description": null,
"id": "JK9ybyj",
"link": "https://i.imgur.com/JK9ybyj.jpg",
"title": null,
},
Object {
"deletehash": "j83zimv4VtDA0Xp",
"deletehash": "jyby9KJ",
"description": null,
"id": "JK9ybyj",
"link": "https://i.imgur.com/JK9ybyj.jpg",
"title": null,
},
]
`);
Expand Down
28 changes: 22 additions & 6 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
import { EventEmitter } from 'events';
import got, { Options } from 'got';
import { IncomingMessage } from 'http';
import got, { ExtendOptions } from 'got';
import { getAuthorizationHeader, Credentials } from './helpers';
import { getImage, upload, Payload } from './image';

type ImgurResponse = {
data?: any;
success?: boolean;
status?: number;
};

export class ImgurClient extends EventEmitter {
constructor(readonly credentials: Credentials) {
super();
}

async request(options: Options): Promise<any> {
async request(options: ExtendOptions): Promise<IncomingMessage> {
try {
return await got(options);
return (await got(options)) as IncomingMessage;
} catch (err) {
throw new Error(err);
}
}

async authorizedRequest(options: Options): Promise<any> {
async authorizedRequest(options: ExtendOptions): Promise<ImgurResponse> {
try {
const authorization = await getAuthorizationHeader(this);
const mergedOptions = got.mergeOptions(options, {
headers: { authorization },
responseType: 'json',
resolveBodyOnly: true,
});
return await this.request(mergedOptions);
return (await this.request(mergedOptions)) as ImgurResponse;
} catch (err) {
throw new Error(err.message);
throw new Error(err);
}
}

async getImage(imageHash: string) {
return getImage(this, imageHash);
}

async upload(payload: string | string[] | Payload | Payload[]) {
return upload(this, payload);
}
}
1 change: 1 addition & 0 deletions src/helpers/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ const API_BASE = `${HOST}/${API_VERSION}`;
export const AUTHORIZE_ENDPOINT = `${HOST}/oauth2/authorize`;

export const IMAGE_ENDPOINT = `${API_BASE}/image`;
export const UPLOAD_ENDPOINT = `${API_BASE}/upload`;
6 changes: 3 additions & 3 deletions src/image/getImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ImgurClient } from '../client';
import { IMAGE_ENDPOINT } from '../helpers';

type ImageResponse = {
data: {
data?: {
id?: string;
title?: string | null;
description?: string | null;
Expand Down Expand Up @@ -37,8 +37,8 @@ type ImageResponse = {
showsAds?: boolean;
};
};
success: boolean;
status: number;
success?: boolean;
status?: number;
};

export async function getImage(
Expand Down
2 changes: 2 additions & 0 deletions src/image/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './getImage';
export * from './upload';
Loading

0 comments on commit eeba909

Please sign in to comment.