Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP interceptor strict JSON validation #106

Closed
diego-aquino opened this issue Mar 17, 2024 · 1 comment · Fixed by #109
Closed

HTTP interceptor strict JSON validation #106

diego-aquino opened this issue Mar 17, 2024 · 1 comment · Fixed by #109
Assignees
Labels
feature New feature or request
Milestone

Comments

@diego-aquino
Copy link
Member

diego-aquino commented Mar 17, 2024

As of v0.2.x, JSON bodies are not completely statically validated. This allows defining bodies using non-JSON values, such as Date and Error.

This issue means to add strict JSON validation to interceptor declarations. Unfortunately, it won't be possible to declare body types using raw TypeScript interface's, because they do not have automatic index signatures as type's. See microsoft/TypeScript#15300 for more information.

microsoft/TypeScript#1897 proposes a native json type, which might help in the future. Right now, though, the interface vs type problem seems unsolvable natively.

@diego-aquino diego-aquino self-assigned this Mar 17, 2024
@diego-aquino diego-aquino added the feature New feature or request label Mar 17, 2024
@diego-aquino diego-aquino added this to the v0.3.0 milestone Mar 17, 2024
@diego-aquino diego-aquino linked a pull request Mar 17, 2024 that will close this issue
@diego-aquino
Copy link
Member Author

diego-aquino commented Mar 17, 2024

Migration guide

In order to keep compatibility with client code bases with minimal changes, I'm proposing a new utility typed exported by zimic called JSONCompatible. In code bases using interfaces, the migration workflow is as follows:

  1. For each interface used as body in HTTP interceptor schemas, refactor them to use type. For example:

    interface User {
      id: string;
      name: string;
    }
    
    const interceptor = createHttpInterceptor<{
      GET: {
        response: {
          // shows an error because interfaces do not have index signatures by default
          200: { body: User[] };
        };
      };
    }>({
      worker,
      baseURL: 'http://localhost:3000',
    });

    should be refactored to:

    // refactored from interface to type
    type User = {
      id: string;
      name: string;
    };
    
    const interceptor = createHttpInterceptor<{
      GET: {
        response: {
          200: { body: User[] }; // no error now!
        };
      };
    }>({
      worker,
      baseURL: 'http://localhost:3000',
    });
  2. For each type used as body in HTTP interceptor schemas, consider using JSONCompatible to make sure the type can be represented as an HTTP body (e.g. is a valid JSON). This is not required, although it provides more type safety to your code. Following the example of step 1:

    import type { JSONCompatible } from 'zimic';
    
    // now checked that it is compatible with HTTP bodies
    type User = JSONCompatible<{
      id: string;
      name: string;
    }>;
    
    const interceptor = createHttpInterceptor<{
      GET: {
        response: {
          200: { body: User[] };
        };
      };
    }>({
      worker,
      baseURL: 'http://localhost:3000',
    });
    • In case your interface or type was relying on the loose JSON validation and using non-JSON properties, JSONCompatible will show type errors. This is expected because the type was in fact not JSON-compatible. To fix this problem, we recommend using another utility type exported by zimic, JSONSerialized, which transforms an object type into its equivalent when serialized as JSON and later deserialized. In this case, refactoring the original interface to type is not required. For example:

      import type { JSONSerialized } from 'zimic';
      
      // since `User` is no longer used directly in the schema,
      // declaring it as an interface or type are both valid
      interface User {
        id: string;
        name: string;
        birthDate: Date; // not a valid JSON value; actually becomes `string` when serialized
      }
      
      type UserResponse = JSONSerialized<User>;
      
      const interceptor = createHttpInterceptor<{
        GET: {
          response: {
            // converts `birthDate: Date` to `birthDate: string`
            200: { body: JSONSerialized<User>[] }; // no error!
          };
        };
      }>({
        worker,
        baseURL: 'http://localhost:3000',
      });

diego-aquino added a commit that referenced this issue Mar 17, 2024
### Features
- [#zimic] Added the utility types `JSONCompatible` and
`JSONSerialized`.
- [#zimic] Added strict JSON validation to interceptor schemas. Since
this is a breaking change, see the [Migration
guide](https://github.com/diego-aquino/zimic/issues/106#issuecomment-2002623779)
to enable the strict validation!

Closes #106.
@diego-aquino diego-aquino mentioned this issue Mar 12, 2024
5 tasks
diego-aquino added a commit that referenced this issue Aug 25, 2024
)

### Fixes
- [#zimic] Added support to declare HTTP schema bodies using
`interface`. This became no longer possible after
#106. Zimic now is less strict
about the validation of JSON bodies in HTTP schemas, due to [limitations
in TypeScript](microsoft/TypeScript#15300).
Due to the type error message being somewhat cryptic ("Index signature
is missing in type X"), we believe allowing `type` and `interface` and
serializing JSON body types before use is a good compromise.
- Deprecated `HttpResponseSchemaByStatusCode.Strict` in favor of
`HttpResponseSchemaByStatusCode`.
- Added `HttpFormDataSerialized` and updated the typegen code to wrap
this type around raw schemas. This is to ensure that Zimic correctly
understands schemas not strictly correct, as sometimes happens for
search params and headers.

### Development
- [#zimic] Updated `tsconfig.json` to ignore `interceptor/*.d.ts` and
`http/*.d.ts` in type checking and linting.
- [#eslint] Disabled the rule "no-redeclare", which is already enforced
by the TypeScript copiler.

Closes #350.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

1 participant