Last active
April 3, 2020 08:53
-
-
Save SagnikPradhan/d1bce39db1424d0af1fe9e88943d7d8c to your computer and use it in GitHub Desktop.
Request Function in Typescript for a Discord API Wrapper
This file contains hidden or 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
import https from 'https'; | |
import {validErrors, AppError} from '../../Error-Management'; | |
// All errors originating here are of the same type | |
const errName = validErrors['DISCORD_CLIENT_INTERNAL']; | |
// Better object | |
type ValidObjectValues = string|number|boolean|string[]|number[]|boolean[]; | |
type BetterObject<T> = {[index: string]: T|BetterObject<T>}; | |
// Request Options | |
interface RequestOptions { | |
body: BetterObject<ValidObjectValues>|Multipart; | |
headers: { | |
'Content-Type': 'application/json'|'multipart/form-data'; | |
'User-Agent': string; | |
[header: string]: string; | |
}; | |
} | |
/** | |
* Multipart Form Data | |
*/ | |
export class Multipart { | |
boundary = 'discordtslibraryboundary'; | |
#multipart = ''; | |
/** | |
* Create a Multipart form | |
* @param data - Data to convert as Multipart | |
*/ | |
constructor(...data: { | |
name: string; | |
data: Buffer|BetterObject<string|number|boolean>|string; | |
fileName?: string; | |
}[]) { | |
if (data.length > 0) { | |
// Add each of them | |
data.forEach(({name, data, fileName}) => | |
this.append(name, data, fileName)); | |
} | |
} | |
/** | |
* Add Field to multipart | |
* @param name - Name of the field | |
* @param data - Data to be sent | |
* @param fileName - Filename for files | |
*/ | |
append( | |
name: string, | |
data: Buffer|BetterObject<string|number|boolean>|string, | |
fileName?: string, | |
): void { | |
if (!data) return; | |
// Field Start | |
let str = `\r\n--${this.boundary}\r\n`; | |
// Sub headers | |
str += `Content-Disposition: form-data; name="${name}"`; | |
str += fileName ? `; filename="${fileName}"` : ''; | |
// Content Type | |
// Files | |
if (data instanceof Buffer) { | |
str += `\r\nContent-Type: application/octet-stream`; | |
}; | |
// JSON Payloads | |
if (typeof(data) == 'object' && !fileName) { | |
str += `\r\nContent-Type: application/json`; | |
data = JSON.stringify(data); | |
} | |
// Data | |
str += '\r\n\r\n'; | |
str += data; | |
this.#multipart += str; | |
} | |
/** | |
* Complete current multipart form and return | |
*/ | |
parse(): string { | |
return this.#multipart + `\r\n--${this.boundary}--`; | |
} | |
} | |
/** | |
* Parse body into string | |
* @param type - Type of body | |
* @param body - Body itself | |
*/ | |
function parseBody(body: BetterObject<ValidObjectValues> | Multipart): string { | |
// If Multipart body | |
if (body instanceof Multipart) return body.parse(); | |
// If JSON Body | |
if (['object', 'boolean', 'string', 'number'].includes(typeof body)) { | |
return JSON.stringify(body); | |
} | |
// Other types not supported | |
throw new AppError( | |
errName, 'Invalid Request Body', {bodyType: typeof body}, false, | |
); | |
} | |
/** | |
* HTTPS Request Function | |
* @param path - URL to request to | |
* @param method - HTTP Method to use | |
* @param options - Request options | |
*/ | |
export function request( | |
path: string, | |
method: 'GET' | 'POST' | 'PATCH' | 'UPDATE' | 'DELETE', | |
options: RequestOptions, | |
): Promise<void> { | |
return new Promise((resolve, reject) => { | |
const reqURL = new URL(path); | |
// Make request | |
const clientRequest = https.request(reqURL, { | |
method, | |
headers: options.headers, | |
}, (response) => { | |
let chunks = ''; | |
// Handle Errors | |
response.on('error', ({name, message}) => { | |
reject(new AppError( | |
errName, 'Error on Response', {name, message}, false, | |
)); | |
}); | |
response.on('data', (chunk) => chunks += chunk); | |
response.on('end', () => { | |
// Try parsing the response | |
try { | |
resolve(JSON.parse(chunks)); | |
} catch ({name, message}) { | |
reject(new AppError( | |
errName, 'Error on Parsing Response', {name, message}, false, | |
)); | |
} | |
}); | |
}); | |
// Handle Errors | |
clientRequest.on('error', ({name, message}) => { | |
reject(new AppError( | |
errName, 'Error on Request', {name, message}, false, | |
)); | |
}); | |
// Write body if available | |
if (options.body) clientRequest.write(parseBody(options.body)); | |
// End Request | |
clientRequest.end(); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment