This repository has been archived by the owner on Dec 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: initial impl of ImgurClient class
begin rewrite of client as an event emitter class BREAKING CHANGE: ImgurClient class replaces imgur object
- Loading branch information
Showing
11 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { EventEmitter } from 'events'; | ||
import got, { Options } from 'got'; | ||
import { getAuthorizationHeader, Credentials } from './helpers'; | ||
|
||
export class ImgurClient extends EventEmitter { | ||
constructor(readonly credentials: Credentials) { | ||
super(); | ||
} | ||
|
||
async request(options: Options): Promise<any> { | ||
try { | ||
return await got(options); | ||
} catch (err) { | ||
throw new Error(err); | ||
} | ||
} | ||
|
||
async authorizedRequest(options: Options): Promise<any> { | ||
try { | ||
const authorization = await getAuthorizationHeader(this); | ||
const mergedOptions = got.mergeOptions(options, { | ||
headers: { authorization }, | ||
responseType: 'json', | ||
resolveBodyOnly: true, | ||
}); | ||
return await this.request(mergedOptions); | ||
} catch (err) { | ||
throw new Error(err.message); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
export type AccessToken = { | ||
accessToken: string; | ||
}; | ||
|
||
export type ClientId = { | ||
clientId: string; | ||
}; | ||
|
||
export type Login = ClientId & { | ||
username: string; | ||
password: string; | ||
}; | ||
|
||
export type Credentials = AccessToken | ClientId | Login; | ||
|
||
export function isAccessToken(arg: any): arg is AccessToken { | ||
return arg.accessToken !== undefined; | ||
} | ||
|
||
export function isLogin(arg: any): arg is Login { | ||
return arg.username !== undefined && arg.password !== undefined; | ||
} | ||
|
||
export function isClientId(arg: any): arg is ClientId { | ||
return arg.clientId !== undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const HOST = 'https://api.imgur.com'; | ||
const API_VERSION = '3'; | ||
const API_BASE = `${HOST}/${API_VERSION}`; | ||
|
||
export const AUTHORIZE_ENDPOINT = `${HOST}/oauth2/authorize`; | ||
|
||
export const IMAGE_ENDPOINT = `${API_BASE}/image`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ImgurClient } from '../client'; | ||
import { getAuthorizationHeader } from './getAuthorizationHeader'; | ||
|
||
test('returns provided access code in bearer header', async () => { | ||
const accessToken = 'abc123'; | ||
const client = new ImgurClient({ accessToken }); | ||
const authorizationHeader = await getAuthorizationHeader(client); | ||
expect(authorizationHeader).toBe(`Bearer ${accessToken}`); | ||
}); | ||
|
||
test('returns provided client id in client id header', async () => { | ||
const clientId = 'abc123'; | ||
const client = new ImgurClient({ clientId }); | ||
const authorizationHeader = await getAuthorizationHeader(client); | ||
expect(authorizationHeader).toBe(`Client-ID ${clientId}`); | ||
}); | ||
|
||
test('retrieves access token from imgur via provided username/password/clientid', async () => { | ||
const client = new ImgurClient({ | ||
username: 'fakeusername', | ||
password: 'fakepassword', | ||
clientId: 'fakeclientd', | ||
}); | ||
const authorizationHeader = await getAuthorizationHeader(client); | ||
expect(authorizationHeader).toMatchInlineSnapshot( | ||
`"Bearer 123accesstoken456"` | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { isAccessToken, isClientId, isLogin } from './credentials'; | ||
import { ImgurClient } from '../client'; | ||
import { AUTHORIZE_ENDPOINT } from '../helpers'; | ||
|
||
export async function getAuthorizationHeader(client: ImgurClient) { | ||
if (isAccessToken(client.credentials)) { | ||
return `Bearer ${client.credentials.accessToken}`; | ||
} | ||
|
||
if (isClientId(client.credentials) && !isLogin(client.credentials)) { | ||
return `Client-ID ${client.credentials.clientId}`; | ||
} | ||
|
||
const { clientId, username, password } = client.credentials; | ||
|
||
const options: Record<string, any> = { | ||
url: AUTHORIZE_ENDPOINT, | ||
searchParams: { | ||
client_id: clientId, | ||
response_type: 'token', | ||
}, | ||
}; | ||
|
||
let response = await client.request(options); | ||
|
||
const cookies = Array.isArray(response.headers['set-cookie']) | ||
? response.headers['set-cookie'][0] | ||
: response.headers['set-cookie']; | ||
const authorizeToken = cookies.match('(^|;)[s]*authorize_token=([^;]*)')[2]; | ||
|
||
options.method = 'POST'; | ||
options.form = { | ||
username, | ||
password, | ||
allow: authorizeToken, | ||
}; | ||
|
||
options.followRedirect = false; | ||
options.headers = { | ||
cookie: `authorize_token=${authorizeToken}`, | ||
}; | ||
|
||
response = await client.request(options); | ||
const location = response.headers.location; | ||
const token = JSON.parse( | ||
'{"' + | ||
decodeURI(location.slice(location.indexOf('#') + 1)) | ||
.replace(/"/g, '\\"') | ||
.replace(/&/g, '","') | ||
.replace(/=/g, '":"') + | ||
'"}' | ||
); | ||
|
||
const accessToken = token.access_token; | ||
(client.credentials as any).accessToken = accessToken; | ||
return `Bearer ${accessToken}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './getAuthorizationHeader'; | ||
export * from './credentials'; | ||
export * from './endpoints'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { ImgurClient } from '../client'; | ||
import { getImage } from './getImage'; | ||
|
||
test('returns an image response', async () => { | ||
const accessToken = 'abc123'; | ||
const client = new ImgurClient({ accessToken }); | ||
const response = await getImage(client, 'CEddrgP'); | ||
expect(response).toMatchInlineSnapshot(` | ||
Object { | ||
"data": Object { | ||
"description": "image-description", | ||
"id": "CEddrgP", | ||
"title": "image-title", | ||
}, | ||
"status": 200, | ||
"success": true, | ||
} | ||
`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { ImgurClient } from '../client'; | ||
import { IMAGE_ENDPOINT } from '../helpers'; | ||
|
||
type ImageResponse = { | ||
data: { | ||
id?: string; | ||
title?: string | null; | ||
description?: string | null; | ||
datetime?: number; | ||
type?: string; | ||
animated?: boolean; | ||
width?: number; | ||
height?: number; | ||
size?: number; | ||
views?: number; | ||
bandwidth?: number; | ||
vote?: boolean | null; | ||
favorite?: boolean; | ||
nsfw?: boolean; | ||
section?: string | null; | ||
account_url?: string | null; | ||
account_id?: string | null; | ||
is_ad?: boolean; | ||
in_most_viral?: boolean; | ||
has_sound?: boolean; | ||
tags?: string[]; | ||
ad_type?: number; | ||
ad_url?: string; | ||
edited?: string; | ||
in_gallery?: string; | ||
link?: string; | ||
ad_config?: { | ||
safeFlags?: string[]; | ||
highRiskFlags?: string[]; | ||
unsafeFlags?: string[]; | ||
wallUnsafeFlags?: string[]; | ||
showsAds?: boolean; | ||
}; | ||
}; | ||
success: boolean; | ||
status: number; | ||
}; | ||
|
||
export async function getImage( | ||
client: ImgurClient, | ||
imageHash: string | ||
): Promise<ImageResponse> { | ||
const url = `${IMAGE_ENDPOINT}/${imageHash}`; | ||
return await client.authorizedRequest({ url }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { ImgurClient } from './client'; |