Skip to content

Commit

Permalink
docs(wiki, #zimic): v0.10.0 (#487)
Browse files Browse the repository at this point in the history
Closes #485.
  • Loading branch information
diego-aquino authored Nov 26, 2024
1 parent d0bfcc6 commit 9d4ff00
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import nodeConfig from '@zimic/eslint-config-node';
const fileName = fileURLToPath(import.meta.url);
const currentDirectory = path.dirname(fileName);

const zimicConfigWithLanguageOptionsIndex = nodeConfig.findIndex((config) => config.languageOptions !== undefined);
const indexOfConfigWithLanguageOptions = nodeConfig.findIndex((config) => config.languageOptions !== undefined);

export default [
...nodeConfig.slice(0, zimicConfigWithLanguageOptionsIndex),
...nodeConfig.slice(0, indexOfConfigWithLanguageOptions),
{
...nodeConfig[zimicConfigWithLanguageOptionsIndex],
...nodeConfig[indexOfConfigWithLanguageOptions],
files: ['*.ts'],
languageOptions: {
parserOptions: {
Expand All @@ -20,7 +20,7 @@ export default [
},
},
},
...nodeConfig.slice(zimicConfigWithLanguageOptionsIndex + 1),
...nodeConfig.slice(indexOfConfigWithLanguageOptions + 1),
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
Expand Down
150 changes: 127 additions & 23 deletions docs/wiki/api‐zimic‐interceptor‐http.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ const baseURL = interceptor.baseURL();

#### Unhandled requests

When a request is not matched by any interceptor handlers, it is considered unhandled and will be logged to the console
by default.
When a request is not matched by any interceptor handlers, it is considered **unhandled** and will be logged to the
console by default.

> [!TIP]
>
Expand All @@ -144,65 +144,169 @@ by default.
> and [restrictions](#http-handlerwithrestriction) correctly match the request. Additionally, confirm that no errors
> occurred while creating the response.
In a [local interceptor](getting‐started#local-http-interceptors), unhandled requests are always bypassed, meaning that
they pass through the interceptor and reach the real network.
[Remote interceptors](getting‐started#remote-http-interceptors) in pair with an [interceptor server](cli‐zimic‐server)
always reject unhandled requests because they cannot be bypassed.
In a [local interceptor](getting‐started#local-http-interceptors), unhandled requests can be either bypassed or
rejected. Bypassed requests reach the real network, whereas rejected requests fail with an network error. The default
behavior in local interceptors is to bypass unhandled requests.

On the other hand, [remote interceptors](getting‐started#remote-http-interceptors) and
[interceptor server](cli‐zimic‐server) always reject unhandled requests. This is because the unhandled requests have
already reached the interceptor server, so there would be no way of bypassing them at this point.

You can override the default logging behavior per interceptor with `onUnhandledRequest` in
[`httpInterceptor.create(options)`](#httpinterceptorcreateoptions).
[`httpInterceptor.create(options)`](#httpinterceptorcreateoptions). `onUnhandledRequest` also accepts a function to
dynamically determine which strategy to use for an unhandled request.

<details>
<summary>
Example 1: Ignore all unhandled requests with no logging:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: { log: false },
onUnhandledRequest: {
action: 'bypass', // Allow unhandled requests to reach the real network
log: false, // Do not log warnings about unhandled requests
},
});
```

`onUnhandledRequest` also accepts a function to dynamically choose when to ignore an unhandled request. Calling
`await context.log()` logs the request to the console. Learn more about the `request` object at
[Intercepted HTTP resources](#intercepted-http-resources).
</details>

<details>
<summary>
Example 2: Reject all unhandled requests with logging:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: {
action: 'reject', // Do not allow unhandled requests to reach the real network
log: true, // Log warnings about unhandled requests
},
});
```

</details>

<details>
<summary>
Example 3: Dynamically ignore or reject unhandled requests:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
onUnhandledRequest: async (request, context) => {
onUnhandledRequest: async (request) => {
const url = new URL(request.url);

// Ignore only unhandled requests to /assets
if (!url.pathname.startsWith('/assets')) {
await context.log();
if (url.pathname.startsWith('/assets')) {
return { action: 'bypass', log: false };
}

// Reject all other unhandled requests
return { action: 'reject', log: true };
},
});
```

</details>

If you want to override the default logging behavior for all interceptors, or requests that did not match any known base
URL, you can use `httpInterceptor.default.onUnhandledRequest`. Keep in mind that defining an `onUnhandledRequest` when
creating an interceptor will take precedence over `httpInterceptor.default.onUnhandledRequest`.
URL, you can use `httpInterceptor.default.local.onUnhandledRequest` or
`httpInterceptor.default.remote.onUnhandledRequest`. Keep in mind that defining an `onUnhandledRequest` when creating an
interceptor will take precedence over `httpInterceptor.default.local.onUnhandledRequest` and
`httpInterceptor.default.remote.onUnhandledRequest`.

<details>
<summary>
Example 4: Ignore all unhandled requests with no logging:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

// Example 1: Ignore all unhandled requests
httpInterceptor.default.onUnhandledRequest({ log: false });
httpInterceptor.default.local.onUnhandledRequest = {
action: 'bypass',
log: false,
};

httpInterceptor.default.remote.onUnhandledRequest = {
action: 'reject',
log: false,
};
```

</details>

<details>
<summary>
Example 5: Reject all unhandled requests with logging:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

// For local interceptors:
httpInterceptor.default.local.onUnhandledRequest = {
action: 'reject',
log: true,
};

// For remote interceptors:
httpInterceptor.default.remote.onUnhandledRequest = {
action: 'reject',
log: true,
};
```

</details>

// Example 2: Ignore only unhandled requests to /assets
httpInterceptor.default.onUnhandledRequest(async (request, context) => {
<details>
<summary>
Example 6: Dynamically ignore or reject all unhandled requests:
</summary>

```ts
import { httpInterceptor } from 'zimic/interceptor/http';

// For local interceptors:
httpInterceptor.default.local.onUnhandledRequest = (request) => {
const url = new URL(request.url);

if (!url.pathname.startsWith('/assets')) {
await context.log();
// Ignore only unhandled requests to /assets
if (url.pathname.startsWith('/assets')) {
return { action: 'bypass', log: false };
}
});

// Reject all other unhandled requests
return { action: 'reject', log: true };
};

// For remote interceptors:
httpInterceptor.default.remote.onUnhandledRequest = (request) => {
const url = new URL(request.url);

return {
action: 'reject', // Reject all unhandled requests
log: !url.pathname.startsWith('/assets'), // Log warnings for all unhandled requests except /assets
};
};
```

</details>

#### Saving requests

The option `saveRequests` indicates whether [request handlers](#httprequesthandler) should save their intercepted
Expand Down
3 changes: 2 additions & 1 deletion packages/zimic/src/interceptor/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export type {
HttpInterceptorRequest,
HttpInterceptorResponse,
TrackedHttpInterceptorRequest,
UnhandledHttpInterceptorRequest,
} from './requestHandler/types/requests';

export type {
Expand All @@ -38,6 +37,8 @@ export type {
UnhandledRequestStrategy,
} from './interceptor/types/options';

export type { UnhandledHttpInterceptorRequest } from './interceptor/types/requests';

export type { InferHttpInterceptorSchema } from './interceptor/types/schema';

export type { LocalHttpInterceptor, RemoteHttpInterceptor, HttpInterceptor } from './interceptor/types/public';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import { promiseIfRemote } from '@/interceptor/http/interceptorWorker/__tests__/
import { DEFAULT_UNHANDLED_REQUEST_STRATEGY } from '@/interceptor/http/interceptorWorker/constants';
import LocalHttpRequestHandler from '@/interceptor/http/requestHandler/LocalHttpRequestHandler';
import RemoteHttpRequestHandler from '@/interceptor/http/requestHandler/RemoteHttpRequestHandler';
import {
HttpRequestBodySchema,
UnhandledHttpInterceptorRequest,
UnhandledHttpInterceptorRequestSchema,
} from '@/interceptor/http/requestHandler/types/requests';
import { HttpRequestBodySchema } from '@/interceptor/http/requestHandler/types/requests';
import { AccessControlHeaders, DEFAULT_ACCESS_CONTROL_HEADERS } from '@/interceptor/server/constants';
import { methodCanHaveRequestBody } from '@/utils/http';
import { waitForDelay } from '@/utils/time';
Expand All @@ -22,6 +18,7 @@ import { expectBypassedResponse, expectPreflightResponse, expectFetchError } fro
import { assessPreflightInterference, usingHttpInterceptor } from '@tests/utils/interceptors';

import { HttpInterceptorOptions, UnhandledRequestStrategy } from '../../types/options';
import { UnhandledHttpInterceptorRequest, UnhandledHttpInterceptorRequestMethodSchema } from '../../types/requests';
import { RuntimeSharedHttpInterceptorTestsOptions, verifyUnhandledRequestMessage } from './utils';

const verifyUnhandledRequest = vi.fn((request: UnhandledHttpInterceptorRequest, method: string) => {
Expand All @@ -37,7 +34,7 @@ const verifyUnhandledRequest = vi.fn((request: UnhandledHttpInterceptorRequest,
expectTypeOf(request.pathParams).toEqualTypeOf<{}>();
expect(request.pathParams).toEqual({});

type BodySchema = HttpRequestBodySchema<UnhandledHttpInterceptorRequestSchema>;
type BodySchema = HttpRequestBodySchema<UnhandledHttpInterceptorRequestMethodSchema>;

expectTypeOf(request.body).toEqualTypeOf<BodySchema>();
expect(request).toHaveProperty('body');
Expand Down Expand Up @@ -466,15 +463,15 @@ export function declareUnhandledRequestHttpInterceptorTests(options: RuntimeShar
{ overrideDefault: 'static' as const },
{ overrideDefault: 'factory' as const },
])('Logging disabled: override default $overrideDefault', ({ overrideDefault }) => {
const logWarning = false;
const log = false;

const localOnUnhandledRequest: UnhandledRequestStrategy.LocalDeclaration = {
action: 'bypass',
log: logWarning,
log,
};
const remoteOnUnhandledRequest: UnhandledRequestStrategy.RemoteDeclaration = {
action: 'reject',
log: logWarning,
log,
};

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UnhandledHttpInterceptorRequest } from '@/interceptor/http/requestHandler/types/requests';
import { PossiblePromise } from '@/types/utils';

import { UnhandledHttpInterceptorRequest } from './requests';

/**
* An type of an HTTP interceptor.
*
Expand All @@ -18,9 +19,9 @@ export type HttpInterceptorPlatform = 'node' | 'browser';
/**
* The strategy to treat unhandled requests.
*
* When `logWarning` is `true` or `undefined`, warnings about the unhandled requests are logged to the console.
* Similarly, if provided a factory, unhandled request warnings will be logged if the function returns a declaration
* with `logWarning` set to `true` or `undefined`.
* When `log` is `true` or `undefined`, warnings about unhandled requests are logged to the console. If provided a
* factory, unhandled request warnings will be logged if the function returns a
* {@link UnhandledRequestStrategy.Declaration strategy declaration} containing `log` as `true` or `undefined`.
*
* @see {@link https://github.com/zimicjs/zimic/wiki/api‐zimic‐interceptor‐http#unhandled-requests Unhandled requests}
*/
Expand Down
17 changes: 17 additions & 0 deletions packages/zimic/src/interceptor/http/interceptor/types/requests.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import { HttpBody } from '@/http';
import { HttpSchema, HttpSchemaMethod, HttpSchemaPath } from '@/http/types/schema';
import { Default } from '@/types/utils';

import { HttpResponseFactoryContext } from '../../interceptorWorker/types/requests';
import { HttpInterceptorRequest } from '../../requestHandler/types/requests';

export type HttpInterceptorRequestContext<
Schema extends HttpSchema,
Method extends HttpSchemaMethod<Schema>,
Path extends HttpSchemaPath<Schema, Method>,
> = HttpResponseFactoryContext<Default<Default<Schema[Path][Method]>['request']>['body']>;

export type UnhandledHttpInterceptorRequestPath = string;

export type UnhandledHttpInterceptorRequestMethodSchema = HttpSchema.Method<{
request: {
headers: Record<string, string>;
searchParams: Record<string, string | string[]>;
body: HttpBody;
};
}>;

export type UnhandledHttpInterceptorRequest = Omit<
HttpInterceptorRequest<string, UnhandledHttpInterceptorRequestMethodSchema>,
'response'
>;
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ import { createURL, excludeNonPathParams } from '@/utils/urls';
import HttpSearchParams from '../../../http/searchParams/HttpSearchParams';
import HttpInterceptorClient, { AnyHttpInterceptorClient } from '../interceptor/HttpInterceptorClient';
import { HttpInterceptorPlatform, HttpInterceptorType, UnhandledRequestStrategy } from '../interceptor/types/options';
import {
UnhandledHttpInterceptorRequestPath,
UnhandledHttpInterceptorRequestMethodSchema,
} from '../interceptor/types/requests';
import {
HTTP_INTERCEPTOR_REQUEST_HIDDEN_PROPERTIES,
HTTP_INTERCEPTOR_RESPONSE_HIDDEN_PROPERTIES,
HttpInterceptorRequest,
HttpInterceptorResponse,
UnhandledHttpInterceptorRequestPath,
UnhandledHttpInterceptorRequestSchema,
} from '../requestHandler/types/requests';
import { DEFAULT_UNHANDLED_REQUEST_STRATEGY } from './constants';
import HttpInterceptorWorkerStore from './HttpInterceptorWorkerStore';
Expand Down Expand Up @@ -244,7 +246,9 @@ abstract class HttpInterceptorWorker {
}

static async parseRawUnhandledRequest(request: HttpRequest) {
return this.parseRawRequest<UnhandledHttpInterceptorRequestPath, UnhandledHttpInterceptorRequestSchema>(request);
return this.parseRawRequest<UnhandledHttpInterceptorRequestPath, UnhandledHttpInterceptorRequestMethodSchema>(
request,
);
}

static async parseRawRequest<Path extends string, MethodSchema extends HttpMethodSchema>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import HttpInterceptorWorkerStore from '../interceptorWorker/HttpInterceptorWork
export class HttpInterceptorNamespaceDefault {
local: {
/**
* Gets or sets the default strategy for unhandled requests. If a request does not start with the base URL of any
* interceptors, this strategy will be used. If a function is provided, it will be called with the unhandled
* request.
* Gets or sets the default strategy for local unhandled requests. If a request does not start with the base URL of
* any local interceptors, this strategy will be used. If a function is provided, it will be called with the
* unhandled request.
*
* You can override this default for specific interceptors by using `onUnhandledRequest` in
* {@link https://github.com/zimicjs/zimic/wiki/api‐zimic‐interceptor‐http#httpinterceptorcreateoptions `httpInterceptor.create(options)`}.
Expand All @@ -21,9 +21,9 @@ export class HttpInterceptorNamespaceDefault {

remote: {
/**
* Gets or sets the default strategy for unhandled requests. If a request does not start with the base URL of any
* interceptors, this strategy will be used. If a function is provided, it will be called with the unhandled
* request.
* Gets or sets the default strategy for remote unhandled requests. If a request does not start with the base URL of
* any remote interceptors, this strategy will be used. If a function is provided, it will be called with the
* unhandled request.
*
* You can override this default for specific interceptors by using `onUnhandledRequest` in
* {@link https://github.com/zimicjs/zimic/wiki/api‐zimic‐interceptor‐http#httpinterceptorcreateoptions `httpInterceptor.create(options)`}.
Expand Down
Loading

0 comments on commit 9d4ff00

Please sign in to comment.