Skip to content

Commit

Permalink
Add context option (#777)
Browse files Browse the repository at this point in the history
Co-Authored-By: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
szmarczak and sindresorhus committed Jun 26, 2019
1 parent 3557896 commit 3bb5aa7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 4 deletions.
39 changes: 39 additions & 0 deletions readme.md
Expand Up @@ -174,6 +174,45 @@ Type: `object | Array | number | string | boolean | null` *(JSON-serializable va

JSON body. If the `Content-Type` header is not set, it will be set to `application/json`.

###### context

Type: `object`

User data. In contrast to other options, `context` is not enumerable.

**Note:** The object is never merged, it's just passed through. Got will not modify the object in any way.

It's very useful for storing auth tokens:

```js
const got = require('got');

const instance = got.extend({
hooks: {
beforeRequest: [
options => {
if (!options.context && !options.context.token) {
throw new Error('Token required');
}

options.header.token = options.context.token;
}
]
}
});

(async () => {
const context = {
token: 'secret'
};

const response = await instance('https://httpbin.org/headers', {context});

// Let's see the headers
console.log(response.body);
})();
```

###### responseType

Type: `string`<br>
Expand Down
9 changes: 9 additions & 0 deletions source/merge.ts
Expand Up @@ -37,6 +37,15 @@ export default function merge<Target extends Record<string, unknown>, Source ext
target[key] = sourceValue;
}
}

if (Reflect.has(source, 'context')) {
Object.defineProperty(target, 'context', {
writable: true,
configurable: true,
enumerable: false,
value: source.context
});
}
}

return target as Target & Source;
Expand Down
6 changes: 2 additions & 4 deletions source/utils/types.ts
Expand Up @@ -138,13 +138,11 @@ export interface Options extends Omit<https.RequestOptions, 'agent' | 'timeout'
dnsCache?: Map<string, string> | Keyv | false;
url?: URL | string;
searchParams?: Record<string, string | number | boolean | null> | URLSearchParams | string;
/*
Deprecated
*/
query?: Options['searchParams'];
query?: Options['searchParams']; // Deprecated
useElectronNet?: boolean;
form?: Record<string, any>;
json?: Record<string, any>;
context?: {[key: string]: unknown};
}

export interface NormalizedOptions extends Omit<Required<Options>, 'timeout' | 'dnsCache' | 'retry'> {
Expand Down
51 changes: 51 additions & 0 deletions test/arguments.ts
Expand Up @@ -262,3 +262,54 @@ test('throws if the `searchParams` value is invalid', async t => {
message: 'The `searchParams` value \'\' must be a string, number, boolean or null'
});
});

test('`context` option is not enumerable', withServer, async (t, server, got) => {
server.get('/', echoUrl);

const context = {
foo: 'bar'
};

await got({
context,
hooks: {
beforeRequest: [
options => {
t.is(options.context, context);
t.false({}.propertyIsEnumerable.call(options, 'context'));
}
]
}
});
});

test('`context` option is accessible when using hooks', withServer, async (t, server, got) => {
server.get('/', echoUrl);

const context = {
foo: 'bar'
};

await got({
context,
hooks: {
init: [
options => {
t.is(options.context, context);
t.false({}.propertyIsEnumerable.call(options, 'context'));
}
]
}
});
});

test('`context` option is accessible when extending instances', t => {
const context = {
foo: 'bar'
};

const instance = got.extend({context});

t.is(instance.defaults.options.context, context);
t.false({}.propertyIsEnumerable.call(instance.defaults.options, 'context'));
});

0 comments on commit 3bb5aa7

Please sign in to comment.