stoplightio / prism

Turn any OpenAPI2/3 and Postman Collection file into an API server with mocking, transformations and validations.
https://stoplight.io/open-source/prism
Apache License 2.0
4.32k stars 349 forks source link

honor x-schema-faker settings when mocking from Ninja (aka hosted) #1954

Open EdVinyard opened 2 years ago

EdVinyard commented 2 years ago

follow-up to investigate Pass x- settings to Prism when mocking from Ninja

Goal

When a workspace-project-branch OAS specification includes x-schema-faker options, the mock responses to ninja by the mocks component should honor those options.

Two possible implementation strategies, both with significant drawbacks:

  1. Pass a pre-configured JSF instance into Prism's mock() function. In mocks, cache pre-configured JSF instances based on workspace-project-branch, and figure out how to invalidate this cache when each specification changes. Drawback: This exposes Prism implementation details (that JSF is used) to its client, mocks.

  2. Introduce the concepts of workspace, project, and branch into Prism (or at the very least the notion of distinct Prism configurations). This also requires that the configuration (or a placeholder/key) be supplied along with requests. Drawback: Prism should not know anything about multiple configurations, especially regarding workspaces, projects, and branches.

Note: This probably requires changes to both mocks and prism regardless of the implementation strategy.

Background

mocks uses prism to generate mock reponses. prism in turn uses json-schema-faker (JSF). Specifically, prism:

The current code-path is traced in detail below.

in mocks, handleRequest()

in platform-internal/packages/mocks/src/index.ts, handleRequest() calls validateInputAndMock()

    function handleRequest(
      req: IncomingMessage,
      { workspaceSlug, projectSlug, serviceNodeId, 
        prismUrl, branchName: branch }: ApiLocationInfo,
      config: IHttpOperationConfig,
    ) {
      ...

      TE.bindW('response', ({ input, resolved }) => TE.fromEither(validateInputAndMock(resolved, input, config)(logger))),
      ...

in mocks, validateInputAndMock()

in platform-internal/packages/mocks/src/index.ts, validateInputAndMock() calls mock()

    import mock from '@stoplight/prism-http/dist/mocker';

    function validateInputAndMock(
      resource: IHttpOperation, 
      element: IHttpRequest, 
      config: IHttpOperationConfig
    ) {
      logger.error('validateInputAndMock()');
      return pipe(
        validateInput({ resource, element }),
        E.fold<IPrismDiagnostic[], unknown, IPrismDiagnostic[]>(
          validations => validations,
          () => [],
        ),
        validations => mock({ 
          resource, 
          input: { data: element, validations }, 
          config 
        }),
      );
    }

in prism, mock()

Notice that Prism lacks any concept of workspaces, projects, or branches. Furthermore, Prism lacks any way to retrieve that information.

in prism/packages/http/src/mocker/index.ts, mock() calls generate()

    import { generate, generateStatic } from './generator/JSONSchema';

    const mock = (...) => {
      const payloadGenerator: PayloadGenerator = config.dynamic
        ? partial(generate, resource['__bundled__'])
        : partial(generateStatic, resource);      
    }

Prism uses a single, global instance of json-schema-faker (JSF). This works well when it's started from the command line, but when run from mocks, we need a JSF instance configured per workspace-project-branch.

in prism, generate()

in prism/packages/http/src/mocker/generator/JSONSchema.ts, generate() calls json-schema-faker's generate()

    import * as jsf from 'json-schema-faker';

    jsf.extend('faker', () => faker);
    jsf.option({
      failOnInvalidTypes: false,
      failOnInvalidFormat: false,
      alwaysFakeOptionals: true,
      optionalsProbability: 1 as any,
      fixedProbabilities: true,
      ignoreMissingRefs: true,
      maxItems: 20,
      maxLength: 100,
    });

    generateKeyPair(...) {
      jsf.generate( ... )
    }

gz#8423

(related to Zendesk ticket #8423)

gz#6772

(related to Zendesk ticket #6772)

gz#8775

(related to Zendesk ticket #8775)

gz#9701

(related to Zendesk ticket #9701)

ryotrellim commented 2 years ago

Consider: