Skip to content

Commit

Permalink
Make the context option mergeable (#1459)
Browse files Browse the repository at this point in the history
  • Loading branch information
ghermeto authored Oct 21, 2020
1 parent 71c319d commit 2b8ed1f
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 21 deletions.
4 changes: 1 addition & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,7 @@ JSON body. If the `Content-Type` header is not set, it will be set to `applicati

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.
User data. `context` is shallow merged and enumerable. If it contains non-enumerable properties they will NOT be merged.

It's very useful for storing auth tokens:

Expand Down
12 changes: 3 additions & 9 deletions source/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,7 @@ interface PlainOptions extends URLOptions {
dnsCache?: CacheableLookup | boolean;

/**
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.
User data. `context` is shallow merged and enumerable. If it contains non-enumerable properties they will NOT be merged.
@example
```
Expand Down Expand Up @@ -1139,9 +1136,8 @@ const waitForOpenFile = async (file: ReadStream): Promise<void> => new Promise((

const redirectCodes: ReadonlySet<number> = new Set([300, 301, 302, 303, 304, 307, 308]);

type NonEnumerableProperty = 'context' | 'body' | 'json' | 'form';
type NonEnumerableProperty = 'body' | 'json' | 'form';
const nonEnumerableProperties: NonEnumerableProperty[] = [
'context',
'body',
'json',
'form'
Expand Down Expand Up @@ -1780,9 +1776,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
}

// `options.context`
if (!options.context) {
options.context = {};
}
options.context = {...defaults?.context, ...options.context};

// `options.hooks`
const areHooksDefault = options.hooks === defaults?.hooks;
Expand Down
2 changes: 1 addition & 1 deletion source/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ export interface Got extends Record<HTTPAlias, GotRequestFunction>, GotRequestFu
- If the parent property is a plain `object` too, both values are merged recursively into a new `object`.
- Otherwise, only the new value is deeply cloned.
- If the new property is an `Array`, it overwrites the old one with a deep clone of the new property.
- Properties that are not enumerable, such as `context`, `body`, `json`, and `form`, will not be merged.
- Properties that are not enumerable, such as `body`, `json`, and `form`, will not be merged.
- Otherwise, the new value is assigned to the key.
**Note:** Only Got options are merged! Custom user options should be defined via [`options.context`](#context).
Expand Down
33 changes: 26 additions & 7 deletions test/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ test('throws if the `searchParams` value is invalid', async t => {
});
});

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

const context = {
Expand All @@ -340,8 +340,8 @@ test('`context` option is not enumerable', withServer, async (t, server, got) =>
hooks: {
beforeRequest: [
options => {
t.is(options.context, context);
t.false({}.propertyIsEnumerable.call(options, 'context'));
t.deepEqual(options.context, context);
t.true({}.propertyIsEnumerable.call(options, 'context'));
}
]
}
Expand All @@ -360,8 +360,8 @@ test('`context` option is accessible when using hooks', withServer, async (t, se
hooks: {
beforeRequest: [
options => {
t.is(options.context, context);
t.false({}.propertyIsEnumerable.call(options, 'context'));
t.deepEqual(options.context, context);
t.true({}.propertyIsEnumerable.call(options, 'context'));
}
]
}
Expand All @@ -375,8 +375,27 @@ test('`context` option is accessible when extending instances', t => {

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

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

test('`context` option is shallow merged', t => {
const context = {
foo: 'bar'
};

const context2 = {
bar: 'baz'
};

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

t.deepEqual(instance1.defaults.options.context, context);
t.true({}.propertyIsEnumerable.call(instance1.defaults.options, 'context'));

const instance2 = instance1.extend({context: context2});

t.deepEqual(instance2.defaults.options.context, {...context, ...context2});
});

test('throws if `options.encoding` is `null`', async t => {
Expand Down
2 changes: 1 addition & 1 deletion test/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ test('beforeRetry is called with options', withServer, async (t, server, got) =>
beforeRetry: [
(options, error, retryCount) => {
t.is(options.url.hostname, 'localhost');
t.is(options.context, context);
t.deepEqual(options.context, context);
t.truthy(error);
t.true(retryCount! >= 1);
}
Expand Down

0 comments on commit 2b8ed1f

Please sign in to comment.