Skip to content

Commit

Permalink
feat: better typescript definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
gr2m committed Apr 28, 2019
1 parent f68a813 commit 67b026e
Show file tree
Hide file tree
Showing 7 changed files with 1,695 additions and 47 deletions.
4 changes: 3 additions & 1 deletion src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import getUserAgent = require('universal-user-agent')
import version = require('./version')
const userAgent = `octokit-endpoint.js/${version} ${getUserAgent()}`

import { EndpointDefaultOptions } from './types'

export = {
method: 'GET',
baseUrl: 'https://api.github.com',
Expand All @@ -14,4 +16,4 @@ export = {
format: '',
previews: [] as string[]
}
}
} as EndpointDefaultOptions
6 changes: 4 additions & 2 deletions src/endpoint-with-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export = endpointWithDefaults
import merge = require('./merge')
import parse = require('./parse')

function endpointWithDefaults (defaults: typeof import('./defaults'), route: string, options: { method?: string; url?: string; [key: string]: any }) {
return parse(merge(defaults, route!, options))
import { EndpointDefaultOptions, EndpointOptions, RequestOptions, Route, RouteOptions } from './types'

function endpointWithDefaults (defaults: EndpointDefaultOptions, route: Route | EndpointOptions, options?: RouteOptions) {
return parse(merge(defaults, route, options))
}
16 changes: 10 additions & 6 deletions src/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import isPlainObject = require('is-plain-object')

import lowercaseKeys = require('./util/lowercase-keys')

function defaultOptions (defaults: typeof import('./defaults') | null, route: string | {}, options?: any) {
import { EndpointDefaultOptions, Route, RouteOptions } from './types'

type Defaults = EndpointDefaultOptions | null

function defaultOptions (defaults: Defaults, route: Route | RouteOptions, options?: RouteOptions) {
if (typeof route === 'string') {
let [method, url] = route.split(' ')
options = Object.assign(url ? { method, url } : { url: method }, options)
Expand All @@ -16,15 +20,15 @@ function defaultOptions (defaults: typeof import('./defaults') | null, route: st
// lowercase header names before merging with defaults to avoid duplicates
options.headers = lowercaseKeys(options.headers)

options = merge.all([defaults, options].filter(Boolean), { isMergeableObject: isPlainObject })
const mergedOptions = merge.all([defaults!, options].filter(Boolean), { isMergeableObject: isPlainObject }) as EndpointDefaultOptions

// mediaType.previews arrays are merged, instead of overwritten
if (defaults && defaults.mediaType.previews.length) {
options.mediaType.previews = defaults.mediaType.previews.filter(preview => !options.mediaType.previews.includes(preview))
.concat(options.mediaType.previews)
mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(preview => !mergedOptions.mediaType.previews.includes(preview))
.concat(mergedOptions.mediaType.previews)
}

options.mediaType.previews = options.mediaType.previews.map((preview: string) => preview.replace(/-preview/, ''))
mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map((preview: string) => preview.replace(/-preview/, ''))

return options
return mergedOptions
}
24 changes: 6 additions & 18 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,16 @@ import addQueryParameters = require('./util/add-query-parameters')
import extractUrlVariableNames = require('./util/extract-url-variable-names')
import omit = require('./util/omit')

interface toRequestOptions {
method: string;
url: string;
headers?: {
accept: string;
[header: string]: string | number | undefined
};
baseUrl?: string;
request?: any;
mediaType: {
format?: string;
previews: string[];
}
}
function toRequestOptions (options: toRequestOptions) {
import { EndpointDefaultOptions, Method, RequestOptions } from './types'

function toRequestOptions(options: EndpointDefaultOptions): RequestOptions {
// https://fetch.spec.whatwg.org/#methods
let method = options.method.toUpperCase()
let method = options.method.toUpperCase() as Method

// replace :varname with {varname} to make it RFC 6570 compatible
let url = options.url.replace(/:([a-z]\w+)/g, '{+$1}')
let headers = Object.assign({}, options.headers)
let body: string | {} | undefined
let body: string | object | undefined
let parameters = omit(options, ['method', 'baseUrl', 'url', 'headers', 'request', 'mediaType'])

// extract variable names from URL to calculate remaining variables later
Expand Down Expand Up @@ -89,7 +77,7 @@ function toRequestOptions (options: toRequestOptions) {

// Only return body/request keys if present
return Object.assign(
{ method: method, url, headers },
{ method, url, headers },
typeof body !== 'undefined' ? { body } : null,
options.request ? { request: options.request } : null
)
Expand Down
93 changes: 93 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
export interface endpoint {
(route: Route, options?: RouteOptions): RequestOptions;
(options: EndpointOptions): RequestOptions;
DEFAULTS: EndpointDefaultOptions;
defaults: (newDefaults: RouteOptions) => endpoint;
merge: {
/**
* Merges current endpoint defaults with passed route and parameters,
* without transforming them into request options.
*
* @param {string} route Request method + URL. Example: `'GET /orgs/:org'`
* @param {object} [parameters] URL, query or body parameters, as well as `headers`, `mediaType.{format|previews}`, `request`, or `baseUrl`.
*
*/
(route: Route, parameters?: Parameters): Defaults;

/**
* Merges current endpoint defaults with passed route and parameters,
* without transforming them into request options.
*
* @param {object} endpoint Must set `method` and `url`. Plus URL, query or body parameters, as well as `headers`, `mediaType.{format|previews}`, `request`, or `baseUrl`.
*/
(options: Parameters): Defaults;

/**
* Returns current default options.
*
* @deprecated use endpoint.DEFAULTS instead
*/
(): Defaults;
};

/**
* Stateless method to turn endpoint options into request options.
* Calling `endpoint(options)` is the same as calling `endpoint.parse(endpoint.merge(options))`.
*
* @param {object} options `method`, `url`. Plus URL, query or body parameters, as well as `headers`, `mediaType.{format|previews}`, `request`, or `baseUrl`.
*/
parse: (options: Defaults) => RequestOptions;
}

export type Route = string;

export type Method = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT";

export type RouteOptions = {
baseUrl?: string;
headers?: {
accept?: string;
authorization?: string;
"user-agent"?: string;
[header: string]: string | number | undefined;
};
mediaType?: {
format?: string;
previews?: string[];
};
request?: {
[option: string]: any;
};
[option: string]: any;
};

export type EndpointOptions = RouteOptions & {
method: Method;
};

export type EndpointDefaultOptions = RouteOptions & {
method: Method;
baseUrl: string;
headers: {
accept: string;
"user-agent": string;
};
mediaType: {
format: string;
previews: string[];
};
};

export type RequestOptions = {
method: Method;
url: string;
headers: {
accept: string;
"user-agent": string;
[option: string]: any;
};
body?: any;
request?: {
[option: string]: any;
};
};
26 changes: 6 additions & 20 deletions src/with-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,13 @@ import endpointWithDefaults = require('./endpoint-with-defaults')
import merge = require('./merge')
import parse = require('./parse')

interface withDefaults {
(route: any, options: any): any
DEFAULTS: (typeof import('./defaults') | null) & any
defaults: (newDefaults: {
method: string;
baseUrl: string;
headers: {
accept: string;
'user-agent': string;
};
mediaType: {
format: string;
previews: string[];
};
}) => withDefaults;
merge: (route: any, options?: any) => typeof import('./defaults')
}
import { EndpointOptions, EndpointDefaultOptions, endpoint, Route, RouteOptions } from './types'

function withDefaults(oldDefaults: EndpointDefaultOptions | null, newDefaults: RouteOptions): endpoint {
const DEFAULTS = merge(oldDefaults, newDefaults)
const endpoint = endpointWithDefaults.bind(null, DEFAULTS)

function withDefaults (oldDefaults: typeof import('./defaults') | null, newDefaults: typeof import('./defaults')): withDefaults {
const DEFAULTS: typeof oldDefaults & typeof newDefaults = merge(oldDefaults, newDefaults)
return Object.assign(endpointWithDefaults.bind(null, DEFAULTS), {
return Object.assign(endpoint, {
DEFAULTS,
defaults: withDefaults.bind(null, DEFAULTS),
merge: merge.bind(null, DEFAULTS),
Expand Down
Loading

0 comments on commit 67b026e

Please sign in to comment.